From b6a5d4eba610711d15ed99dc5f2e9e126ce06086 Mon Sep 17 00:00:00 2001 From: sr55 Date: Sun, 13 Mar 2011 16:44:49 +0000 Subject: Rename Direction C# to CS git-svn-id: svn://svn.handbrake.fr/HandBrake/trunk@3846 b64f7644-9d1e-0410-96f1-a4d463321fa5 --- .../Services/Encode.cs | 595 +++++++++++++++++++++ 1 file changed, 595 insertions(+) create mode 100644 win/CS/HandBrake.ApplicationServices/Services/Encode.cs (limited to 'win/CS/HandBrake.ApplicationServices/Services/Encode.cs') diff --git a/win/CS/HandBrake.ApplicationServices/Services/Encode.cs b/win/CS/HandBrake.ApplicationServices/Services/Encode.cs new file mode 100644 index 000000000..4a6333da7 --- /dev/null +++ b/win/CS/HandBrake.ApplicationServices/Services/Encode.cs @@ -0,0 +1,595 @@ +/* Encode.cs $ + This file is part of the HandBrake source code. + Homepage: . + It may be used under the terms of the GNU General Public License. */ + +namespace HandBrake.ApplicationServices.Services +{ + using System; + using System.ComponentModel; + using System.Diagnostics; + using System.IO; + using System.Text; + using System.Threading; + using System.Windows.Forms; + + using HandBrake.ApplicationServices.EventArgs; + using HandBrake.ApplicationServices.Functions; + using HandBrake.ApplicationServices.Model; + using HandBrake.ApplicationServices.Parsing; + using HandBrake.ApplicationServices.Services.Interfaces; + using HandBrake.ApplicationServices.Utilities; + + /// + /// Class which handles the CLI + /// + public class Encode : IEncode + { + #region Private Variables + + /// + /// The Log Buffer + /// + private StringBuilder logBuffer; + + /// + /// The Log file writer + /// + private StreamWriter fileWriter; + + /// + /// Gets The Process Handle + /// + private IntPtr processHandle; + + /// + /// Gets the Process ID + /// + private int processId; + + /// + /// Windows 7 API Pack wrapper + /// + private Win7 windowsSeven = new Win7(); + + /// + /// A Lock for the filewriter + /// + static readonly object fileWriterLock = new object(); + + #endregion + + /// + /// Initializes a new instance of the class. + /// + public Encode() + { + this.EncodeStarted += this.EncodeEncodeStarted; + GrowlCommunicator.Register(); + } + + #region Delegates and Event Handlers + + /// + /// Fires when a new CLI QueueTask starts + /// + public event EventHandler EncodeStarted; + + /// + /// Fires when a CLI QueueTask finishes. + /// + public event EncodeCompletedStatus EncodeCompleted; + + /// + /// Encode process has progressed + /// + public event EncodeProgessStatus EncodeStatusChanged; + #endregion + + #region Properties + + /// + /// Gets or sets The HB Process + /// + protected Process HbProcess { get; set; } + + /// + /// Gets a value indicating whether IsEncoding. + /// + public bool IsEncoding { get; private set; } + + /// + /// Gets ActivityLog. + /// + public string ActivityLog + { + get + { + if (this.IsEncoding == false) + { + try + { + ReadFile(); // Read the last log file back in if it exists + } + catch (Exception exc) + { + return exc.ToString(); + } + } + + return string.IsNullOrEmpty(this.logBuffer.ToString()) ? "No log data available..." : this.logBuffer.ToString(); + } + } + + #endregion + + #region Public Methods + + /// + /// Execute a HandBrakeCLI process. + /// + /// + /// The encodeQueueTask. + /// + /// + /// Enable Logging. When Disabled we onlt parse Standard Ouput for progress info. Standard Error log data is ignored. + /// + public void Start(QueueTask encodeQueueTask, bool enableLogging) + { + try + { + QueueTask queueTask = encodeQueueTask; + + if (queueTask == null) + { + throw new ArgumentNullException("QueueTask was null"); + } + + if (IsEncoding) + { + throw new Exception("HandBrake is already encodeing."); + } + + IsEncoding = true; + + if (enableLogging) + { + try + { + SetupLogging(queueTask); + } + catch (Exception) + { + IsEncoding = false; + throw; + } + } + + if (Init.PreventSleep) + { + Win32.PreventSleep(); + } + + string handbrakeCLIPath = Path.Combine(Application.StartupPath, "HandBrakeCLI.exe"); + ProcessStartInfo cliStart = new ProcessStartInfo(handbrakeCLIPath, queueTask.Query) + { + RedirectStandardOutput = true, + RedirectStandardError = enableLogging ? true : false, + UseShellExecute = false, + CreateNoWindow = !Init.ShowCliForInGuiEncodeStatus ? true : false + }; + + this.HbProcess = new Process { StartInfo = cliStart }; + + this.HbProcess.Start(); + + if (enableLogging) + { + this.HbProcess.ErrorDataReceived += HbProcErrorDataReceived; + this.HbProcess.BeginErrorReadLine(); + } + + this.processId = HbProcess.Id; + this.processHandle = HbProcess.Handle; + + // Set the process Priority + if (this.processId != -1) + { + this.HbProcess.EnableRaisingEvents = true; + this.HbProcess.Exited += this.HbProcessExited; + } + + // Set the Process Priority + switch (Init.ProcessPriority) + { + case "Realtime": + this.HbProcess.PriorityClass = ProcessPriorityClass.RealTime; + break; + case "High": + this.HbProcess.PriorityClass = ProcessPriorityClass.High; + break; + case "Above Normal": + this.HbProcess.PriorityClass = ProcessPriorityClass.AboveNormal; + break; + case "Normal": + this.HbProcess.PriorityClass = ProcessPriorityClass.Normal; + break; + case "Low": + this.HbProcess.PriorityClass = ProcessPriorityClass.Idle; + break; + default: + this.HbProcess.PriorityClass = ProcessPriorityClass.BelowNormal; + break; + } + + // Fire the Encode Started Event + if (this.EncodeStarted != null) + this.EncodeStarted(this, new EventArgs()); + } + catch (Exception exc) + { + if (this.EncodeCompleted != null) + this.EncodeCompleted(this, new EncodeCompletedEventArgs(false, exc, "An Error has occured in EncodeService.Run()")); + } + } + + /// + /// Stop the Encode + /// + public void Stop() + { + this.Stop(null); + } + + /// + /// Kill the CLI process + /// + /// + /// The Exception that has occured. + /// This will get bubbled up through the EncodeCompletedEventArgs + /// + public void Stop(Exception exc) + { + try + { + if (this.HbProcess != null && !this.HbProcess.HasExited) + { + this.HbProcess.Kill(); + } + } + catch (Exception) + { + // No need to report anything to the user. If it fails, it's probably already stopped. + } + + + if (exc == null) + { + if (this.EncodeCompleted != null) + this.EncodeCompleted(this, new EncodeCompletedEventArgs(true, null, string.Empty)); + } + else + { + if (this.EncodeCompleted != null) + this.EncodeCompleted(this, new EncodeCompletedEventArgs(false, exc, "An Error has occured.")); + } + } + + /// + /// Attempt to Safely kill a DirectRun() CLI + /// NOTE: This will not work with a MinGW CLI + /// Note: http://www.cygwin.com/ml/cygwin/2006-03/msg00330.html + /// + public void SafelyStop() + { + if ((int)this.processHandle == 0) + return; + + // Allow the CLI to exit cleanly + Win32.SetForegroundWindow((int)this.processHandle); + SendKeys.Send("^C"); + SendKeys.Flush(); + + /*/if (HbProcess != null) + //{ + // HbProcess.StandardInput.AutoFlush = true; + // HbProcess.StandardInput.WriteLine("^c^z"); + //}*/ + } + + /// + /// Save a copy of the log to the users desired location or a default location + /// if this feature is enabled in options. + /// + /// + /// The Destination File Path + /// + public void ProcessLogs(string destination) + { + try + { + string logDir = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + + "\\HandBrake\\logs"; + string tempLogFile = Path.Combine(logDir, string.Format("last_encode_log{0}.txt", Init.InstanceId)); + + string encodeDestinationPath = Path.GetDirectoryName(destination); + string destinationFile = Path.GetFileName(destination); + string encodeLogFile = destinationFile + " " + + DateTime.Now.ToString().Replace("/", "-").Replace(":", "-") + ".txt"; + + // Make sure the log directory exists. + if (!Directory.Exists(logDir)) + Directory.CreateDirectory(logDir); + + // Copy the Log to HandBrakes log folder in the users applciation data folder. + File.Copy(tempLogFile, Path.Combine(logDir, encodeLogFile)); + + // Save a copy of the log file in the same location as the enocde. + if (Init.SaveLogWithVideo) + File.Copy(tempLogFile, Path.Combine(encodeDestinationPath, encodeLogFile)); + + // Save a copy of the log file to a user specified location + if (Directory.Exists(Init.SaveLogPath)) + if (Init.SaveLogPath != String.Empty && Init.SaveLogToSpecifiedPath) + File.Copy(tempLogFile, Path.Combine(Init.SaveLogPath, encodeLogFile)); + } + catch (Exception exc) + { + // This exception doesn't warrent user interaction, but it should be logged (TODO) + } + } + + #endregion + + #region Private Helper Methods + + /// + /// The HandBrakeCLI process has exited. + /// + /// + /// The sender. + /// + /// + /// The EventArgs. + /// + private void HbProcessExited(object sender, EventArgs e) + { + IsEncoding = false; + if (windowsSeven.IsWindowsSeven) + { + windowsSeven.SetTaskBarProgressToNoProgress(); + } + + if (Init.PreventSleep) + { + Win32.AllowSleep(); + } + + try + { + lock (fileWriterLock) + { + // This is just a quick hack to ensure that we are done processing the logging data. + // Logging data comes in after the exit event has processed sometimes. We should really impliment ISyncronizingInvoke + // and set the SyncObject on the process. I think this may resolve this properly. + // For now, just wait 2.5 seconds to let any trailing log messages come in and be processed. + Thread.Sleep(2500); + + this.HbProcess.CancelErrorRead(); + + if (fileWriter != null) + { + fileWriter.Close(); + fileWriter.Dispose(); + } + + fileWriter = null; + } + } + catch (Exception exc) + { + // This exception doesn't warrent user interaction, but it should be logged (TODO) + } + + if (this.EncodeCompleted != null) + this.EncodeCompleted(this, new EncodeCompletedEventArgs(true, null, string.Empty)); + } + + /// + /// Read the log file + /// + private void ReadFile() + { + logBuffer = new StringBuilder(); + lock (logBuffer) + { + // 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), + // we'll need to make a copy of it. + string logDir = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\HandBrake\\logs"; + string logFile = Path.Combine(logDir, string.Format("last_encode_log{0}.txt", Init.InstanceId)); + string logFile2 = Path.Combine(logDir, string.Format("tmp_appReadable_log{0}.txt", Init.InstanceId)); + int logFilePosition = 0; + + try + { + // Copy the log file. + if (File.Exists(logFile)) + File.Copy(logFile, logFile2, true); + else + return; + + // Start the Reader + // Only use text which continues on from the last read line + using (StreamReader sr = new StreamReader(logFile2)) + { + string line; + int i = 1; + while ((line = sr.ReadLine()) != null) + { + if (i > logFilePosition) + { + logBuffer.AppendLine(line); + logFilePosition++; + } + i++; + } + sr.Close(); + } + } + catch (Exception exc) + { + logBuffer.Append( + Environment.NewLine + "Unable to read Log file..." + Environment.NewLine + exc + + Environment.NewLine); + } + } + } + + /// + /// Setup the logging. + /// + /// + /// The encode QueueTask. + /// + private void SetupLogging(QueueTask encodeQueueTask) + { + string logDir = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\HandBrake\\logs"; + string logFile = Path.Combine(logDir, string.Format("last_encode_log{0}.txt", Init.InstanceId)); + string logFile2 = Path.Combine(logDir, string.Format("tmp_appReadable_log{0}.txt", Init.InstanceId)); + + try + { + logBuffer = new StringBuilder(); + + // Clear the current Encode Logs + if (File.Exists(logFile)) File.Delete(logFile); + if (File.Exists(logFile2)) File.Delete(logFile2); + + fileWriter = new StreamWriter(logFile) { AutoFlush = true }; + + fileWriter.WriteLine(UtilityService.CreateCliLogHeader(encodeQueueTask)); + logBuffer.AppendLine(UtilityService.CreateCliLogHeader(encodeQueueTask)); + } + catch (Exception) + { + if (fileWriter != null) + { + fileWriter.Close(); + fileWriter.Dispose(); + } + throw; + } + } + + /// + /// Recieve the Standard Error information and process it + /// + /// + /// The Sender Object + /// + /// + /// DataReceived EventArgs + /// + private void HbProcErrorDataReceived(object sender, DataReceivedEventArgs e) + { + if (!String.IsNullOrEmpty(e.Data)) + { + try + { + lock (logBuffer) + logBuffer.AppendLine(e.Data); + + lock (fileWriterLock) + { + if (fileWriter != null && fileWriter.BaseStream.CanWrite) + { + fileWriter.WriteLine(e.Data); + + // If the logging grows past 100MB, kill the encode and stop. + if (fileWriter.BaseStream.Length > 100000000) + { + this.Stop( + new Exception( + "The encode has been stopped. The log file has grown to over 100MB which indicates a serious problem has occured with the encode." + + "Please check the encode log for an indication of what the problem is.")); + } + } + } + } + catch (Exception exc) + { + // Do Nothing. + } + } + } + + /// + /// Encode Started + /// + /// + /// The sender. + /// + /// + /// The EventArgs. + /// + private void EncodeEncodeStarted(object sender, EventArgs e) + { + Thread monitor = new Thread(EncodeMonitor); + monitor.Start(); + } + + /// + /// Monitor the QueueTask + /// + private void EncodeMonitor() + { + try + { + Parser encode = new Parser(HbProcess.StandardOutput.BaseStream); + encode.OnEncodeProgress += EncodeOnEncodeProgress; + while (!encode.EndOfStream) + encode.ReadEncodeStatus(); + } + catch (Exception exc) + { + EncodeOnEncodeProgress(null, 0, 0, 0, 0, 0, "Unknown, status not available.."); + } + } + + /// + /// Displays the Encode status in the GUI + /// + /// The sender + /// The current task + /// Number of tasks + /// Percent complete + /// Current encode speed in fps + /// Avg encode speed + /// Time Left + private void EncodeOnEncodeProgress(object sender, int currentTask, int taskCount, float percentComplete, float currentFps, float avg, string timeRemaining) + { + EncodeProgressEventArgs eventArgs = new EncodeProgressEventArgs + { + AverageFrameRate = avg, + CurrentFrameRate = currentFps, + EstimatedTimeLeft = Converters.EncodeToTimespan(timeRemaining), + PercentComplete = percentComplete, + Task = currentTask, + TaskCount = taskCount + }; + + if (this.EncodeStatusChanged != null) + this.EncodeStatusChanged(this, eventArgs); + + if (windowsSeven.IsWindowsSeven) + { + int percent; + int.TryParse(Math.Round(percentComplete).ToString(), out percent); + + windowsSeven.SetTaskBarProgress(percent); + } + } + + #endregion + } +} \ No newline at end of file -- cgit v1.2.3