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