OSDN Git Service

CLI: update the built in presets
[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.HasExited)\r
246                 {\r
247                     this.HbProcess.Kill();\r
248                 }\r
249             }\r
250             catch (Exception exc)\r
251             {\r
252                 errorService.ShowError("Unable to stop HandBrakeCLI. It may not be running.", exc.ToString());\r
253             }\r
254 \r
255             if (this.EncodeEnded != null)\r
256                 this.EncodeEnded(this, new EventArgs());\r
257         }\r
258 \r
259         /// <summary>\r
260         /// Attempt to Safely kill a DirectRun() CLI\r
261         /// NOTE: This will not work with a MinGW CLI\r
262         /// Note: http://www.cygwin.com/ml/cygwin/2006-03/msg00330.html\r
263         /// </summary>\r
264         public void SafelyClose()\r
265         {\r
266             if ((int)this.processHandle == 0)\r
267                 return;\r
268 \r
269             // Allow the CLI to exit cleanly\r
270             Win32.SetForegroundWindow((int)this.processHandle);\r
271             SendKeys.Send("^C");\r
272             SendKeys.Flush();\r
273 \r
274             /*/if (HbProcess != null)\r
275             //{\r
276             //    HbProcess.StandardInput.AutoFlush = true;\r
277             //    HbProcess.StandardInput.WriteLine("^c^z");\r
278             //}*/\r
279         }\r
280 \r
281         /* Helpers */\r
282 \r
283         /// <summary>\r
284         /// Save a copy of the log to the users desired location or a default location\r
285         /// if this feature is enabled in options.\r
286         /// </summary>\r
287         /// <param name="destination">\r
288         /// The Destination File Path\r
289         /// </param>\r
290         protected void CopyLog(string destination)\r
291         {\r
292             try\r
293             {\r
294                 string logDir = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) +\r
295                                 "\\HandBrake\\logs";\r
296                 string tempLogFile = Path.Combine(logDir, string.Format("last_encode_log{0}.txt", Init.InstanceId));\r
297 \r
298                 string encodeDestinationPath = Path.GetDirectoryName(destination);\r
299                 string destinationFile = Path.GetFileName(destination);\r
300                 string encodeLogFile = destinationFile + " " +\r
301                                        DateTime.Now.ToString().Replace("/", "-").Replace(":", "-") + ".txt";\r
302 \r
303                 // Make sure the log directory exists.\r
304                 if (!Directory.Exists(logDir))\r
305                     Directory.CreateDirectory(logDir);\r
306 \r
307                 // Copy the Log to HandBrakes log folder in the users applciation data folder.\r
308                 File.Copy(tempLogFile, Path.Combine(logDir, encodeLogFile));\r
309 \r
310                 // Save a copy of the log file in the same location as the enocde.\r
311                 if (Init.SaveLogWithVideo)\r
312                     File.Copy(tempLogFile, Path.Combine(encodeDestinationPath, encodeLogFile));\r
313 \r
314                 // Save a copy of the log file to a user specified location\r
315                 if (Directory.Exists(Init.SaveLogPath))\r
316                     if (Init.SaveLogPath != String.Empty && Init.SaveLogToSpecifiedPath)\r
317                         File.Copy(tempLogFile, Path.Combine(Init.SaveLogPath, encodeLogFile));\r
318             }\r
319             catch (Exception exc)\r
320             {\r
321                 errorService.ShowError("Unable to make a copy of the log file", exc.ToString());\r
322             }\r
323         }\r
324 \r
325         /// <summary>\r
326         /// The HandBrakeCLI process has exited.\r
327         /// </summary>\r
328         /// <param name="sender">\r
329         /// The sender.\r
330         /// </param>\r
331         /// <param name="e">\r
332         /// The EventArgs.\r
333         /// </param>\r
334         private void HbProcess_Exited(object sender, EventArgs e)\r
335         {\r
336             IsEncoding = false;\r
337             if (this.EncodeEnded != null)\r
338                 this.EncodeEnded(this, new EventArgs());\r
339 \r
340             if (windowsSeven.IsWindowsSeven)\r
341             {\r
342                 windowsSeven.SetTaskBarProgressToNoProgress();\r
343             }\r
344 \r
345             if (Init.PreventSleep)\r
346             {\r
347                 Win32.AllowSleep();\r
348             }\r
349 \r
350             try\r
351             {\r
352                 if (fileWriter != null)\r
353                 {\r
354                     fileWriter.Close();\r
355                     fileWriter.Dispose();\r
356                 }\r
357             }\r
358             catch (Exception exc)\r
359             {\r
360                 errorService.ShowError("Unable to close the log file wrtier", exc.ToString());\r
361             }\r
362         }\r
363 \r
364         /// <summary>\r
365         /// Read the log file\r
366         /// </summary>\r
367         private void ReadFile()\r
368         {\r
369             logBuffer = new StringBuilder();\r
370             lock (logBuffer)\r
371             {\r
372                 // 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
373                 // we'll need to make a copy of it.\r
374                 string logDir = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\HandBrake\\logs";\r
375                 string logFile = Path.Combine(logDir, string.Format("last_encode_log{0}.txt", Init.InstanceId));\r
376                 string logFile2 = Path.Combine(logDir, string.Format("tmp_appReadable_log{0}.txt", Init.InstanceId));\r
377                 int logFilePosition = 0;\r
378 \r
379                 try\r
380                 {\r
381                     // Copy the log file.\r
382                     if (File.Exists(logFile))\r
383                         File.Copy(logFile, logFile2, true);\r
384                     else\r
385                         return;\r
386 \r
387                     // Start the Reader\r
388                     // Only use text which continues on from the last read line\r
389                     using (StreamReader sr = new StreamReader(logFile2))\r
390                     {\r
391                         string line;\r
392                         int i = 1;\r
393                         while ((line = sr.ReadLine()) != null)\r
394                         {\r
395                             if (i > logFilePosition)\r
396                             {\r
397                                 logBuffer.AppendLine(line);\r
398                                 logFilePosition++;\r
399                             }\r
400                             i++;\r
401                         }\r
402                         sr.Close();\r
403                     }\r
404                 }\r
405                 catch (Exception exc)\r
406                 {\r
407                     throw new Exception("Unable to read log file" + Environment.NewLine + exc);\r
408                 }\r
409             }\r
410         }\r
411 \r
412         /// <summary>\r
413         /// Setup the logging.\r
414         /// </summary>\r
415         /// <param name="encodeJob">\r
416         /// The encode Job.\r
417         /// </param>\r
418         private void SetupLogging(Job encodeJob)\r
419         {\r
420             string logDir = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\HandBrake\\logs";\r
421             string logFile = Path.Combine(logDir, string.Format("last_encode_log{0}.txt", Init.InstanceId));\r
422             string logFile2 = Path.Combine(logDir, string.Format("tmp_appReadable_log{0}.txt", Init.InstanceId));\r
423 \r
424             try\r
425             {\r
426                 logBuffer = new StringBuilder();\r
427 \r
428                 // Clear the current Encode Logs\r
429                 if (File.Exists(logFile)) File.Delete(logFile);\r
430                 if (File.Exists(logFile2)) File.Delete(logFile2);\r
431 \r
432                 fileWriter = new StreamWriter(logFile) { AutoFlush = true };\r
433 \r
434                 fileWriter.WriteLine(Logging.CreateCliLogHeader(encodeJob));\r
435                 logBuffer.AppendLine(Logging.CreateCliLogHeader(encodeJob));\r
436             }\r
437             catch (Exception exc)\r
438             {\r
439                 if (fileWriter != null)\r
440                 {\r
441                     fileWriter.Close();\r
442                     fileWriter.Dispose();\r
443                 }\r
444 \r
445                 errorService.ShowError("Error", exc.ToString());\r
446             }\r
447         }\r
448 \r
449         /// <summary>\r
450         /// Recieve the Standard Error information and process it\r
451         /// </summary>\r
452         /// <param name="sender">\r
453         /// The Sender Object\r
454         /// </param>\r
455         /// <param name="e">\r
456         /// DataReceived EventArgs\r
457         /// </param>\r
458         private void HbProcErrorDataReceived(object sender, DataReceivedEventArgs e)\r
459         {\r
460             if (!String.IsNullOrEmpty(e.Data))\r
461             {\r
462                 try\r
463                 {\r
464                     lock (logBuffer)\r
465                         logBuffer.AppendLine(e.Data);\r
466 \r
467                     if (fileWriter != null && fileWriter.BaseStream.CanWrite)\r
468                     {\r
469                         fileWriter.WriteLine(e.Data);\r
470 \r
471                         // If the logging grows past 100MB, kill the encode and stop.\r
472                         if (fileWriter.BaseStream.Length > 100000000)\r
473                         {\r
474                             this.Stop();\r
475                             errorService.ShowError("The encode has been stopped. The log file has grown to over 100MB which indicates a serious problem has occured with the encode.",\r
476                                 "Please check the encode log for an indication of what the problem is.");\r
477                         }\r
478                     }            \r
479                 }\r
480                 catch (Exception exc)\r
481                 {\r
482                     // errorService.ShowError("Unable to write log data...", exc.ToString());\r
483                 }\r
484             }\r
485         }\r
486 \r
487         #region Encode Status from Standard Output\r
488 \r
489         /// <summary>\r
490         /// Encode Started\r
491         /// </summary>\r
492         /// <param name="sender">\r
493         /// The sender.\r
494         /// </param>\r
495         /// <param name="e">\r
496         /// The EventArgs.\r
497         /// </param>\r
498         private void Encode_EncodeStarted(object sender, EventArgs e)\r
499         {\r
500             Thread monitor = new Thread(EncodeMonitor);\r
501             monitor.Start();\r
502         }\r
503 \r
504         /// <summary>\r
505         /// Monitor the Job\r
506         /// </summary>\r
507         private void EncodeMonitor()\r
508         {\r
509             try\r
510             {\r
511                 Parser encode = new Parser(HbProcess.StandardOutput.BaseStream);\r
512                 encode.OnEncodeProgress += EncodeOnEncodeProgress;\r
513                 while (!encode.EndOfStream)\r
514                     encode.ReadEncodeStatus();\r
515             }\r
516             catch (Exception exc)\r
517             {\r
518                 EncodeOnEncodeProgress(null, 0, 0, 0, 0, 0, "Unknown, status not available..");\r
519             }\r
520         }\r
521 \r
522         /// <summary>\r
523         /// Displays the Encode status in the GUI\r
524         /// </summary>\r
525         /// <param name="sender">The sender</param>\r
526         /// <param name="currentTask">The current task</param>\r
527         /// <param name="taskCount">Number of tasks</param>\r
528         /// <param name="percentComplete">Percent complete</param>\r
529         /// <param name="currentFps">Current encode speed in fps</param>\r
530         /// <param name="avg">Avg encode speed</param>\r
531         /// <param name="timeRemaining">Time Left</param>\r
532         private void EncodeOnEncodeProgress(object sender, int currentTask, int taskCount, float percentComplete, float currentFps, float avg, string timeRemaining)\r
533         {\r
534             EncodeProgressEventArgs eventArgs = new EncodeProgressEventArgs\r
535                 {\r
536                     AverageFrameRate = avg,\r
537                     CurrentFrameRate = currentFps,\r
538                     EstimatedTimeLeft = timeRemaining,\r
539                     PercentComplete = percentComplete,\r
540                     Task = currentTask,\r
541                     TaskCount = taskCount\r
542                 };\r
543 \r
544             if (this.EncodeStatusChanged != null)\r
545                 this.EncodeStatusChanged(this, eventArgs);\r
546 \r
547             if (windowsSeven.IsWindowsSeven)\r
548             {\r
549                 int percent;\r
550                 int.TryParse(Math.Round(percentComplete).ToString(), out percent);\r
551 \r
552                 windowsSeven.SetTaskBarProgress(percent);\r
553             }\r
554         }\r
555 \r
556         #endregion\r
557     }\r
558 }