OSDN Git Service

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