OSDN Git Service

WinGui:
[handbrake-jp/handbrake-jp-git.git] / win / C# / HandBrake.ApplicationServices / Services / Encode.cs
1 /*  Encode.cs $\r
2     This file is part of the HandBrake source code.\r
3     Homepage: <http://handbrake.fr/>.\r
4     It may be used under the terms of the GNU General Public License. */\r
5 \r
6 namespace HandBrake.ApplicationServices.Services\r
7 {\r
8     using System;\r
9     using System.Diagnostics;\r
10     using System.IO;\r
11     using System.Text;\r
12     using System.Threading;\r
13     using System.Windows.Forms;\r
14 \r
15     using HandBrake.ApplicationServices.Functions;\r
16     using HandBrake.ApplicationServices.Model;\r
17     using HandBrake.ApplicationServices.Parsing;\r
18     using HandBrake.ApplicationServices.Properties;\r
19     using HandBrake.ApplicationServices.Services.Interfaces;\r
20 \r
21     using Timer = System.Threading.Timer;\r
22 \r
23     /// <summary>\r
24     /// Class which handles the CLI\r
25     /// </summary>\r
26     public class Encode : IEncode\r
27     {\r
28         /* Private Variables */\r
29 \r
30         /// <summary>\r
31         /// An Encode Job\r
32         /// </summary>\r
33         private Job job;\r
34 \r
35         /// <summary>\r
36         /// The Log Buffer\r
37         /// </summary>\r
38         private StringBuilder logBuffer;\r
39 \r
40         /// <summary>\r
41         /// The line number thats been read to in the log file\r
42         /// </summary>\r
43         private int logFilePosition;\r
44 \r
45         /// <summary>\r
46         /// A Timer for this window\r
47         /// </summary>\r
48         private Timer windowTimer;\r
49 \r
50         /// <summary>\r
51         /// Gets The Process Handle\r
52         /// </summary>\r
53         private IntPtr processHandle;\r
54 \r
55         /// <summary>\r
56         /// Gets the Process ID\r
57         /// </summary>\r
58         private int processID;\r
59 \r
60         /// <summary>\r
61         /// Windows 7 API Pack wrapper\r
62         /// </summary>\r
63         private Win7 windowsSeven = new Win7();\r
64 \r
65         /* Constructor */\r
66 \r
67         /// <summary>\r
68         /// Initializes a new instance of the <see cref="Encode"/> class.\r
69         /// </summary>\r
70         public Encode()\r
71         {\r
72             this.EncodeStarted += Encode_EncodeStarted;\r
73         }\r
74 \r
75         /* Delegates */\r
76 \r
77         /// <summary>\r
78         /// Encode Progess Status\r
79         /// </summary>\r
80         /// <param name="sender">\r
81         /// The sender.\r
82         /// </param>\r
83         /// <param name="e">\r
84         /// The EncodeProgressEventArgs.\r
85         /// </param>\r
86         public delegate void EncodeProgessStatus(object sender, EncodeProgressEventArgs e);\r
87 \r
88         /* Event Handlers */\r
89 \r
90         /// <summary>\r
91         /// Fires when a new CLI Job starts\r
92         /// </summary>\r
93         public event EventHandler EncodeStarted;\r
94 \r
95         /// <summary>\r
96         /// Fires when a CLI job finishes.\r
97         /// </summary>\r
98         public event EventHandler EncodeEnded;\r
99 \r
100         /// <summary>\r
101         /// Encode process has progressed\r
102         /// </summary>\r
103         public event EncodeProgessStatus EncodeStatusChanged;\r
104 \r
105         /* Properties */\r
106 \r
107         /// <summary>\r
108         /// Gets or sets The HB Process\r
109         /// </summary>\r
110         protected Process HbProcess { get; set; }\r
111 \r
112         /// <summary>\r
113         /// Gets a value indicating whether IsEncoding.\r
114         /// </summary>\r
115         public bool IsEncoding { get; private set; }\r
116 \r
117         /* Public Methods */\r
118 \r
119         /// <summary>\r
120         /// Gets ActivityLog.\r
121         /// </summary>\r
122         public string ActivityLog\r
123         {\r
124             get\r
125             {\r
126                 if (logBuffer == null)\r
127                 {\r
128                     ResetLogReader();\r
129                     ReadFile(null);\r
130                 }\r
131 \r
132                 return logBuffer != null ? logBuffer.ToString() : string.Empty;\r
133             }\r
134         }\r
135 \r
136         /// <summary>\r
137         /// Create a preview sample video\r
138         /// </summary>\r
139         /// <param name="query">\r
140         /// The CLI Query\r
141         /// </param>\r
142         public void CreatePreviewSample(string query)\r
143         {\r
144             this.Run(new Job { Query = query }, true);\r
145         }\r
146 \r
147         /// <summary>\r
148         /// Kill the CLI process\r
149         /// </summary>\r
150         public void Stop()\r
151         {\r
152             if (this.HbProcess != null)\r
153                 this.HbProcess.Kill();\r
154 \r
155             Process[] list = Process.GetProcessesByName("HandBrakeCLI");\r
156             foreach (Process process in list)\r
157                 process.Kill();\r
158 \r
159             if (this.EncodeEnded != null)\r
160                 this.EncodeEnded(this, new EventArgs());\r
161         }\r
162 \r
163         /// <summary>\r
164         /// Attempt to Safely kill a DirectRun() CLI\r
165         /// NOTE: This will not work with a MinGW CLI\r
166         /// Note: http://www.cygwin.com/ml/cygwin/2006-03/msg00330.html\r
167         /// </summary>\r
168         public void SafelyClose()\r
169         {\r
170             if ((int)this.processHandle == 0)\r
171                 return;\r
172 \r
173             // Allow the CLI to exit cleanly\r
174             Win32.SetForegroundWindow((int)this.processHandle);\r
175             SendKeys.Send("^C");\r
176             SendKeys.Flush();\r
177 \r
178             // HbProcess.StandardInput.AutoFlush = true;\r
179             // HbProcess.StandardInput.WriteLine("^C");\r
180         }\r
181 \r
182         /// <summary>\r
183         /// Execute a HandBrakeCLI process.\r
184         /// </summary>\r
185         /// <param name="encJob">\r
186         /// The enc Job.\r
187         /// </param>\r
188         /// <param name="requireStandardOuput">\r
189         /// Set to True to show no window and force standard output redirect\r
190         /// </param>\r
191         protected void Run(Job encJob, bool requireStandardOuput)\r
192         {\r
193             this.job = encJob;\r
194             try\r
195             {\r
196                 ResetLogReader();\r
197                 IsEncoding = true;\r
198 \r
199                 string handbrakeCLIPath = Path.Combine(Application.StartupPath, "HandBrakeCLI.exe");\r
200                 string logPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\HandBrake\\logs", "last_encode_log.txt");\r
201                 string strCmdLine = String.Format(@" /C """"{0}"" {1} 2>""{2}"" """, handbrakeCLIPath, encJob.Query, logPath);\r
202                 var cliStart = new ProcessStartInfo("CMD.exe", strCmdLine);\r
203 \r
204                 if (Settings.Default.enocdeStatusInGui || requireStandardOuput)\r
205                 {\r
206                     cliStart.RedirectStandardOutput = true;\r
207                     cliStart.UseShellExecute = false;\r
208                     if (!Settings.Default.showCliForInGuiEncodeStatus || requireStandardOuput)\r
209                         cliStart.CreateNoWindow = true;\r
210                 }\r
211                 if (Settings.Default.cli_minimized)\r
212                     cliStart.WindowStyle = ProcessWindowStyle.Minimized;\r
213 \r
214                 Process[] before = Process.GetProcesses(); // Get a list of running processes before starting.\r
215                 HbProcess = Process.Start(cliStart);\r
216                 this.processID = Main.GetCliProcess(before);\r
217 \r
218                 if (HbProcess != null)\r
219                     this.processHandle = HbProcess.MainWindowHandle; // Set the process Handle\r
220 \r
221                 // Start the Log Monitor\r
222                 windowTimer = new Timer(new TimerCallback(ReadFile), null, 1000, 1000);\r
223 \r
224                 // Set the process Priority\r
225                 Process hbCliProcess = null;\r
226                 if (this.processID != -1)\r
227                 {\r
228                     hbCliProcess = Process.GetProcessById(this.processID);\r
229                     hbCliProcess.EnableRaisingEvents = true;\r
230                     hbCliProcess.Exited += new EventHandler(HbProcess_Exited);\r
231                 }\r
232 \r
233                 if (hbCliProcess != null)\r
234                     switch (Settings.Default.processPriority)\r
235                     {\r
236                         case "Realtime":\r
237                             hbCliProcess.PriorityClass = ProcessPriorityClass.RealTime;\r
238                             break;\r
239                         case "High":\r
240                             hbCliProcess.PriorityClass = ProcessPriorityClass.High;\r
241                             break;\r
242                         case "Above Normal":\r
243                             hbCliProcess.PriorityClass = ProcessPriorityClass.AboveNormal;\r
244                             break;\r
245                         case "Normal":\r
246                             hbCliProcess.PriorityClass = ProcessPriorityClass.Normal;\r
247                             break;\r
248                         case "Low":\r
249                             hbCliProcess.PriorityClass = ProcessPriorityClass.Idle;\r
250                             break;\r
251                         default:\r
252                             hbCliProcess.PriorityClass = ProcessPriorityClass.BelowNormal;\r
253                             break;\r
254                     }\r
255 \r
256                 // Fire the Encode Started Event\r
257                 if (this.EncodeStarted != null)\r
258                     this.EncodeStarted(this, new EventArgs());\r
259             }\r
260             catch (Exception exc)\r
261             {\r
262                 Main.ShowExceptiowWindow("It would appear that HandBrakeCLI has not started correctly. You should take a look at the Activity log as it may indicate the reason why.\n\nDetailed Error Information: error occured in runCli()", exc.ToString());\r
263             }\r
264         }\r
265 \r
266         /// <summary>\r
267         /// The HandBrakeCLI process has exited.\r
268         /// </summary>\r
269         /// <param name="sender">\r
270         /// The sender.\r
271         /// </param>\r
272         /// <param name="e">\r
273         /// The EventArgs.\r
274         /// </param>\r
275         private void HbProcess_Exited(object sender, EventArgs e)\r
276         {\r
277             IsEncoding = false;\r
278 \r
279             windowTimer.Dispose();\r
280             ReadFile(null);\r
281 \r
282             if (this.EncodeEnded != null)\r
283                 this.EncodeEnded(this, new EventArgs());\r
284 \r
285             if (windowsSeven.IsWindowsSeven)\r
286             {\r
287                 windowsSeven.SetTaskBarProgressToNoProgress();\r
288             }\r
289         }\r
290 \r
291         /// <summary>\r
292         /// Function to run the CLI directly rather than via CMD\r
293         /// TODO: Code to handle the Log data has yet to be written.\r
294         /// TODO: Code to handle the % / ETA info has to be written.\r
295         /// </summary>\r
296         /// <param name="query">\r
297         /// The query.\r
298         /// </param>\r
299         protected void DirectRun(string query)\r
300         {\r
301             try\r
302             {\r
303                 if (this.EncodeStarted != null)\r
304                     this.EncodeStarted(this, new EventArgs());\r
305 \r
306                 IsEncoding = true;\r
307 \r
308                 ResetLogReader();\r
309 \r
310                 // Setup the job\r
311                 string handbrakeCLIPath = Path.Combine(Environment.CurrentDirectory, "HandBrakeCLI.exe");\r
312                 HbProcess = new Process\r
313                                 {\r
314                                     StartInfo =\r
315                                         {\r
316                                             FileName = handbrakeCLIPath,\r
317                                             Arguments = query,\r
318                                             UseShellExecute = false,\r
319                                             RedirectStandardOutput = true,\r
320                                             RedirectStandardError = true,\r
321                                             RedirectStandardInput = true,\r
322                                             CreateNoWindow = false,\r
323                                             WindowStyle = ProcessWindowStyle.Minimized\r
324                                         }\r
325                                 };\r
326 \r
327                 // Setup event handlers for rediected data\r
328                 HbProcess.ErrorDataReceived += new DataReceivedEventHandler(HbProcErrorDataReceived);\r
329                 HbProcess.OutputDataReceived += new DataReceivedEventHandler(HbProcOutputDataReceived);\r
330 \r
331                 // Start the process\r
332                 HbProcess.Start();\r
333 \r
334                 // Setup the asynchronous reading of stdin and stderr\r
335                 HbProcess.BeginErrorReadLine();\r
336                 HbProcess.BeginOutputReadLine();\r
337 \r
338                 // Set the Process Priority);\r
339                 switch (Settings.Default.processPriority)\r
340                 {\r
341                     case "Realtime":\r
342                         HbProcess.PriorityClass = ProcessPriorityClass.RealTime;\r
343                         break;\r
344                     case "High":\r
345                         HbProcess.PriorityClass = ProcessPriorityClass.High;\r
346                         break;\r
347                     case "Above Normal":\r
348                         HbProcess.PriorityClass = ProcessPriorityClass.AboveNormal;\r
349                         break;\r
350                     case "Normal":\r
351                         HbProcess.PriorityClass = ProcessPriorityClass.Normal;\r
352                         break;\r
353                     case "Low":\r
354                         HbProcess.PriorityClass = ProcessPriorityClass.Idle;\r
355                         break;\r
356                     default:\r
357                         HbProcess.PriorityClass = ProcessPriorityClass.BelowNormal;\r
358                         break;\r
359                 }\r
360 \r
361                 // Set the class items\r
362                 this.processID = HbProcess.Id;\r
363                 this.processHandle = HbProcess.Handle;\r
364             }\r
365             catch (Exception exc)\r
366             {\r
367                 Console.WriteLine(exc);\r
368             }\r
369         }\r
370 \r
371         /// <summary>\r
372         /// Add the CLI Query to the Log File.\r
373         /// </summary>\r
374         /// <param name="encJob">\r
375         /// The Encode Job Object\r
376         /// </param>\r
377         protected void AddCLIQueryToLog(Job encJob)\r
378         {\r
379             try\r
380             {\r
381                 string logDir = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) +\r
382                                 "\\HandBrake\\logs";\r
383                 string logPath = Path.Combine(logDir, "last_encode_log.txt");\r
384 \r
385                 var reader = new StreamReader(File.Open(logPath, FileMode.Open, FileAccess.Read, FileShare.Read));\r
386                 string log = reader.ReadToEnd();\r
387                 reader.Close();\r
388 \r
389                 var writer = new StreamWriter(File.Create(logPath));\r
390 \r
391                 writer.WriteLine("### CLI Query: " + encJob.Query);\r
392                 writer.WriteLine("### User Query: " + encJob.CustomQuery);\r
393                 writer.WriteLine("#########################################");\r
394                 writer.WriteLine(log);\r
395                 writer.Flush();\r
396                 writer.Close();\r
397             }\r
398             catch (Exception)\r
399             {\r
400                 return;\r
401             }\r
402         }\r
403 \r
404         /// <summary>\r
405         /// Save a copy of the log to the users desired location or a default location\r
406         /// if this feature is enabled in options.\r
407         /// </summary>\r
408         /// <param name="destination">\r
409         /// The Destination File Path\r
410         /// </param>\r
411         protected void CopyLog(string destination)\r
412         {\r
413             try\r
414             {\r
415                 string logDir = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) +\r
416                                 "\\HandBrake\\logs";\r
417                 string tempLogFile = Path.Combine(logDir, "last_encode_log.txt");\r
418 \r
419                 string encodeDestinationPath = Path.GetDirectoryName(destination);\r
420                 string destinationFile = Path.GetFileName(destination);\r
421                 string encodeLogFile = destinationFile + " " +\r
422                                        DateTime.Now.ToString().Replace("/", "-").Replace(":", "-") + ".txt";\r
423 \r
424                 // Make sure the log directory exists.\r
425                 if (!Directory.Exists(logDir))\r
426                     Directory.CreateDirectory(logDir);\r
427 \r
428                 // Copy the Log to HandBrakes log folder in the users applciation data folder.\r
429                 File.Copy(tempLogFile, Path.Combine(logDir, encodeLogFile));\r
430 \r
431                 // Save a copy of the log file in the same location as the enocde.\r
432                 if (Settings.Default.saveLogWithVideo)\r
433                     File.Copy(tempLogFile, Path.Combine(encodeDestinationPath, encodeLogFile));\r
434 \r
435                 // Save a copy of the log file to a user specified location\r
436                 if (Directory.Exists(Settings.Default.saveLogPath))\r
437                     if (Settings.Default.saveLogPath != String.Empty && Settings.Default.saveLogToSpecifiedPath)\r
438                         File.Copy(tempLogFile, Path.Combine(Settings.Default.saveLogPath, encodeLogFile));\r
439             }\r
440             catch (Exception exc)\r
441             {\r
442                 Main.ShowExceptiowWindow("Unable to make a copy of the log file", exc.ToString());\r
443             }\r
444         }\r
445 \r
446         /// <summary>\r
447         /// Read the log file\r
448         /// </summary>\r
449         /// <param name="n">\r
450         /// The object.\r
451         /// </param>\r
452         private void ReadFile(object n)\r
453         {\r
454             lock (logBuffer)\r
455             {\r
456                 // last_encode_log.txt is the primary log file. Since .NET can't read this file whilst the CLI is outputing to it (Not even in read only mode),\r
457                 // we'll need to make a copy of it.\r
458                 string logDir = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) +\r
459                                 "\\HandBrake\\logs";\r
460                 string logFile = Path.Combine(logDir, "last_encode_log.txt");\r
461                 string logFile2 = Path.Combine(logDir, "tmp_appReadable_log.txt");\r
462 \r
463                 try\r
464                 {\r
465                     // Make sure the application readable log file does not already exist. FileCopy fill fail if it does.\r
466                     if (File.Exists(logFile2))\r
467                         File.Delete(logFile2);\r
468 \r
469                     // Copy the log file.\r
470                     if (File.Exists(logFile))\r
471                         File.Copy(logFile, logFile2, true);\r
472                     else\r
473                     {\r
474                         ResetLogReader();\r
475                         return;\r
476                     }\r
477 \r
478                     // Put the Query and User Generated Query Flag on the log.\r
479                     if (logFilePosition == 0 && job.Query != null)\r
480                     {\r
481                         logBuffer.AppendLine("### CLI Query: " + job.Query);\r
482                         logBuffer.AppendLine("### User Query: " + job.CustomQuery);\r
483                         logBuffer.AppendLine("#########################################");\r
484                     }\r
485 \r
486                     // Start the Reader\r
487                     // Only use text which continues on from the last read line\r
488                     StreamReader sr = new StreamReader(logFile2);\r
489                     string line;\r
490                     int i = 1;\r
491                     while ((line = sr.ReadLine()) != null)\r
492                     {\r
493                         if (i > logFilePosition)\r
494                         {\r
495                             logBuffer.AppendLine(line);\r
496                             logFilePosition++;\r
497                         }\r
498                         i++;\r
499                     }\r
500                     sr.Close();\r
501                     sr.Dispose();\r
502                 }\r
503                 catch (Exception)\r
504                 {\r
505                     ResetLogReader();\r
506                 }\r
507             }\r
508         }\r
509 \r
510         /// <summary>\r
511         /// Reset the Log Reader\r
512         /// </summary>\r
513         private void ResetLogReader()\r
514         {\r
515             logFilePosition = 0;\r
516             logBuffer = new StringBuilder();\r
517         }\r
518 \r
519         /// <summary>\r
520         /// Recieve the Standard Error information and process it\r
521         /// </summary>\r
522         /// <param name="sender">\r
523         /// The Sender Object\r
524         /// </param>\r
525         /// <param name="e">\r
526         /// DataReceived EventArgs\r
527         /// </param>\r
528         private void HbProcErrorDataReceived(object sender, DataReceivedEventArgs e)\r
529         {\r
530             if (!String.IsNullOrEmpty(e.Data))\r
531             {\r
532                 lock (logBuffer)\r
533                     logBuffer.AppendLine(e.Data);\r
534             }\r
535         }\r
536 \r
537         /// <summary>\r
538         /// Standard Input Data Recieved from the CLI\r
539         /// </summary>\r
540         /// <param name="sender">\r
541         /// The Sender Object\r
542         /// </param>\r
543         /// <param name="e">\r
544         /// DataReceived EventArgs\r
545         /// </param>\r
546         private void HbProcOutputDataReceived(object sender, DataReceivedEventArgs e)\r
547         {\r
548             if (!String.IsNullOrEmpty(e.Data))\r
549             {\r
550                 lock (logBuffer)\r
551                     logBuffer.AppendLine(e.Data);\r
552             }\r
553         }\r
554 \r
555         /// <summary>\r
556         /// Encode Started\r
557         /// </summary>\r
558         /// <param name="sender">\r
559         /// The sender.\r
560         /// </param>\r
561         /// <param name="e">\r
562         /// The EventArgs.\r
563         /// </param>\r
564         private void Encode_EncodeStarted(object sender, EventArgs e)\r
565         {\r
566             Thread monitor = new Thread(EncodeMonitor);\r
567             monitor.Start();\r
568         }\r
569 \r
570         /// <summary>\r
571         /// Monitor the Job\r
572         /// </summary>\r
573         private void EncodeMonitor()\r
574         {\r
575             try\r
576             {\r
577                 Parser encode = new Parser(HbProcess.StandardOutput.BaseStream);\r
578                 encode.OnEncodeProgress += EncodeOnEncodeProgress;\r
579                 while (!encode.EndOfStream)\r
580                     encode.ReadEncodeStatus();\r
581 \r
582                // Main.ShowExceptiowWindow("Encode Monitor Stopped", "Stopped");\r
583             }\r
584             catch (Exception exc)\r
585             {\r
586                 Main.ShowExceptiowWindow("An Unknown Error has occured", exc.ToString());\r
587             }\r
588         }\r
589 \r
590         /// <summary>\r
591         /// Displays the Encode status in the GUI\r
592         /// </summary>\r
593         /// <param name="sender">The sender</param>\r
594         /// <param name="currentTask">The current task</param>\r
595         /// <param name="taskCount">Number of tasks</param>\r
596         /// <param name="percentComplete">Percent complete</param>\r
597         /// <param name="currentFps">Current encode speed in fps</param>\r
598         /// <param name="avg">Avg encode speed</param>\r
599         /// <param name="timeRemaining">Time Left</param>\r
600         private void EncodeOnEncodeProgress(object sender, int currentTask, int taskCount, float percentComplete, float currentFps, float avg, TimeSpan timeRemaining)\r
601         {\r
602             EncodeProgressEventArgs eventArgs = new EncodeProgressEventArgs\r
603                 {\r
604                     AverageFrameRate = avg,\r
605                     CurrentFrameRate = currentFps,\r
606                     EstimatedTimeLeft = timeRemaining,\r
607                     PercentComplete = percentComplete,\r
608                     Task = currentTask,\r
609                     TaskCount = taskCount\r
610                 };\r
611 \r
612             if (this.EncodeStatusChanged != null)\r
613                 this.EncodeStatusChanged(this, eventArgs);\r
614 \r
615             if (windowsSeven.IsWindowsSeven)\r
616             {\r
617                 int percent;\r
618                 int.TryParse(Math.Round(percentComplete).ToString(), out percent);\r
619 \r
620                 windowsSeven.SetTaskBarProgress(percent);\r
621             }\r
622         }\r
623     }\r
624 }