summaryrefslogtreecommitdiffstats
path: root/win/CS/HandBrake.ApplicationServices/Services/Encode.cs
diff options
context:
space:
mode:
authorsr55 <[email protected]>2011-03-13 16:44:49 +0000
committersr55 <[email protected]>2011-03-13 16:44:49 +0000
commitb6a5d4eba610711d15ed99dc5f2e9e126ce06086 (patch)
treee72eb5be161c3ac9b01bbc3b3efcd4ab8f91e06c /win/CS/HandBrake.ApplicationServices/Services/Encode.cs
parent38f64c238720fe0524f99b380fbb1a8c795a7586 (diff)
Rename Direction C# to CS
git-svn-id: svn://svn.handbrake.fr/HandBrake/trunk@3846 b64f7644-9d1e-0410-96f1-a4d463321fa5
Diffstat (limited to 'win/CS/HandBrake.ApplicationServices/Services/Encode.cs')
-rw-r--r--win/CS/HandBrake.ApplicationServices/Services/Encode.cs595
1 files changed, 595 insertions, 0 deletions
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: <http://handbrake.fr/>.
+ 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;
+
+ /// <summary>
+ /// Class which handles the CLI
+ /// </summary>
+ public class Encode : IEncode
+ {
+ #region Private Variables
+
+ /// <summary>
+ /// The Log Buffer
+ /// </summary>
+ private StringBuilder logBuffer;
+
+ /// <summary>
+ /// The Log file writer
+ /// </summary>
+ private StreamWriter fileWriter;
+
+ /// <summary>
+ /// Gets The Process Handle
+ /// </summary>
+ private IntPtr processHandle;
+
+ /// <summary>
+ /// Gets the Process ID
+ /// </summary>
+ private int processId;
+
+ /// <summary>
+ /// Windows 7 API Pack wrapper
+ /// </summary>
+ private Win7 windowsSeven = new Win7();
+
+ /// <summary>
+ /// A Lock for the filewriter
+ /// </summary>
+ static readonly object fileWriterLock = new object();
+
+ #endregion
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Encode"/> class.
+ /// </summary>
+ public Encode()
+ {
+ this.EncodeStarted += this.EncodeEncodeStarted;
+ GrowlCommunicator.Register();
+ }
+
+ #region Delegates and Event Handlers
+
+ /// <summary>
+ /// Fires when a new CLI QueueTask starts
+ /// </summary>
+ public event EventHandler EncodeStarted;
+
+ /// <summary>
+ /// Fires when a CLI QueueTask finishes.
+ /// </summary>
+ public event EncodeCompletedStatus EncodeCompleted;
+
+ /// <summary>
+ /// Encode process has progressed
+ /// </summary>
+ public event EncodeProgessStatus EncodeStatusChanged;
+ #endregion
+
+ #region Properties
+
+ /// <summary>
+ /// Gets or sets The HB Process
+ /// </summary>
+ protected Process HbProcess { get; set; }
+
+ /// <summary>
+ /// Gets a value indicating whether IsEncoding.
+ /// </summary>
+ public bool IsEncoding { get; private set; }
+
+ /// <summary>
+ /// Gets ActivityLog.
+ /// </summary>
+ 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
+
+ /// <summary>
+ /// Execute a HandBrakeCLI process.
+ /// </summary>
+ /// <param name="encodeQueueTask">
+ /// The encodeQueueTask.
+ /// </param>
+ /// <param name="enableLogging">
+ /// Enable Logging. When Disabled we onlt parse Standard Ouput for progress info. Standard Error log data is ignored.
+ /// </param>
+ 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()"));
+ }
+ }
+
+ /// <summary>
+ /// Stop the Encode
+ /// </summary>
+ public void Stop()
+ {
+ this.Stop(null);
+ }
+
+ /// <summary>
+ /// Kill the CLI process
+ /// </summary>
+ /// <param name="exc">
+ /// The Exception that has occured.
+ /// This will get bubbled up through the EncodeCompletedEventArgs
+ /// </param>
+ 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."));
+ }
+ }
+
+ /// <summary>
+ /// 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
+ /// </summary>
+ 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");
+ //}*/
+ }
+
+ /// <summary>
+ /// Save a copy of the log to the users desired location or a default location
+ /// if this feature is enabled in options.
+ /// </summary>
+ /// <param name="destination">
+ /// The Destination File Path
+ /// </param>
+ 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
+
+ /// <summary>
+ /// The HandBrakeCLI process has exited.
+ /// </summary>
+ /// <param name="sender">
+ /// The sender.
+ /// </param>
+ /// <param name="e">
+ /// The EventArgs.
+ /// </param>
+ 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));
+ }
+
+ /// <summary>
+ /// Read the log file
+ /// </summary>
+ 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);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Setup the logging.
+ /// </summary>
+ /// <param name="encodeQueueTask">
+ /// The encode QueueTask.
+ /// </param>
+ 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;
+ }
+ }
+
+ /// <summary>
+ /// Recieve the Standard Error information and process it
+ /// </summary>
+ /// <param name="sender">
+ /// The Sender Object
+ /// </param>
+ /// <param name="e">
+ /// DataReceived EventArgs
+ /// </param>
+ 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.
+ }
+ }
+ }
+
+ /// <summary>
+ /// Encode Started
+ /// </summary>
+ /// <param name="sender">
+ /// The sender.
+ /// </param>
+ /// <param name="e">
+ /// The EventArgs.
+ /// </param>
+ private void EncodeEncodeStarted(object sender, EventArgs e)
+ {
+ Thread monitor = new Thread(EncodeMonitor);
+ monitor.Start();
+ }
+
+ /// <summary>
+ /// Monitor the QueueTask
+ /// </summary>
+ 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..");
+ }
+ }
+
+ /// <summary>
+ /// Displays the Encode status in the GUI
+ /// </summary>
+ /// <param name="sender">The sender</param>
+ /// <param name="currentTask">The current task</param>
+ /// <param name="taskCount">Number of tasks</param>
+ /// <param name="percentComplete">Percent complete</param>
+ /// <param name="currentFps">Current encode speed in fps</param>
+ /// <param name="avg">Avg encode speed</param>
+ /// <param name="timeRemaining">Time Left</param>
+ 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