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