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