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