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
6 namespace HandBrake.ApplicationServices.Services
\r
9 using System.Diagnostics;
\r
12 using System.Threading;
\r
13 using System.Windows.Forms;
\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
21 using Timer = System.Threading.Timer;
\r
24 /// Class which handles the CLI
\r
26 public class Encode : IEncode
\r
28 #region Private Variables
\r
38 private StringBuilder logBuffer;
\r
41 /// The Log file writer
\r
43 private StreamWriter fileWriter;
\r
46 /// Gets The Process Handle
\r
48 private IntPtr processHandle;
\r
51 /// Gets the Process ID
\r
53 private int processID;
\r
56 /// Windows 7 API Pack wrapper
\r
58 private Win7 windowsSeven = new Win7();
\r
65 /// Initializes a new instance of the <see cref="Encode"/> class.
\r
69 this.EncodeStarted += Encode_EncodeStarted;
\r
72 #region Delegates and Event Handlers
\r
75 /// Encode Progess Status
\r
77 /// <param name="sender">
\r
80 /// <param name="e">
\r
81 /// The EncodeProgressEventArgs.
\r
83 public delegate void EncodeProgessStatus(object sender, EncodeProgressEventArgs e);
\r
85 /* Event Handlers */
\r
88 /// Fires when a new CLI Job starts
\r
90 public event EventHandler EncodeStarted;
\r
93 /// Fires when a CLI job finishes.
\r
95 public event EventHandler EncodeEnded;
\r
98 /// Encode process has progressed
\r
100 public event EncodeProgessStatus EncodeStatusChanged;
\r
106 /// Gets or sets The HB Process
\r
108 protected Process HbProcess { get; set; }
\r
111 /// Gets a value indicating whether IsEncoding.
\r
113 public bool IsEncoding { get; private set; }
\r
116 /// Gets ActivityLog.
\r
118 public string ActivityLog
\r
122 if (this.IsEncoding == false)
\r
124 ReadFile(); // Read the last log file back in if it exists
\r
127 return string.IsNullOrEmpty(this.logBuffer.ToString()) ? "No log data available..." : this.logBuffer.ToString();
\r
131 /* Public Methods */
\r
134 /// Create a preview sample video
\r
136 /// <param name="query">
\r
139 public void CreatePreviewSample(string query)
\r
141 this.Run(new Job { Query = query }, false);
\r
145 /// Execute a HandBrakeCLI process.
\r
147 /// <param name="encJob">
\r
150 /// <param name="enableLogging">
\r
151 /// Enable Logging. When Disabled we onlt parse Standard Ouput for progress info. Standard Error log data is ignored.
\r
153 protected void Run(Job encJob, bool enableLogging)
\r
161 SetupLogging(encJob);
\r
163 if (Settings.Default.preventSleep)
\r
165 Win32.PreventSleep();
\r
168 string handbrakeCLIPath = Path.Combine(Application.StartupPath, "HandBrakeCLI.exe");
\r
169 ProcessStartInfo cliStart = new ProcessStartInfo(handbrakeCLIPath, encJob.Query)
\r
171 RedirectStandardOutput = true,
\r
172 RedirectStandardError = enableLogging ? true : false,
\r
173 UseShellExecute = false,
\r
174 CreateNoWindow = !Settings.Default.showCliForInGuiEncodeStatus ? true : false
\r
177 this.HbProcess = Process.Start(cliStart);
\r
181 this.HbProcess.ErrorDataReceived += HbProcErrorDataReceived;
\r
182 this.HbProcess.BeginErrorReadLine();
\r
185 this.processID = HbProcess.Id;
\r
186 this.processHandle = HbProcess.MainWindowHandle;
\r
188 // Set the process Priority
\r
189 if (this.processID != -1)
\r
191 this.HbProcess.EnableRaisingEvents = true;
\r
192 this.HbProcess.Exited += HbProcess_Exited;
\r
195 // Set the Process Priority
\r
196 switch (Settings.Default.processPriority)
\r
199 this.HbProcess.PriorityClass = ProcessPriorityClass.RealTime;
\r
202 this.HbProcess.PriorityClass = ProcessPriorityClass.High;
\r
204 case "Above Normal":
\r
205 this.HbProcess.PriorityClass = ProcessPriorityClass.AboveNormal;
\r
208 this.HbProcess.PriorityClass = ProcessPriorityClass.Normal;
\r
211 this.HbProcess.PriorityClass = ProcessPriorityClass.Idle;
\r
214 this.HbProcess.PriorityClass = ProcessPriorityClass.BelowNormal;
\r
218 // Fire the Encode Started Event
\r
219 if (this.EncodeStarted != null)
\r
220 this.EncodeStarted(this, new EventArgs());
\r
222 catch (Exception exc)
\r
224 Main.ShowExceptiowWindow("It would appear that HandBrakeCLI has not started correctly."+
\r
225 "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
231 /// Kill the CLI process
\r
237 if (this.HbProcess != null) this.HbProcess.Kill();
\r
239 Process[] list = Process.GetProcessesByName("HandBrakeCLI");
\r
240 foreach (Process process in list)
\r
243 catch (Exception exc)
\r
245 Main.ShowExceptiowWindow("Unable to stop HandBrakeCLI. It may not be running.", exc.ToString());
\r
248 if (this.EncodeEnded != null)
\r
249 this.EncodeEnded(this, new EventArgs());
\r
253 /// Attempt to Safely kill a DirectRun() CLI
\r
254 /// NOTE: This will not work with a MinGW CLI
\r
255 /// Note: http://www.cygwin.com/ml/cygwin/2006-03/msg00330.html
\r
257 public void SafelyClose()
\r
259 if ((int)this.processHandle == 0)
\r
262 // Allow the CLI to exit cleanly
\r
263 Win32.SetForegroundWindow((int)this.processHandle);
\r
264 SendKeys.Send("^C");
\r
267 if (HbProcess != null)
\r
269 HbProcess.StandardInput.AutoFlush = true;
\r
270 HbProcess.StandardInput.WriteLine("^C");
\r
276 /// Add the CLI Query to the Log File.
\r
278 /// <param name="encJob">
\r
279 /// The Encode Job Object
\r
281 private static string CreateCliLogHeader(Job encJob)
\r
285 //string logDir = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) +
\r
286 // "\\HandBrake\\logs";
\r
287 //string logPath = Path.Combine(logDir, "last_encode_log.txt");
\r
289 //var reader = new StreamReader(File.Open(logPath, FileMode.Open, FileAccess.Read, FileShare.Read));
\r
290 //string log = reader.ReadToEnd();
\r
293 //var writer = new StreamWriter(File.Create(logPath));
\r
296 //writer.WriteLine("### CLI Query: " + encJob.Query);
\r
297 //writer.WriteLine("### User Query: " + encJob.CustomQuery);
\r
298 //writer.WriteLine("#########################################");
\r
299 //writer.WriteLine(log);
\r
302 StringBuilder logHeader = new StringBuilder();
\r
303 logHeader.AppendLine("### CLI Query: " + encJob.Query);
\r
304 logHeader.AppendLine("### User Query: " + encJob.CustomQuery);
\r
305 logHeader.AppendLine("#########################################");
\r
307 return logHeader.ToString();
\r
311 return string.Empty;
\r
316 /// Save a copy of the log to the users desired location or a default location
\r
317 /// if this feature is enabled in options.
\r
319 /// <param name="destination">
\r
320 /// The Destination File Path
\r
322 protected void CopyLog(string destination)
\r
326 string logDir = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) +
\r
327 "\\HandBrake\\logs";
\r
328 string tempLogFile = Path.Combine(logDir, "last_encode_log.txt");
\r
330 string encodeDestinationPath = Path.GetDirectoryName(destination);
\r
331 string destinationFile = Path.GetFileName(destination);
\r
332 string encodeLogFile = destinationFile + " " +
\r
333 DateTime.Now.ToString().Replace("/", "-").Replace(":", "-") + ".txt";
\r
335 // Make sure the log directory exists.
\r
336 if (!Directory.Exists(logDir))
\r
337 Directory.CreateDirectory(logDir);
\r
339 // Copy the Log to HandBrakes log folder in the users applciation data folder.
\r
340 File.Copy(tempLogFile, Path.Combine(logDir, encodeLogFile));
\r
342 // Save a copy of the log file in the same location as the enocde.
\r
343 if (Settings.Default.saveLogWithVideo)
\r
344 File.Copy(tempLogFile, Path.Combine(encodeDestinationPath, encodeLogFile));
\r
346 // Save a copy of the log file to a user specified location
\r
347 if (Directory.Exists(Settings.Default.saveLogPath))
\r
348 if (Settings.Default.saveLogPath != String.Empty && Settings.Default.saveLogToSpecifiedPath)
\r
349 File.Copy(tempLogFile, Path.Combine(Settings.Default.saveLogPath, encodeLogFile));
\r
351 catch (Exception exc)
\r
353 Main.ShowExceptiowWindow("Unable to make a copy of the log file", exc.ToString());
\r
358 /// The HandBrakeCLI process has exited.
\r
360 /// <param name="sender">
\r
363 /// <param name="e">
\r
366 private void HbProcess_Exited(object sender, EventArgs e)
\r
368 IsEncoding = false;
\r
372 if (this.EncodeEnded != null)
\r
373 this.EncodeEnded(this, new EventArgs());
\r
375 if (windowsSeven.IsWindowsSeven)
\r
377 windowsSeven.SetTaskBarProgressToNoProgress();
\r
380 if (Properties.Settings.Default.preventSleep)
\r
382 Win32.AllowSleep();
\r
387 if (fileWriter != null)
\r
388 fileWriter.Close();
\r
390 catch(Exception exc)
\r
392 Main.ShowExceptiowWindow("Unable to close the log file wrtier", exc.ToString());
\r
397 /// Read the log file
\r
399 /// <param name="n">
\r
402 private void ReadFile()
\r
404 logBuffer = new StringBuilder();
\r
407 // 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
408 // we'll need to make a copy of it.
\r
409 string logDir = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\HandBrake\\logs";
\r
410 string logFile = Path.Combine(logDir, "last_encode_log.txt");
\r
411 string logFile2 = Path.Combine(logDir, "tmp_appReadable_log.txt");
\r
412 int logFilePosition = 0;
\r
416 // Make sure the application readable log file does not already exist. FileCopy fill fail if it does.
\r
417 if (File.Exists(logFile2))
\r
418 File.Delete(logFile2);
\r
420 // Copy the log file.
\r
421 if (File.Exists(logFile))
\r
422 File.Copy(logFile, logFile2, true);
\r
426 // Start the Reader
\r
427 // Only use text which continues on from the last read line
\r
428 StreamReader sr = new StreamReader(logFile2);
\r
431 while ((line = sr.ReadLine()) != null)
\r
433 if (i > logFilePosition)
\r
435 logBuffer.AppendLine(line);
\r
443 catch (Exception exc)
\r
445 Main.ShowExceptiowWindow("Unable to read log file", exc.ToString());
\r
451 /// Setup the logging.
\r
453 private void SetupLogging(Job encodeJob)
\r
455 string logDir = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\HandBrake\\logs";
\r
456 string logFile = Path.Combine(logDir, "last_encode_log.txt");
\r
457 string logFile2 = Path.Combine(logDir, "tmp_appReadable_log.txt");
\r
461 logBuffer = new StringBuilder();
\r
463 // Clear the current Encode Logs
\r
464 if (File.Exists(logFile)) File.Delete(logFile);
\r
465 if (File.Exists(logFile2)) File.Delete(logFile2);
\r
467 fileWriter = new StreamWriter(logFile) { AutoFlush = true };
\r
469 fileWriter.WriteLine(CreateCliLogHeader(encodeJob));
\r
470 logBuffer.AppendLine(CreateCliLogHeader(encodeJob));
\r
472 catch (Exception exc)
\r
474 if (fileWriter != null)
\r
475 fileWriter.Close();
\r
476 Main.ShowExceptiowWindow("Error", exc.ToString());
\r
481 /// Recieve the Standard Error information and process it
\r
483 /// <param name="sender">
\r
484 /// The Sender Object
\r
486 /// <param name="e">
\r
487 /// DataReceived EventArgs
\r
489 private void HbProcErrorDataReceived(object sender, DataReceivedEventArgs e)
\r
491 if (!String.IsNullOrEmpty(e.Data))
\r
494 logBuffer.AppendLine(e.Data);
\r
498 if (fileWriter != null)
\r
499 fileWriter.WriteLine(e.Data);
\r
501 catch (Exception exc)
\r
503 Main.ShowExceptiowWindow("Unable to write log data...", exc.ToString());
\r
508 #region Encode Status from Standard Output
\r
513 /// <param name="sender">
\r
516 /// <param name="e">
\r
519 private void Encode_EncodeStarted(object sender, EventArgs e)
\r
521 Thread monitor = new Thread(EncodeMonitor);
\r
526 /// Monitor the Job
\r
528 private void EncodeMonitor()
\r
532 Parser encode = new Parser(HbProcess.StandardOutput.BaseStream);
\r
533 encode.OnEncodeProgress += EncodeOnEncodeProgress;
\r
534 while (!encode.EndOfStream)
\r
535 encode.ReadEncodeStatus();
\r
537 catch (Exception exc)
\r
539 Main.ShowExceptiowWindow("An Unknown Error has occured", exc.ToString());
\r
544 /// Displays the Encode status in the GUI
\r
546 /// <param name="sender">The sender</param>
\r
547 /// <param name="currentTask">The current task</param>
\r
548 /// <param name="taskCount">Number of tasks</param>
\r
549 /// <param name="percentComplete">Percent complete</param>
\r
550 /// <param name="currentFps">Current encode speed in fps</param>
\r
551 /// <param name="avg">Avg encode speed</param>
\r
552 /// <param name="timeRemaining">Time Left</param>
\r
553 private void EncodeOnEncodeProgress(object sender, int currentTask, int taskCount, float percentComplete, float currentFps, float avg, TimeSpan timeRemaining)
\r
555 EncodeProgressEventArgs eventArgs = new EncodeProgressEventArgs
\r
557 AverageFrameRate = avg,
\r
558 CurrentFrameRate = currentFps,
\r
559 EstimatedTimeLeft = timeRemaining,
\r
560 PercentComplete = percentComplete,
\r
561 Task = currentTask,
\r
562 TaskCount = taskCount
\r
565 if (this.EncodeStatusChanged != null)
\r
566 this.EncodeStatusChanged(this, eventArgs);
\r
568 if (windowsSeven.IsWindowsSeven)
\r
571 int.TryParse(Math.Round(percentComplete).ToString(), out percent);
\r
573 windowsSeven.SetTaskBarProgress(percent);
\r