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         #region 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 Log file writer\r
42         /// </summary>\r
43         private StreamWriter fileWriter;\r
44 \r
45         /// <summary>\r
46         /// Gets The Process Handle\r
47         /// </summary>\r
48         private IntPtr processHandle;\r
49 \r
50         /// <summary>\r
51         /// Gets the Process ID\r
52         /// </summary>\r
53         private int processID;\r
54 \r
55         /// <summary>\r
56         /// Windows 7 API Pack wrapper\r
57         /// </summary>\r
58         private Win7 windowsSeven = new Win7();\r
59 \r
60         #endregion\r
61 \r
62         /* Constructor */\r
63 \r
64         /// <summary>\r
65         /// Initializes a new instance of the <see cref="Encode"/> class.\r
66         /// </summary>\r
67         public Encode()\r
68         {\r
69             this.EncodeStarted += Encode_EncodeStarted;\r
70         }\r
71 \r
72         #region Delegates and Event Handlers\r
73 \r
74         /// <summary>\r
75         /// Encode Progess Status\r
76         /// </summary>\r
77         /// <param name="sender">\r
78         /// The sender.\r
79         /// </param>\r
80         /// <param name="e">\r
81         /// The EncodeProgressEventArgs.\r
82         /// </param>\r
83         public delegate void EncodeProgessStatus(object sender, EncodeProgressEventArgs e);\r
84 \r
85         /* Event Handlers */\r
86 \r
87         /// <summary>\r
88         /// Fires when a new CLI Job starts\r
89         /// </summary>\r
90         public event EventHandler EncodeStarted;\r
91 \r
92         /// <summary>\r
93         /// Fires when a CLI job finishes.\r
94         /// </summary>\r
95         public event EventHandler EncodeEnded;\r
96 \r
97         /// <summary>\r
98         /// Encode process has progressed\r
99         /// </summary>\r
100         public event EncodeProgessStatus EncodeStatusChanged;\r
101         #endregion\r
102 \r
103         /* Properties */\r
104 \r
105         /// <summary>\r
106         /// Gets or sets The HB Process\r
107         /// </summary>\r
108         protected Process HbProcess { get; set; }\r
109 \r
110         /// <summary>\r
111         /// Gets a value indicating whether IsEncoding.\r
112         /// </summary>\r
113         public bool IsEncoding { get; private set; }\r
114 \r
115         /// <summary>\r
116         /// Gets ActivityLog.\r
117         /// </summary>\r
118         public string ActivityLog\r
119         {\r
120             get\r
121             {\r
122                 if (this.IsEncoding == false)\r
123                 {\r
124                     ReadFile(); // Read the last log file back in if it exists\r
125                 }\r
126 \r
127                 return string.IsNullOrEmpty(this.logBuffer.ToString()) ? "No log data available..." : this.logBuffer.ToString();\r
128             }\r
129         }\r
130 \r
131         /* Public Methods */\r
132 \r
133         /// <summary>\r
134         /// Create a preview sample video\r
135         /// </summary>\r
136         /// <param name="query">\r
137         /// The CLI Query\r
138         /// </param>\r
139         public void CreatePreviewSample(string query)\r
140         {\r
141             this.Run(new Job { Query = query }, false);\r
142         }\r
143 \r
144         /// <summary>\r
145         /// Execute a HandBrakeCLI process.\r
146         /// </summary>\r
147         /// <param name="encJob">\r
148         /// The enc Job.\r
149         /// </param>\r
150         /// <param name="enableLogging">\r
151         /// Enable Logging. When Disabled we onlt parse Standard Ouput for progress info. Standard Error log data is ignored.\r
152         /// </param>\r
153         protected void Run(Job encJob, bool enableLogging)\r
154         {\r
155             this.job = encJob;\r
156             try\r
157             {\r
158                 IsEncoding = true;\r
159 \r
160                 if (enableLogging)\r
161                     SetupLogging(encJob);\r
162 \r
163                 if (Settings.Default.preventSleep)\r
164                 {\r
165                     Win32.PreventSleep();\r
166                 }\r
167 \r
168                 string handbrakeCLIPath = Path.Combine(Application.StartupPath, "HandBrakeCLI.exe");\r
169                 ProcessStartInfo cliStart = new ProcessStartInfo(handbrakeCLIPath, encJob.Query)\r
170                 { \r
171                     RedirectStandardOutput = true,\r
172                     RedirectStandardError = enableLogging ? true : false,\r
173                     UseShellExecute = false,\r
174                     CreateNoWindow = !Settings.Default.showCliForInGuiEncodeStatus ? true : false\r
175                 };\r
176               \r
177                 this.HbProcess = Process.Start(cliStart);\r
178 \r
179                 if (enableLogging)\r
180                 {\r
181                     this.HbProcess.ErrorDataReceived += HbProcErrorDataReceived;\r
182                     this.HbProcess.BeginErrorReadLine();\r
183                 }\r
184 \r
185                 this.processID = HbProcess.Id;\r
186                 this.processHandle = HbProcess.MainWindowHandle;\r
187 \r
188                 // Set the process Priority\r
189                 if (this.processID != -1)\r
190                 {\r
191                     this.HbProcess.EnableRaisingEvents = true;\r
192                     this.HbProcess.Exited += HbProcess_Exited;\r
193                 }\r
194 \r
195                 // Set the Process Priority\r
196                 switch (Settings.Default.processPriority)\r
197                 {\r
198                     case "Realtime":\r
199                         this.HbProcess.PriorityClass = ProcessPriorityClass.RealTime;\r
200                         break;\r
201                     case "High":\r
202                         this.HbProcess.PriorityClass = ProcessPriorityClass.High;\r
203                         break;\r
204                     case "Above Normal":\r
205                         this.HbProcess.PriorityClass = ProcessPriorityClass.AboveNormal;\r
206                         break;\r
207                     case "Normal":\r
208                         this.HbProcess.PriorityClass = ProcessPriorityClass.Normal;\r
209                         break;\r
210                     case "Low":\r
211                         this.HbProcess.PriorityClass = ProcessPriorityClass.Idle;\r
212                         break;\r
213                     default:\r
214                         this.HbProcess.PriorityClass = ProcessPriorityClass.BelowNormal;\r
215                         break;\r
216                 }\r
217 \r
218                 // Fire the Encode Started Event\r
219                 if (this.EncodeStarted != null)\r
220                     this.EncodeStarted(this, new EventArgs());\r
221             }\r
222             catch (Exception exc)\r
223             {\r
224                 Main.ShowExceptiowWindow("It would appear that HandBrakeCLI has not started correctly."+ \r
225                 "You should take a look at the Activity log as it may indicate the reason why.\n\nDetailed Error Information: error occured in runCli()", \r
226                 exc.ToString());\r
227             }\r
228         }\r
229 \r
230         /// <summary>\r
231         /// Kill the CLI process\r
232         /// </summary>\r
233         public void Stop()\r
234         {\r
235             try\r
236             {\r
237                 if (this.HbProcess != null) this.HbProcess.Kill();\r
238 \r
239                 Process[] list = Process.GetProcessesByName("HandBrakeCLI");\r
240                 foreach (Process process in list)\r
241                     process.Kill();\r
242             } \r
243             catch (Exception exc)\r
244             {\r
245                 Main.ShowExceptiowWindow("Unable to stop HandBrakeCLI. It may not be running.", exc.ToString());\r
246             }\r
247 \r
248             if (this.EncodeEnded != null)\r
249                 this.EncodeEnded(this, new EventArgs());\r
250         }\r
251 \r
252         /// <summary>\r
253         /// Attempt to Safely kill a DirectRun() CLI\r
254         /// NOTE: This will not work with a MinGW CLI\r
255         /// Note: http://www.cygwin.com/ml/cygwin/2006-03/msg00330.html\r
256         /// </summary>\r
257         public void SafelyClose()\r
258         {\r
259             if ((int)this.processHandle == 0)\r
260                 return;\r
261 \r
262             // Allow the CLI to exit cleanly\r
263             Win32.SetForegroundWindow((int)this.processHandle);\r
264             SendKeys.Send("^C");\r
265             SendKeys.Flush();\r
266 \r
267             if (HbProcess != null)\r
268             {\r
269                 HbProcess.StandardInput.AutoFlush = true;\r
270                 HbProcess.StandardInput.WriteLine("^C");\r
271             }\r
272         }\r
273 \r
274         /* Helpers */\r
275         /// <summary>\r
276         /// Add the CLI Query to the Log File.\r
277         /// </summary>\r
278         /// <param name="encJob">\r
279         /// The Encode Job Object\r
280         /// </param>\r
281         private static string CreateCliLogHeader(Job encJob)\r
282         {\r
283             try\r
284             {\r
285                 //string logDir = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) +\r
286                 //                "\\HandBrake\\logs";\r
287                 //string logPath = Path.Combine(logDir, "last_encode_log.txt");\r
288 \r
289                 //var reader = new StreamReader(File.Open(logPath, FileMode.Open, FileAccess.Read, FileShare.Read));\r
290                 //string log = reader.ReadToEnd();\r
291                 //reader.Close();\r
292 \r
293                 //var writer = new StreamWriter(File.Create(logPath));\r
294 \r
295 \r
296                 //writer.WriteLine("### CLI Query: " + encJob.Query);\r
297                 //writer.WriteLine("### User Query: " + encJob.CustomQuery);\r
298                 //writer.WriteLine("#########################################");\r
299                 //writer.WriteLine(log);\r
300                 //writer.Flush();\r
301                 //writer.Close();\r
302                 StringBuilder logHeader = new StringBuilder();\r
303                 logHeader.AppendLine("### CLI Query: " + encJob.Query);\r
304                 logHeader.AppendLine("### User Query: " + encJob.CustomQuery);\r
305                 logHeader.AppendLine("#########################################");\r
306 \r
307                 return logHeader.ToString();\r
308             }\r
309             catch (Exception)\r
310             {\r
311                 return string.Empty;\r
312             }\r
313         }\r
314 \r
315         /// <summary>\r
316         /// Save a copy of the log to the users desired location or a default location\r
317         /// if this feature is enabled in options.\r
318         /// </summary>\r
319         /// <param name="destination">\r
320         /// The Destination File Path\r
321         /// </param>\r
322         protected void CopyLog(string destination)\r
323         {\r
324             try\r
325             {\r
326                 string logDir = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) +\r
327                                 "\\HandBrake\\logs";\r
328                 string tempLogFile = Path.Combine(logDir, "last_encode_log.txt");\r
329 \r
330                 string encodeDestinationPath = Path.GetDirectoryName(destination);\r
331                 string destinationFile = Path.GetFileName(destination);\r
332                 string encodeLogFile = destinationFile + " " +\r
333                                        DateTime.Now.ToString().Replace("/", "-").Replace(":", "-") + ".txt";\r
334 \r
335                 // Make sure the log directory exists.\r
336                 if (!Directory.Exists(logDir))\r
337                     Directory.CreateDirectory(logDir);\r
338 \r
339                 // Copy the Log to HandBrakes log folder in the users applciation data folder.\r
340                 File.Copy(tempLogFile, Path.Combine(logDir, encodeLogFile));\r
341 \r
342                 // Save a copy of the log file in the same location as the enocde.\r
343                 if (Settings.Default.saveLogWithVideo)\r
344                     File.Copy(tempLogFile, Path.Combine(encodeDestinationPath, encodeLogFile));\r
345 \r
346                 // Save a copy of the log file to a user specified location\r
347                 if (Directory.Exists(Settings.Default.saveLogPath))\r
348                     if (Settings.Default.saveLogPath != String.Empty && Settings.Default.saveLogToSpecifiedPath)\r
349                         File.Copy(tempLogFile, Path.Combine(Settings.Default.saveLogPath, encodeLogFile));\r
350             }\r
351             catch (Exception exc)\r
352             {\r
353                 Main.ShowExceptiowWindow("Unable to make a copy of the log file", exc.ToString());\r
354             }\r
355         }\r
356 \r
357         /// <summary>\r
358         /// The HandBrakeCLI process has exited.\r
359         /// </summary>\r
360         /// <param name="sender">\r
361         /// The sender.\r
362         /// </param>\r
363         /// <param name="e">\r
364         /// The EventArgs.\r
365         /// </param>\r
366         private void HbProcess_Exited(object sender, EventArgs e)\r
367         {\r
368             IsEncoding = false;\r
369 \r
370             //   ReadFile(null);\r
371 \r
372             if (this.EncodeEnded != null)\r
373                 this.EncodeEnded(this, new EventArgs());\r
374 \r
375             if (windowsSeven.IsWindowsSeven)\r
376             {\r
377                 windowsSeven.SetTaskBarProgressToNoProgress();\r
378             }\r
379 \r
380             if (Properties.Settings.Default.preventSleep)\r
381             {\r
382                 Win32.AllowSleep();\r
383             }\r
384 \r
385             try\r
386             {\r
387                 if (fileWriter != null)\r
388                     fileWriter.Close();\r
389             } \r
390             catch(Exception exc)\r
391             {\r
392                 Main.ShowExceptiowWindow("Unable to close the log file wrtier", exc.ToString());\r
393             }\r
394         }\r
395 \r
396         /// <summary>\r
397         /// Read the log file\r
398         /// </summary>\r
399         /// <param name="n">\r
400         /// The object.\r
401         /// </param>\r
402         private void ReadFile()\r
403         {\r
404             logBuffer = new StringBuilder();\r
405             lock (logBuffer)\r
406             {\r
407                 // 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
408                 // we'll need to make a copy of it.\r
409                 string logDir = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\HandBrake\\logs";\r
410                 string logFile = Path.Combine(logDir, "last_encode_log.txt");\r
411                 string logFile2 = Path.Combine(logDir, "tmp_appReadable_log.txt");\r
412                 int logFilePosition = 0;\r
413 \r
414                 try\r
415                 {\r
416                     // Make sure the application readable log file does not already exist. FileCopy fill fail if it does.\r
417                     if (File.Exists(logFile2))\r
418                         File.Delete(logFile2);\r
419 \r
420                     // Copy the log file.\r
421                     if (File.Exists(logFile))\r
422                         File.Copy(logFile, logFile2, true);\r
423                     else\r
424                         return;\r
425  \r
426                     // Start the Reader\r
427                     // Only use text which continues on from the last read line\r
428                     StreamReader sr = new StreamReader(logFile2);\r
429                     string line;\r
430                     int i = 1;\r
431                     while ((line = sr.ReadLine()) != null)\r
432                     {\r
433                         if (i > logFilePosition)\r
434                         {\r
435                             logBuffer.AppendLine(line);\r
436                             logFilePosition++;\r
437                         }\r
438                         i++;\r
439                     }\r
440                     sr.Close();\r
441                     sr.Dispose();\r
442                 }\r
443                 catch (Exception exc)\r
444                 {\r
445                     Main.ShowExceptiowWindow("Unable to read log file", exc.ToString());\r
446                 }\r
447             }\r
448         }\r
449 \r
450         /// <summary>\r
451         /// Setup the logging.\r
452         /// </summary>\r
453         private void SetupLogging(Job encodeJob)\r
454         {\r
455             string logDir = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\HandBrake\\logs";\r
456             string logFile = Path.Combine(logDir, "last_encode_log.txt");\r
457             string logFile2 = Path.Combine(logDir, "tmp_appReadable_log.txt");\r
458 \r
459             try\r
460             {\r
461                 logBuffer = new StringBuilder();\r
462 \r
463                 // Clear the current Encode Logs\r
464                 if (File.Exists(logFile)) File.Delete(logFile);\r
465                 if (File.Exists(logFile2)) File.Delete(logFile2);\r
466 \r
467                 fileWriter = new StreamWriter(logFile) { AutoFlush = true };\r
468 \r
469                 fileWriter.WriteLine(CreateCliLogHeader(encodeJob));\r
470                 logBuffer.AppendLine(CreateCliLogHeader(encodeJob));\r
471             }\r
472             catch (Exception exc)\r
473             {\r
474                 if (fileWriter != null)\r
475                     fileWriter.Close();\r
476                 Main.ShowExceptiowWindow("Error", exc.ToString());\r
477             }\r
478         }\r
479 \r
480         /// <summary>\r
481         /// Recieve the Standard Error information and process it\r
482         /// </summary>\r
483         /// <param name="sender">\r
484         /// The Sender Object\r
485         /// </param>\r
486         /// <param name="e">\r
487         /// DataReceived EventArgs\r
488         /// </param>\r
489         private void HbProcErrorDataReceived(object sender, DataReceivedEventArgs e)\r
490         {\r
491             if (!String.IsNullOrEmpty(e.Data))\r
492             {\r
493                 lock (logBuffer)\r
494                     logBuffer.AppendLine(e.Data);\r
495 \r
496                 try\r
497                 {\r
498                     if (fileWriter != null)\r
499                         fileWriter.WriteLine(e.Data);\r
500                 }\r
501                 catch (Exception exc)\r
502                 {\r
503                     Main.ShowExceptiowWindow("Unable to write log data...", exc.ToString());\r
504                 }         \r
505             }\r
506         }\r
507 \r
508         #region Encode Status from Standard Output\r
509 \r
510         /// <summary>\r
511         /// Encode Started\r
512         /// </summary>\r
513         /// <param name="sender">\r
514         /// The sender.\r
515         /// </param>\r
516         /// <param name="e">\r
517         /// The EventArgs.\r
518         /// </param>\r
519         private void Encode_EncodeStarted(object sender, EventArgs e)\r
520         {\r
521             Thread monitor = new Thread(EncodeMonitor);\r
522             monitor.Start();\r
523         }\r
524 \r
525         /// <summary>\r
526         /// Monitor the Job\r
527         /// </summary>\r
528         private void EncodeMonitor()\r
529         {\r
530             try\r
531             {\r
532                 Parser encode = new Parser(HbProcess.StandardOutput.BaseStream);\r
533                 encode.OnEncodeProgress += EncodeOnEncodeProgress;\r
534                 while (!encode.EndOfStream)\r
535                     encode.ReadEncodeStatus();\r
536             }\r
537             catch (Exception exc)\r
538             {\r
539                 Main.ShowExceptiowWindow("An Unknown Error has occured", exc.ToString());\r
540             }\r
541         }\r
542 \r
543         /// <summary>\r
544         /// Displays the Encode status in the GUI\r
545         /// </summary>\r
546         /// <param name="sender">The sender</param>\r
547         /// <param name="currentTask">The current task</param>\r
548         /// <param name="taskCount">Number of tasks</param>\r
549         /// <param name="percentComplete">Percent complete</param>\r
550         /// <param name="currentFps">Current encode speed in fps</param>\r
551         /// <param name="avg">Avg encode speed</param>\r
552         /// <param name="timeRemaining">Time Left</param>\r
553         private void EncodeOnEncodeProgress(object sender, int currentTask, int taskCount, float percentComplete, float currentFps, float avg, TimeSpan timeRemaining)\r
554         {\r
555             EncodeProgressEventArgs eventArgs = new EncodeProgressEventArgs\r
556                 {\r
557                     AverageFrameRate = avg,\r
558                     CurrentFrameRate = currentFps,\r
559                     EstimatedTimeLeft = timeRemaining,\r
560                     PercentComplete = percentComplete,\r
561                     Task = currentTask,\r
562                     TaskCount = taskCount\r
563                 };\r
564 \r
565             if (this.EncodeStatusChanged != null)\r
566                 this.EncodeStatusChanged(this, eventArgs);\r
567 \r
568             if (windowsSeven.IsWindowsSeven)\r
569             {\r
570                 int percent;\r
571                 int.TryParse(Math.Round(percentComplete).ToString(), out percent);\r
572 \r
573                 windowsSeven.SetTaskBarProgress(percent);\r
574             }\r
575         }\r
576 \r
577         #endregion\r
578     }\r
579 }