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     using Timer = System.Threading.Timer;\r
22 \r
23     /// <summary>\r
24     /// Class which handles the CLI\r
25     /// </summary>\r
26     public class Encode : IEncode\r
27     {\r
28         /* Private Variables */\r
29 \r
30         /// <summary>\r
31         /// An Encode Job\r
32         /// </summary>\r
33         private Job job;\r
34 \r
35         /// <summary>\r
36         /// The Log Buffer\r
37         /// </summary>\r
38         private StringBuilder logBuffer;\r
39 \r
40         /// <summary>\r
41         /// The line number thats been read to in the log file\r
42         /// </summary>\r
43         private int logFilePosition;\r
44 \r
45         /// <summary>\r
46         /// A Timer for this window\r
47         /// </summary>\r
48         private Timer windowTimer;\r
49 \r
50         /// <summary>\r
51         /// Gets The Process Handle\r
52         /// </summary>\r
53         private IntPtr processHandle;\r
54 \r
55         /// <summary>\r
56         /// Gets the Process ID\r
57         /// </summary>\r
58         private int processID;\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         }\r
69 \r
70         /* Delegates */\r
71 \r
72         /// <summary>\r
73         /// Encode Progess Status\r
74         /// </summary>\r
75         /// <param name="sender">\r
76         /// The sender.\r
77         /// </param>\r
78         /// <param name="e">\r
79         /// The EncodeProgressEventArgs.\r
80         /// </param>\r
81         public delegate void EncodeProgessStatus(object sender, EncodeProgressEventArgs e);\r
82 \r
83         /* Event Handlers */\r
84 \r
85         /// <summary>\r
86         /// Fires when a new CLI Job starts\r
87         /// </summary>\r
88         public event EventHandler EncodeStarted;\r
89 \r
90         /// <summary>\r
91         /// Fires when a CLI job finishes.\r
92         /// </summary>\r
93         public event EventHandler EncodeEnded;\r
94 \r
95         /// <summary>\r
96         /// Encode process has progressed\r
97         /// </summary>\r
98         public event EncodeProgessStatus EncodeStatusChanged;\r
99 \r
100         /* Properties */\r
101 \r
102         /// <summary>\r
103         /// Gets or sets The HB Process\r
104         /// </summary>\r
105         protected Process HbProcess { get; set; }\r
106 \r
107         /// <summary>\r
108         /// Gets a value indicating whether IsEncoding.\r
109         /// </summary>\r
110         public bool IsEncoding { get; private set; }\r
111 \r
112         /* Public Methods */\r
113 \r
114         /// <summary>\r
115         /// Gets ActivityLog.\r
116         /// </summary>\r
117         public string ActivityLog\r
118         {\r
119             get\r
120             {\r
121                 if (logBuffer == null)\r
122                 {\r
123                     ResetLogReader();\r
124                     ReadFile(null);\r
125                 }\r
126 \r
127                 return logBuffer != null ? logBuffer.ToString() : string.Empty;\r
128             }\r
129         }\r
130 \r
131         /// <summary>\r
132         /// Create a preview sample video\r
133         /// </summary>\r
134         /// <param name="query">\r
135         /// The CLI Query\r
136         /// </param>\r
137         public void CreatePreviewSample(string query)\r
138         {\r
139             this.Run(new Job { Query = query }, true);\r
140         }\r
141 \r
142         /// <summary>\r
143         /// Kill the CLI process\r
144         /// </summary>\r
145         public void Stop()\r
146         {\r
147             if (this.HbProcess != null)\r
148                 this.HbProcess.Kill();\r
149 \r
150             Process[] list = Process.GetProcessesByName("HandBrakeCLI");\r
151             foreach (Process process in list)\r
152                 process.Kill();\r
153 \r
154             if (this.EncodeEnded != null)\r
155                 this.EncodeEnded(this, new EventArgs());\r
156         }\r
157 \r
158         /// <summary>\r
159         /// Attempt to Safely kill a DirectRun() CLI\r
160         /// NOTE: This will not work with a MinGW CLI\r
161         /// Note: http://www.cygwin.com/ml/cygwin/2006-03/msg00330.html\r
162         /// </summary>\r
163         public void SafelyClose()\r
164         {\r
165             if ((int)this.processHandle == 0)\r
166                 return;\r
167 \r
168             // Allow the CLI to exit cleanly\r
169             Win32.SetForegroundWindow((int)this.processHandle);\r
170             SendKeys.Send("^C");\r
171             SendKeys.Flush();\r
172 \r
173             // HbProcess.StandardInput.AutoFlush = true;\r
174             // HbProcess.StandardInput.WriteLine("^C");\r
175         }\r
176 \r
177         /// <summary>\r
178         /// Execute a HandBrakeCLI process.\r
179         /// </summary>\r
180         /// <param name="encJob">\r
181         /// The enc Job.\r
182         /// </param>\r
183         /// <param name="requireStandardOuput">\r
184         /// Set to True to show no window and force standard output redirect\r
185         /// </param>\r
186         protected void Run(Job encJob, bool requireStandardOuput)\r
187         {\r
188             this.job = encJob;\r
189             try\r
190             {\r
191                 ResetLogReader();\r
192                 IsEncoding = true;\r
193 \r
194                 string handbrakeCLIPath = Path.Combine(Application.StartupPath, "HandBrakeCLI.exe");\r
195                 string logPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\HandBrake\\logs", "last_encode_log.txt");\r
196                 string strCmdLine = String.Format(@" /C """"{0}"" {1} 2>""{2}"" """, handbrakeCLIPath, encJob.Query, logPath);\r
197                 var cliStart = new ProcessStartInfo("CMD.exe", strCmdLine);\r
198 \r
199                 if (Settings.Default.enocdeStatusInGui || requireStandardOuput)\r
200                 {\r
201                     cliStart.RedirectStandardOutput = true;\r
202                     cliStart.UseShellExecute = false;\r
203                     if (!Settings.Default.showCliForInGuiEncodeStatus || requireStandardOuput)\r
204                         cliStart.CreateNoWindow = true;\r
205                 }\r
206                 if (Settings.Default.cli_minimized)\r
207                     cliStart.WindowStyle = ProcessWindowStyle.Minimized;\r
208 \r
209                 Process[] before = Process.GetProcesses(); // Get a list of running processes before starting.\r
210                 HbProcess = Process.Start(cliStart);\r
211                 this.processID = Main.GetCliProcess(before);\r
212 \r
213                 if (HbProcess != null)\r
214                     this.processHandle = HbProcess.MainWindowHandle; // Set the process Handle\r
215 \r
216                 // Start the Log Monitor\r
217                 windowTimer = new Timer(new TimerCallback(ReadFile), null, 1000, 1000);\r
218 \r
219                 // Set the process Priority\r
220                 Process hbCliProcess = null;\r
221                 if (this.processID != -1)\r
222                 {\r
223                     hbCliProcess = Process.GetProcessById(this.processID);\r
224                     hbCliProcess.EnableRaisingEvents = true;\r
225                     hbCliProcess.Exited += new EventHandler(HbProcess_Exited);\r
226                 }\r
227 \r
228                 if (hbCliProcess != null)\r
229                     switch (Settings.Default.processPriority)\r
230                     {\r
231                         case "Realtime":\r
232                             hbCliProcess.PriorityClass = ProcessPriorityClass.RealTime;\r
233                             break;\r
234                         case "High":\r
235                             hbCliProcess.PriorityClass = ProcessPriorityClass.High;\r
236                             break;\r
237                         case "Above Normal":\r
238                             hbCliProcess.PriorityClass = ProcessPriorityClass.AboveNormal;\r
239                             break;\r
240                         case "Normal":\r
241                             hbCliProcess.PriorityClass = ProcessPriorityClass.Normal;\r
242                             break;\r
243                         case "Low":\r
244                             hbCliProcess.PriorityClass = ProcessPriorityClass.Idle;\r
245                             break;\r
246                         default:\r
247                             hbCliProcess.PriorityClass = ProcessPriorityClass.BelowNormal;\r
248                             break;\r
249                     }\r
250 \r
251                 // Fire the Encode Started Event\r
252                 if (this.EncodeStarted != null)\r
253                     this.EncodeStarted(this, new EventArgs());\r
254             }\r
255             catch (Exception exc)\r
256             {\r
257                 Main.ShowExceptiowWindow("It would appear that HandBrakeCLI has not started correctly. You should take a look at the Activity log as it may indicate the reason why.\n\nDetailed Error Information: error occured in runCli()", exc.ToString());\r
258             }\r
259         }\r
260 \r
261         /// <summary>\r
262         /// The HandBrakeCLI process has exited.\r
263         /// </summary>\r
264         /// <param name="sender">\r
265         /// The sender.\r
266         /// </param>\r
267         /// <param name="e">\r
268         /// The EventArgs.\r
269         /// </param>\r
270         private void HbProcess_Exited(object sender, EventArgs e)\r
271         {\r
272             IsEncoding = false;\r
273 \r
274             windowTimer.Dispose();\r
275             ReadFile(null);\r
276 \r
277             if (this.EncodeEnded != null)\r
278                 this.EncodeEnded(this, new EventArgs());\r
279         }\r
280 \r
281         /// <summary>\r
282         /// Function to run the CLI directly rather than via CMD\r
283         /// TODO: Code to handle the Log data has yet to be written.\r
284         /// TODO: Code to handle the % / ETA info has to be written.\r
285         /// </summary>\r
286         /// <param name="query">\r
287         /// The query.\r
288         /// </param>\r
289         protected void DirectRun(string query)\r
290         {\r
291             try\r
292             {\r
293                 if (this.EncodeStarted != null)\r
294                     this.EncodeStarted(this, new EventArgs());\r
295 \r
296                 IsEncoding = true;\r
297 \r
298                 ResetLogReader();\r
299 \r
300                 // Setup the job\r
301                 string handbrakeCLIPath = Path.Combine(Environment.CurrentDirectory, "HandBrakeCLI.exe");\r
302                 HbProcess = new Process\r
303                                 {\r
304                                     StartInfo =\r
305                                         {\r
306                                             FileName = handbrakeCLIPath,\r
307                                             Arguments = query,\r
308                                             UseShellExecute = false,\r
309                                             RedirectStandardOutput = true,\r
310                                             RedirectStandardError = true,\r
311                                             RedirectStandardInput = true,\r
312                                             CreateNoWindow = false,\r
313                                             WindowStyle = ProcessWindowStyle.Minimized\r
314                                         }\r
315                                 };\r
316 \r
317                 // Setup event handlers for rediected data\r
318                 HbProcess.ErrorDataReceived += new DataReceivedEventHandler(HbProcErrorDataReceived);\r
319                 HbProcess.OutputDataReceived += new DataReceivedEventHandler(HbProcOutputDataReceived);\r
320 \r
321                 // Start the process\r
322                 HbProcess.Start();\r
323 \r
324                 // Setup the asynchronous reading of stdin and stderr\r
325                 HbProcess.BeginErrorReadLine();\r
326                 HbProcess.BeginOutputReadLine();\r
327 \r
328                 // Set the Process Priority);\r
329                 switch (Settings.Default.processPriority)\r
330                 {\r
331                     case "Realtime":\r
332                         HbProcess.PriorityClass = ProcessPriorityClass.RealTime;\r
333                         break;\r
334                     case "High":\r
335                         HbProcess.PriorityClass = ProcessPriorityClass.High;\r
336                         break;\r
337                     case "Above Normal":\r
338                         HbProcess.PriorityClass = ProcessPriorityClass.AboveNormal;\r
339                         break;\r
340                     case "Normal":\r
341                         HbProcess.PriorityClass = ProcessPriorityClass.Normal;\r
342                         break;\r
343                     case "Low":\r
344                         HbProcess.PriorityClass = ProcessPriorityClass.Idle;\r
345                         break;\r
346                     default:\r
347                         HbProcess.PriorityClass = ProcessPriorityClass.BelowNormal;\r
348                         break;\r
349                 }\r
350 \r
351                 // Set the class items\r
352                 this.processID = HbProcess.Id;\r
353                 this.processHandle = HbProcess.Handle;\r
354             }\r
355             catch (Exception exc)\r
356             {\r
357                 Console.WriteLine(exc);\r
358             }\r
359         }\r
360 \r
361         /// <summary>\r
362         /// Add the CLI Query to the Log File.\r
363         /// </summary>\r
364         /// <param name="encJob">\r
365         /// The Encode Job Object\r
366         /// </param>\r
367         protected void AddCLIQueryToLog(Job encJob)\r
368         {\r
369             try\r
370             {\r
371                 string logDir = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) +\r
372                                 "\\HandBrake\\logs";\r
373                 string logPath = Path.Combine(logDir, "last_encode_log.txt");\r
374 \r
375                 var reader = new StreamReader(File.Open(logPath, FileMode.Open, FileAccess.Read, FileShare.Read));\r
376                 string log = reader.ReadToEnd();\r
377                 reader.Close();\r
378 \r
379                 var writer = new StreamWriter(File.Create(logPath));\r
380 \r
381                 writer.WriteLine("### CLI Query: " + encJob.Query);\r
382                 writer.WriteLine("### User Query: " + encJob.CustomQuery);\r
383                 writer.WriteLine("#########################################");\r
384                 writer.WriteLine(log);\r
385                 writer.Flush();\r
386                 writer.Close();\r
387             }\r
388             catch (Exception)\r
389             {\r
390                 return;\r
391             }\r
392         }\r
393 \r
394         /// <summary>\r
395         /// Save a copy of the log to the users desired location or a default location\r
396         /// if this feature is enabled in options.\r
397         /// </summary>\r
398         /// <param name="destination">\r
399         /// The Destination File Path\r
400         /// </param>\r
401         protected void CopyLog(string destination)\r
402         {\r
403             try\r
404             {\r
405                 string logDir = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) +\r
406                                 "\\HandBrake\\logs";\r
407                 string tempLogFile = Path.Combine(logDir, "last_encode_log.txt");\r
408 \r
409                 string encodeDestinationPath = Path.GetDirectoryName(destination);\r
410                 string destinationFile = Path.GetFileName(destination);\r
411                 string encodeLogFile = destinationFile + " " +\r
412                                        DateTime.Now.ToString().Replace("/", "-").Replace(":", "-") + ".txt";\r
413 \r
414                 // Make sure the log directory exists.\r
415                 if (!Directory.Exists(logDir))\r
416                     Directory.CreateDirectory(logDir);\r
417 \r
418                 // Copy the Log to HandBrakes log folder in the users applciation data folder.\r
419                 File.Copy(tempLogFile, Path.Combine(logDir, encodeLogFile));\r
420 \r
421                 // Save a copy of the log file in the same location as the enocde.\r
422                 if (Settings.Default.saveLogWithVideo)\r
423                     File.Copy(tempLogFile, Path.Combine(encodeDestinationPath, encodeLogFile));\r
424 \r
425                 // Save a copy of the log file to a user specified location\r
426                 if (Directory.Exists(Settings.Default.saveLogPath))\r
427                     if (Settings.Default.saveLogPath != String.Empty && Settings.Default.saveLogToSpecifiedPath)\r
428                         File.Copy(tempLogFile, Path.Combine(Settings.Default.saveLogPath, encodeLogFile));\r
429             }\r
430             catch (Exception exc)\r
431             {\r
432                 Main.ShowExceptiowWindow("Unable to make a copy of the log file", exc.ToString());\r
433             }\r
434         }\r
435 \r
436         /// <summary>\r
437         /// Read the log file\r
438         /// </summary>\r
439         /// <param name="n">\r
440         /// The object.\r
441         /// </param>\r
442         private void ReadFile(object n)\r
443         {\r
444             lock (logBuffer)\r
445             {\r
446                 // 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
447                 // we'll need to make a copy of it.\r
448                 string logDir = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) +\r
449                                 "\\HandBrake\\logs";\r
450                 string logFile = Path.Combine(logDir, "last_encode_log.txt");\r
451                 string logFile2 = Path.Combine(logDir, "tmp_appReadable_log.txt");\r
452 \r
453                 try\r
454                 {\r
455                     // Make sure the application readable log file does not already exist. FileCopy fill fail if it does.\r
456                     if (File.Exists(logFile2))\r
457                         File.Delete(logFile2);\r
458 \r
459                     // Copy the log file.\r
460                     if (File.Exists(logFile))\r
461                         File.Copy(logFile, logFile2, true);\r
462                     else\r
463                     {\r
464                         ResetLogReader();\r
465                         return;\r
466                     }\r
467 \r
468                     // Put the Query and User Generated Query Flag on the log.\r
469                     if (logFilePosition == 0 && job.Query != null)\r
470                     {\r
471                         logBuffer.AppendLine("### CLI Query: " + job.Query);\r
472                         logBuffer.AppendLine("### User Query: " + job.CustomQuery);\r
473                         logBuffer.AppendLine("#########################################");\r
474                     }\r
475 \r
476                     // Start the Reader\r
477                     // Only use text which continues on from the last read line\r
478                     StreamReader sr = new StreamReader(logFile2);\r
479                     string line;\r
480                     int i = 1;\r
481                     while ((line = sr.ReadLine()) != null)\r
482                     {\r
483                         if (i > logFilePosition)\r
484                         {\r
485                             logBuffer.AppendLine(line);\r
486                             logFilePosition++;\r
487                         }\r
488                         i++;\r
489                     }\r
490                     sr.Close();\r
491                     sr.Dispose();\r
492                 }\r
493                 catch (Exception)\r
494                 {\r
495                     ResetLogReader();\r
496                 }\r
497             }\r
498         }\r
499 \r
500         /// <summary>\r
501         /// Reset the Log Reader\r
502         /// </summary>\r
503         private void ResetLogReader()\r
504         {\r
505             logFilePosition = 0;\r
506             logBuffer = new StringBuilder();\r
507         }\r
508 \r
509         /// <summary>\r
510         /// Recieve the Standard Error information and process it\r
511         /// </summary>\r
512         /// <param name="sender">\r
513         /// The Sender Object\r
514         /// </param>\r
515         /// <param name="e">\r
516         /// DataReceived EventArgs\r
517         /// </param>\r
518         private void HbProcErrorDataReceived(object sender, DataReceivedEventArgs e)\r
519         {\r
520             if (!String.IsNullOrEmpty(e.Data))\r
521             {\r
522                 lock (logBuffer)\r
523                     logBuffer.AppendLine(e.Data);\r
524             }\r
525         }\r
526 \r
527         /// <summary>\r
528         /// Standard Input Data Recieved from the CLI\r
529         /// </summary>\r
530         /// <param name="sender">\r
531         /// The Sender Object\r
532         /// </param>\r
533         /// <param name="e">\r
534         /// DataReceived EventArgs\r
535         /// </param>\r
536         private void HbProcOutputDataReceived(object sender, DataReceivedEventArgs e)\r
537         {\r
538             if (!String.IsNullOrEmpty(e.Data))\r
539             {\r
540                 lock (logBuffer)\r
541                     logBuffer.AppendLine(e.Data);\r
542             }\r
543         }\r
544 \r
545         /// <summary>\r
546         /// Encode Started\r
547         /// </summary>\r
548         /// <param name="sender">\r
549         /// The sender.\r
550         /// </param>\r
551         /// <param name="e">\r
552         /// The EventArgs.\r
553         /// </param>\r
554         private void Encode_EncodeStarted(object sender, EventArgs e)\r
555         {\r
556             Thread monitor = new Thread(EncodeMonitor);\r
557             monitor.Start();\r
558         }\r
559 \r
560         /// <summary>\r
561         /// Monitor the Job\r
562         /// </summary>\r
563         private void EncodeMonitor()\r
564         {\r
565             try\r
566             {\r
567                 Parser encode = new Parser(HbProcess.StandardOutput.BaseStream);\r
568                 encode.OnEncodeProgress += EncodeOnEncodeProgress;\r
569                 while (!encode.EndOfStream)\r
570                     encode.ReadEncodeStatus();\r
571 \r
572                // Main.ShowExceptiowWindow("Encode Monitor Stopped", "Stopped");\r
573             }\r
574             catch (Exception exc)\r
575             {\r
576                 Main.ShowExceptiowWindow("An Unknown Error has occured", exc.ToString());\r
577             }\r
578         }\r
579 \r
580         /// <summary>\r
581         /// Displays the Encode status in the GUI\r
582         /// </summary>\r
583         /// <param name="sender">The sender</param>\r
584         /// <param name="currentTask">The current task</param>\r
585         /// <param name="taskCount">Number of tasks</param>\r
586         /// <param name="percentComplete">Percent complete</param>\r
587         /// <param name="currentFps">Current encode speed in fps</param>\r
588         /// <param name="avg">Avg encode speed</param>\r
589         /// <param name="timeRemaining">Time Left</param>\r
590         private void EncodeOnEncodeProgress(object sender, int currentTask, int taskCount, float percentComplete, float currentFps, float avg, TimeSpan timeRemaining)\r
591         {\r
592             EncodeProgressEventArgs eventArgs = new EncodeProgressEventArgs\r
593                 {\r
594                     AverageFrameRate = avg,\r
595                     CurrentFrameRate = currentFps,\r
596                     EstimatedTimeLeft = timeRemaining,\r
597                     PercentComplete = percentComplete,\r
598                     Task = currentTask,\r
599                     TaskCount = taskCount\r
600                 };\r
601 \r
602             if (this.EncodeStatusChanged != null)\r
603                 this.EncodeStatusChanged(this, eventArgs);\r
604         }\r
605     }\r
606 }