summaryrefslogtreecommitdiffstats
path: root/win/CS/HandBrake.ApplicationServices/Services/Encode
diff options
context:
space:
mode:
authorsr55 <[email protected]>2014-11-29 18:36:54 +0000
committersr55 <[email protected]>2014-11-29 18:36:54 +0000
commit0e8fddb81113a16689bb5c26a5844cb0c1260cec (patch)
treeeda41ed99656ba86d225ec43802f7891e6d8436a /win/CS/HandBrake.ApplicationServices/Services/Encode
parent7c0af498a9d5f7aae1e5c8d06b939c8189edcfbe (diff)
WinGui: Part 1 - Restructuring the AppServices library in preparation for the new JSON API. Taking the opportunity to improve and simplify the API.
git-svn-id: svn://svn.handbrake.fr/HandBrake/trunk@6568 b64f7644-9d1e-0410-96f1-a4d463321fa5
Diffstat (limited to 'win/CS/HandBrake.ApplicationServices/Services/Encode')
-rw-r--r--win/CS/HandBrake.ApplicationServices/Services/Encode/EncodeBase.cs434
-rw-r--r--win/CS/HandBrake.ApplicationServices/Services/Encode/EncodeService.cs337
-rw-r--r--win/CS/HandBrake.ApplicationServices/Services/Encode/EventArgs/EncodeCompletedEventArgs.cs68
-rw-r--r--win/CS/HandBrake.ApplicationServices/Services/Encode/EventArgs/EncodeProgressEventArgs.cs63
-rw-r--r--win/CS/HandBrake.ApplicationServices/Services/Encode/Interfaces/IEncode.cs123
-rw-r--r--win/CS/HandBrake.ApplicationServices/Services/Encode/Interfaces/IEncodeServiceWrapper.cs18
-rw-r--r--win/CS/HandBrake.ApplicationServices/Services/Encode/LibEncode.cs326
7 files changed, 1369 insertions, 0 deletions
diff --git a/win/CS/HandBrake.ApplicationServices/Services/Encode/EncodeBase.cs b/win/CS/HandBrake.ApplicationServices/Services/Encode/EncodeBase.cs
new file mode 100644
index 000000000..a805a714b
--- /dev/null
+++ b/win/CS/HandBrake.ApplicationServices/Services/Encode/EncodeBase.cs
@@ -0,0 +1,434 @@
+// --------------------------------------------------------------------------------------------------------------------
+// <copyright file="EncodeBase.cs" company="HandBrake Project (http://handbrake.fr)">
+// This file is part of the HandBrake source code - It may be used under the terms of the GNU General Public License.
+// </copyright>
+// <summary>
+// A Base Class for the Encode Services.
+// </summary>
+// --------------------------------------------------------------------------------------------------------------------
+
+namespace HandBrake.ApplicationServices.Services.Encode
+{
+ using System;
+ using System.Globalization;
+ using System.IO;
+ using System.Text;
+ using System.Text.RegularExpressions;
+
+ using HandBrake.ApplicationServices.Exceptions;
+ using HandBrake.ApplicationServices.Model;
+ using HandBrake.ApplicationServices.Services.Encode.EventArgs;
+ using HandBrake.ApplicationServices.Services.Encode.Interfaces;
+ using HandBrake.ApplicationServices.Utilities;
+
+ /// <summary>
+ /// A Base Class for the Encode Services.
+ /// </summary>
+ public class EncodeBase
+ {
+ #region Private Variables
+
+ /// <summary>
+ /// A Lock for the filewriter
+ /// </summary>
+ private static readonly object FileWriterLock = new object();
+
+ /// <summary>
+ /// The Log File Header
+ /// </summary>
+ private readonly StringBuilder header;
+
+ /// <summary>
+ /// The Log Buffer
+ /// </summary>
+ private StringBuilder logBuffer;
+
+ /// <summary>
+ /// The Log file writer
+ /// </summary>
+ private StreamWriter fileWriter;
+
+ #endregion
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="EncodeBase"/> class.
+ /// </summary>
+ public EncodeBase()
+ {
+ this.logBuffer = new StringBuilder();
+ this.header = GeneralUtilities.CreateCliLogHeader();
+ this.LogIndex = 0;
+ }
+
+ #region Events
+
+ /// <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 a value indicating whether IsEncoding.
+ /// </summary>
+ public bool IsEncoding { get; protected set; }
+
+ /// <summary>
+ /// Gets ActivityLog.
+ /// </summary>
+ public string ActivityLog
+ {
+ get
+ {
+ string noLog =
+ "No log data available... Log data will show when you start an encode. \n\nOpen the log file directory to get previous log files.";
+ return string.IsNullOrEmpty(this.logBuffer.ToString())
+ ? this.header + noLog
+ : this.header + this.logBuffer.ToString();
+ }
+ }
+
+ /// <summary>
+ /// Gets the log index.
+ /// </summary>
+ public int LogIndex { get; private set; }
+
+ /// <summary>
+ /// Gets LogBuffer.
+ /// </summary>
+ public StringBuilder LogBuffer
+ {
+ get
+ {
+ return this.logBuffer;
+ }
+ }
+
+ #endregion
+
+ #region Invoke Events
+
+ /// <summary>
+ /// Invoke the Encode Status Changed Event.
+ /// </summary>
+ /// <param name="e">
+ /// The EncodeProgressEventArgs.
+ /// </param>
+ public void InvokeEncodeStatusChanged(EncodeProgressEventArgs e)
+ {
+ EncodeProgessStatus handler = this.EncodeStatusChanged;
+ if (handler != null)
+ {
+ handler(this, e);
+ }
+ }
+
+ /// <summary>
+ /// Invoke the Encode Completed Event
+ /// </summary>
+ /// <param name="e">
+ /// The EncodeCompletedEventArgs.
+ /// </param>
+ public void InvokeEncodeCompleted(EncodeCompletedEventArgs e)
+ {
+ EncodeCompletedStatus handler = this.EncodeCompleted;
+ if (handler != null)
+ {
+ handler(this, e);
+ }
+
+ this.LogIndex = 0; // Reset
+ }
+
+ /// <summary>
+ /// Invoke the Encode Started Event
+ /// </summary>
+ /// <param name="e">
+ /// The EventArgs.
+ /// </param>
+ public void InvokeEncodeStarted(System.EventArgs e)
+ {
+ EventHandler handler = this.EncodeStarted;
+ if (handler != null)
+ {
+ handler(this, e);
+ }
+ }
+
+ #endregion
+
+ #region Methods
+
+ /// <summary>
+ /// A Stop Method to be implemeneted.
+ /// </summary>
+ public virtual void Stop()
+ {
+ // Do Nothing
+ }
+
+ /// <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>
+ /// <param name="configuration">
+ /// The configuration.
+ /// </param>
+ public void ProcessLogs(string destination, HBConfiguration configuration)
+ {
+ try
+ {
+ string logDir = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) +
+ "\\HandBrake\\logs";
+ string tempLogFile = Path.Combine(logDir, string.Format("last_encode_log{0}.txt", GeneralUtilities.ProcessId));
+
+ 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 (configuration.SaveLogWithVideo)
+ {
+ File.Copy(tempLogFile, Path.Combine(encodeDestinationPath, encodeLogFile));
+ }
+
+ // Save a copy of the log file to a user specified location
+ if (Directory.Exists(configuration.SaveLogCopyDirectory) && configuration.SaveLogToCopyDirectory)
+ {
+ File.Copy(
+ tempLogFile, Path.Combine(configuration.SaveLogCopyDirectory, encodeLogFile));
+ }
+ }
+ catch (Exception)
+ {
+ // This exception doesn't warrent user interaction, but it should be logged (TODO)
+ }
+ }
+
+ /// <summary>
+ /// Pase the CLI status output (from standard output)
+ /// </summary>
+ /// <param name="encodeStatus">
+ /// The encode Status.
+ /// </param>
+ /// <param name="startTime">
+ /// The start Time.
+ /// </param>
+ /// <returns>
+ /// The <see cref="EncodeProgressEventArgs"/>.
+ /// </returns>
+ public EncodeProgressEventArgs ReadEncodeStatus(string encodeStatus, DateTime startTime)
+ {
+ try
+ {
+ Match m = Regex.Match(
+ encodeStatus,
+ @"^Encoding: task ([0-9]*) of ([0-9]*), ([0-9]*\.[0-9]*) %( \(([0-9]*\.[0-9]*) fps, avg ([0-9]*\.[0-9]*) fps, ETA ([0-9]{2})h([0-9]{2})m([0-9]{2})s\))?");
+
+ if (m.Success)
+ {
+ int currentTask = int.Parse(m.Groups[1].Value);
+ int totalTasks = int.Parse(m.Groups[2].Value);
+ float percent = float.Parse(m.Groups[3].Value, CultureInfo.InvariantCulture);
+ float currentFps = m.Groups[5].Value == string.Empty
+ ? 0.0F
+ : float.Parse(m.Groups[5].Value, CultureInfo.InvariantCulture);
+ float avgFps = m.Groups[6].Value == string.Empty
+ ? 0.0F
+ : float.Parse(m.Groups[6].Value, CultureInfo.InvariantCulture);
+ string remaining = string.Empty;
+ if (m.Groups[7].Value != string.Empty)
+ {
+ remaining = m.Groups[7].Value + ":" + m.Groups[8].Value + ":" + m.Groups[9].Value;
+ }
+ if (string.IsNullOrEmpty(remaining))
+ {
+ remaining = "Calculating ...";
+ }
+
+ EncodeProgressEventArgs eventArgs = new EncodeProgressEventArgs
+ {
+ AverageFrameRate = avgFps,
+ CurrentFrameRate = currentFps,
+ EstimatedTimeLeft =
+ Converters.EncodeToTimespan(
+ remaining),
+ PercentComplete = percent,
+ Task = currentTask,
+ TaskCount = totalTasks,
+ ElapsedTime =
+ DateTime.Now - startTime,
+ };
+
+ return eventArgs;
+ }
+
+ return null;
+ }
+ catch (Exception)
+ {
+ return null;
+ }
+ }
+
+ /// <summary>
+ /// Setup the logging.
+ /// </summary>
+ /// <param name="encodeQueueTask">
+ /// The encode QueueTask.
+ /// </param>
+ protected void SetupLogging(QueueTask encodeQueueTask)
+ {
+ this.ShutdownFileWriter();
+ string logDir = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\HandBrake\\logs";
+ string logFile = Path.Combine(logDir, string.Format("last_encode_log{0}.txt", GeneralUtilities.ProcessId));
+ string logFile2 = Path.Combine(logDir, string.Format("tmp_appReadable_log{0}.txt", GeneralUtilities.ProcessId));
+
+ try
+ {
+ string query = QueryGeneratorUtility.GenerateQuery(new EncodeTask(encodeQueueTask.Task), encodeQueueTask.Configuration);
+ this.logBuffer = new StringBuilder();
+ this.logBuffer.AppendLine(String.Format("CLI Query: {0}", query));
+ this.logBuffer.AppendLine();
+
+ // Clear the current Encode Logs)
+ if (File.Exists(logFile))
+ {
+ File.Delete(logFile);
+ }
+
+ if (File.Exists(logFile2))
+ {
+ File.Delete(logFile2);
+ }
+
+ this.fileWriter = new StreamWriter(logFile) { AutoFlush = true };
+ this.fileWriter.WriteLine(this.header);
+ this.fileWriter.WriteLine(string.Format("CLI Query: {0}", query));
+ this.fileWriter.WriteLine();
+ }
+ catch (Exception)
+ {
+ if (this.fileWriter != null)
+ {
+ this.fileWriter.Close();
+ this.fileWriter.Dispose();
+ }
+
+ throw;
+ }
+ }
+
+ /// <summary>
+ /// Process an Incomming Log Message.
+ /// </summary>
+ /// <param name="message">
+ /// The message.
+ /// </param>
+ protected void ProcessLogMessage(string message)
+ {
+ if (!String.IsNullOrEmpty(message))
+ {
+ try
+ {
+ this.LogIndex = this.LogIndex + 1;
+
+ lock (this.LogBuffer)
+ {
+ this.LogBuffer.AppendLine(message);
+ }
+
+ lock (FileWriterLock)
+ {
+ if (this.fileWriter != null && this.fileWriter.BaseStream.CanWrite)
+ {
+ this.fileWriter.WriteLine(message);
+ }
+ }
+ }
+ catch (Exception)
+ {
+ // Do Nothing.
+ }
+ }
+ }
+
+ /// <summary>
+ /// Shutdown and Dispose of the File Writer.
+ /// </summary>
+ protected void ShutdownFileWriter()
+ {
+ try
+ {
+ lock (FileWriterLock)
+ {
+ if (this.fileWriter != null)
+ {
+ this.fileWriter.Close();
+ this.fileWriter.Dispose();
+ }
+
+ this.fileWriter = null;
+ }
+ }
+ catch (Exception)
+ {
+ // This exception doesn't warrent user interaction, but it should be logged (TODO)
+ }
+ }
+
+ /// <summary>
+ /// Verify the Encode Destination path exists and if not, create it.
+ /// </summary>
+ /// <param name="task">
+ /// The task.
+ /// </param>
+ /// <exception cref="Exception">
+ /// If the creation fails, an exception is thrown.
+ /// </exception>
+ protected void VerifyEncodeDestinationPath(QueueTask task)
+ {
+ // Make sure the path exists, attempt to create it if it doesn't
+ try
+ {
+ string path = Directory.GetParent(task.Task.Destination).ToString();
+ if (!Directory.Exists(path))
+ {
+ Directory.CreateDirectory(path);
+ }
+ }
+ catch (Exception exc)
+ {
+ throw new GeneralApplicationException(
+ "Unable to create directory for the encoded output.", "Please verify that you have a valid path.", exc);
+ }
+ }
+
+ #endregion
+ }
+} \ No newline at end of file
diff --git a/win/CS/HandBrake.ApplicationServices/Services/Encode/EncodeService.cs b/win/CS/HandBrake.ApplicationServices/Services/Encode/EncodeService.cs
new file mode 100644
index 000000000..dad5b9597
--- /dev/null
+++ b/win/CS/HandBrake.ApplicationServices/Services/Encode/EncodeService.cs
@@ -0,0 +1,337 @@
+// --------------------------------------------------------------------------------------------------------------------
+// <copyright file="EncodeService.cs" company="HandBrake Project (http://handbrake.fr)">
+// This file is part of the HandBrake source code - It may be used under the terms of the GNU General Public License.
+// </copyright>
+// <summary>
+// Class which handles the CLI
+// </summary>
+// --------------------------------------------------------------------------------------------------------------------
+
+namespace HandBrake.ApplicationServices.Services.Encode
+{
+ using System;
+ using System.Diagnostics;
+ using System.IO;
+ using System.Windows.Forms;
+
+ using HandBrake.ApplicationServices.Exceptions;
+ using HandBrake.ApplicationServices.Model;
+ using HandBrake.ApplicationServices.Services.Encode.EventArgs;
+ using HandBrake.ApplicationServices.Services.Encode.Interfaces;
+ using HandBrake.ApplicationServices.Utilities;
+
+ /// <summary>
+ /// Class which handles the CLI
+ /// </summary>
+ public class EncodeService : EncodeBase, IEncode
+ {
+ #region Private Variables
+
+ /// <summary>
+ /// Gets the Process ID
+ /// </summary>
+ private int processId;
+
+ /// <summary>
+ /// The Start time of the current Encode;
+ /// </summary>
+ private DateTime startTime;
+
+ /// <summary>
+ /// The Current Task
+ /// </summary>
+ private QueueTask currentTask;
+
+ /// <summary>
+ /// The init shutdown.
+ /// </summary>
+ private bool initShutdown;
+
+ #endregion
+
+ #region Properties
+
+ /// <summary>
+ /// Gets or sets The HB Process
+ /// </summary>
+ protected Process HbProcess { get; set; }
+
+ /// <summary>
+ /// Gets a value indicating whether can pause.
+ /// </summary>
+ public bool CanPause
+ {
+ get
+ {
+ return false;
+ }
+ }
+
+ /// <summary>
+ /// Gets a value indicating whether is pasued.
+ /// </summary>
+ public bool IsPasued { get; private set; }
+
+ #endregion
+
+ #region Public Methods
+
+ /// <summary>
+ /// Execute a HandBrakeCLI process.
+ /// This should only be called from the UI thread.
+ /// </summary>
+ /// <param name="encodeQueueTask">
+ /// The encodeQueueTask.
+ /// </param>
+ public void Start(QueueTask encodeQueueTask)
+ {
+ try
+ {
+ if (this.IsEncoding)
+ {
+ throw new GeneralApplicationException("HandBrake is already encodeing.", "Please try again in a minute", null);
+ }
+
+ this.IsEncoding = true;
+ this.currentTask = encodeQueueTask;
+
+ if (encodeQueueTask.Configuration.IsLoggingEnabled)
+ {
+ try
+ {
+ this.SetupLogging(this.currentTask);
+ }
+ catch (Exception)
+ {
+ this.IsEncoding = false;
+ throw;
+ }
+ }
+
+ // Make sure the path exists, attempt to create it if it doesn't
+ this.VerifyEncodeDestinationPath(this.currentTask);
+
+ string handbrakeCLIPath = Path.Combine(Application.StartupPath, "HandBrakeCLI.exe");
+
+ // TODO tidy this code up, it's kinda messy.
+ string query = this.currentTask.Task.IsPreviewEncode
+ ? QueryGeneratorUtility.GeneratePreviewQuery(
+ new EncodeTask(this.currentTask.Task),
+ encodeQueueTask.Configuration,
+ this.currentTask.Task.PreviewEncodeDuration,
+ this.currentTask.Task.PreviewEncodeStartAt)
+ : QueryGeneratorUtility.GenerateQuery(new EncodeTask(this.currentTask.Task), encodeQueueTask.Configuration);
+
+ ProcessStartInfo cliStart = new ProcessStartInfo(handbrakeCLIPath, query)
+ {
+ RedirectStandardOutput = true,
+ RedirectStandardError = encodeQueueTask.Configuration.IsLoggingEnabled,
+ UseShellExecute = false,
+ CreateNoWindow = true
+ };
+
+ this.HbProcess = new Process { StartInfo = cliStart };
+
+ this.HbProcess.Start();
+
+ this.startTime = DateTime.Now;
+
+ if (encodeQueueTask.Configuration.IsLoggingEnabled)
+ {
+ this.HbProcess.ErrorDataReceived += this.HbProcErrorDataReceived;
+ this.HbProcess.BeginErrorReadLine();
+ }
+
+ this.HbProcess.OutputDataReceived += this.HbProcess_OutputDataReceived;
+ this.HbProcess.BeginOutputReadLine();
+
+ this.processId = this.HbProcess.Id;
+
+ // Set the process Priority
+ if (this.processId != -1)
+ {
+ this.HbProcess.EnableRaisingEvents = true;
+ this.HbProcess.Exited += this.HbProcessExited;
+ }
+
+ // Set the Process Priority
+ switch (encodeQueueTask.Configuration.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
+ this.InvokeEncodeStarted(System.EventArgs.Empty);
+ }
+ catch (Exception exc)
+ {
+ encodeQueueTask.Status = QueueItemStatus.Error;
+ this.IsEncoding = false;
+ this.InvokeEncodeCompleted(
+ new EncodeCompletedEventArgs(
+ false, exc, "An Error occured when trying to encode this source. ", this.currentTask.Task.Destination));
+ throw;
+ }
+ }
+
+ /// <summary>
+ /// The pause.
+ /// </summary>
+ /// <exception cref="NotImplementedException">
+ /// This feature is not available for CLI based encoding.
+ /// </exception>
+ public void Pause()
+ {
+ throw new NotImplementedException("This feature is not available for CLI based encoding.");
+ }
+
+ /// <summary>
+ /// The resume.
+ /// </summary>
+ /// <exception cref="NotImplementedException">
+ /// This feature is not available for CLI based encoding.
+ /// </exception>
+ public void Resume()
+ {
+ throw new NotImplementedException("This feature is not available for CLI based encoding.");
+ }
+
+ /// <summary>
+ /// Kill the CLI process
+ /// </summary>
+ public override void Stop()
+ {
+ 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.
+ }
+ }
+
+ /// <summary>
+ /// Shutdown the service.
+ /// </summary>
+ public void Shutdown()
+ {
+ // Nothing to do.
+ }
+
+ #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, System.EventArgs e)
+ {
+ this.HbProcess.WaitForExit();
+
+ try
+ {
+ this.HbProcess.CancelErrorRead();
+ this.HbProcess.CancelOutputRead();
+ this.ShutdownFileWriter();
+ }
+ catch (Exception)
+ {
+ // This exception doesn't warrent user interaction, but it should be logged (TODO)
+ }
+
+ this.currentTask.Status = QueueItemStatus.Completed;
+ this.IsEncoding = false;
+ this.InvokeEncodeCompleted(new EncodeCompletedEventArgs(true, null, string.Empty, this.currentTask.Task.Destination));
+ }
+
+ /// <summary>
+ /// Recieve the Standard Error information and process it
+ /// </summary>
+ /// <param name="sender">
+ /// The Sender Object
+ /// </param>
+ /// <param name="e">
+ /// DataReceived EventArgs
+ /// </param>
+ /// <remarks>
+ /// Worker Thread.
+ /// </remarks>
+ private void HbProcErrorDataReceived(object sender, DataReceivedEventArgs e)
+ {
+ if (!String.IsNullOrEmpty(e.Data))
+ {
+ if (this.initShutdown && this.LogBuffer.Length < 25000000)
+ {
+ this.initShutdown = false; // Reset this flag.
+ }
+
+ if (this.LogBuffer.Length > 25000000 && !this.initShutdown) // Approx 23.8MB and make sure it's only printed once
+ {
+ this.ProcessLogMessage("ERROR: Initiating automatic shutdown of encode process. The size of the log file indicates that there is an error! ");
+ this.initShutdown = true;
+ this.Stop();
+ }
+
+ this.ProcessLogMessage(e.Data);
+ }
+ }
+
+ /// <summary>
+ /// The hb process output data received.
+ /// </summary>
+ /// <param name="sender">
+ /// The sender.
+ /// </param>
+ /// <param name="e">
+ /// The e.
+ /// </param>
+ private void HbProcess_OutputDataReceived(object sender, DataReceivedEventArgs e)
+ {
+ if (!String.IsNullOrEmpty(e.Data) && this.IsEncoding)
+ {
+ EncodeProgressEventArgs eventArgs = this.ReadEncodeStatus(e.Data, this.startTime);
+ if (eventArgs != null)
+ {
+ if (!this.IsEncoding)
+ {
+ // We can get events out of order since the CLI progress is monitored on a background thread.
+ // So make sure we don't send a status update after an encode complete event.
+ return;
+ }
+
+ this.InvokeEncodeStatusChanged(eventArgs);
+ }
+ }
+ }
+
+ #endregion
+ }
+} \ No newline at end of file
diff --git a/win/CS/HandBrake.ApplicationServices/Services/Encode/EventArgs/EncodeCompletedEventArgs.cs b/win/CS/HandBrake.ApplicationServices/Services/Encode/EventArgs/EncodeCompletedEventArgs.cs
new file mode 100644
index 000000000..b77f3097c
--- /dev/null
+++ b/win/CS/HandBrake.ApplicationServices/Services/Encode/EventArgs/EncodeCompletedEventArgs.cs
@@ -0,0 +1,68 @@
+// --------------------------------------------------------------------------------------------------------------------
+// <copyright file="EncodeCompletedEventArgs.cs" company="HandBrake Project (http://handbrake.fr)">
+// This file is part of the HandBrake source code - It may be used under the terms of the GNU General Public License.
+// </copyright>
+// <summary>
+// Encode Progress Event Args
+// </summary>
+// --------------------------------------------------------------------------------------------------------------------
+
+namespace HandBrake.ApplicationServices.Services.Encode.EventArgs
+{
+ using System;
+ using System.Runtime.Serialization;
+
+ /// <summary>
+ /// Encode Progress Event Args
+ /// </summary>
+ [DataContract]
+ public class EncodeCompletedEventArgs : EventArgs
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="EncodeCompletedEventArgs"/> class.
+ /// </summary>
+ /// <param name="sucessful">
+ /// The sucessful.
+ /// </param>
+ /// <param name="exception">
+ /// The exception.
+ /// </param>
+ /// <param name="errorInformation">
+ /// The error information.
+ /// </param>
+ /// <param name="filename">
+ /// The filename.
+ /// </param>
+ public EncodeCompletedEventArgs(bool sucessful, Exception exception, string errorInformation, string filename)
+ {
+ this.Successful = sucessful;
+ this.Exception = exception;
+ this.ErrorInformation = errorInformation;
+ this.FileName = filename;
+ }
+
+ /// <summary>
+ /// Gets or sets the file name.
+ /// </summary>
+ [DataMember]
+ public string FileName { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether Successful.
+ /// </summary>
+ [DataMember]
+ public bool Successful { get; set; }
+
+ /// <summary>
+ /// Gets or sets Exception.
+ /// </summary>
+ [DataMember]
+ public Exception Exception { get; set; }
+
+ /// <summary>
+ /// Gets or sets ErrorInformation.
+ /// </summary>
+ [DataMember]
+ public string ErrorInformation { get; set; }
+ }
+}
diff --git a/win/CS/HandBrake.ApplicationServices/Services/Encode/EventArgs/EncodeProgressEventArgs.cs b/win/CS/HandBrake.ApplicationServices/Services/Encode/EventArgs/EncodeProgressEventArgs.cs
new file mode 100644
index 000000000..1b93434da
--- /dev/null
+++ b/win/CS/HandBrake.ApplicationServices/Services/Encode/EventArgs/EncodeProgressEventArgs.cs
@@ -0,0 +1,63 @@
+// --------------------------------------------------------------------------------------------------------------------
+// <copyright file="EncodeProgressEventArgs.cs" company="HandBrake Project (http://handbrake.fr)">
+// This file is part of the HandBrake source code - It may be used under the terms of the GNU General Public License.
+// </copyright>
+// <summary>
+// Encode Progress Event Args
+// </summary>
+// --------------------------------------------------------------------------------------------------------------------
+
+namespace HandBrake.ApplicationServices.Services.Encode.EventArgs
+{
+ using System;
+ using System.Runtime.Serialization;
+
+ /// <summary>
+ /// Encode Progress Event Args
+ /// </summary>
+ [DataContract]
+ public class EncodeProgressEventArgs : EventArgs
+ {
+ /// <summary>
+ /// Gets or sets PercentComplete.
+ /// </summary>
+ [DataMember]
+ public float PercentComplete { get; set; }
+
+ /// <summary>
+ /// Gets or sets CurrentFrameRate.
+ /// </summary>
+ [DataMember]
+ public float CurrentFrameRate { get; set; }
+
+ /// <summary>
+ /// Gets or sets AverageFrameRate.
+ /// </summary>
+ [DataMember]
+ public float AverageFrameRate { get; set; }
+
+ /// <summary>
+ /// Gets or sets EstimatedTimeLeft.
+ /// </summary>
+ [DataMember]
+ public TimeSpan EstimatedTimeLeft { get; set; }
+
+ /// <summary>
+ /// Gets or sets Task.
+ /// </summary>
+ [DataMember]
+ public int Task { get; set; }
+
+ /// <summary>
+ /// Gets or sets TaskCount.
+ /// </summary>
+ [DataMember]
+ public int TaskCount { get; set; }
+
+ /// <summary>
+ /// Gets or sets ElapsedTime.
+ /// </summary>
+ [DataMember]
+ public TimeSpan ElapsedTime { get; set; }
+ }
+}
diff --git a/win/CS/HandBrake.ApplicationServices/Services/Encode/Interfaces/IEncode.cs b/win/CS/HandBrake.ApplicationServices/Services/Encode/Interfaces/IEncode.cs
new file mode 100644
index 000000000..90c42149b
--- /dev/null
+++ b/win/CS/HandBrake.ApplicationServices/Services/Encode/Interfaces/IEncode.cs
@@ -0,0 +1,123 @@
+// --------------------------------------------------------------------------------------------------------------------
+// <copyright file="IEncode.cs" company="HandBrake Project (http://handbrake.fr)">
+// This file is part of the HandBrake source code - It may be used under the terms of the GNU General Public License.
+// </copyright>
+// <summary>
+// Encode Progess Status
+// </summary>
+// --------------------------------------------------------------------------------------------------------------------
+
+namespace HandBrake.ApplicationServices.Services.Encode.Interfaces
+{
+ using System;
+
+ using HandBrake.ApplicationServices.Model;
+ using HandBrake.ApplicationServices.Services.Encode.EventArgs;
+
+ /// <summary>
+ /// Encode Progess Status
+ /// </summary>
+ /// <param name="sender">
+ /// The sender.
+ /// </param>
+ /// <param name="e">
+ /// The EncodeProgressEventArgs.
+ /// </param>
+ public delegate void EncodeProgessStatus(object sender, EncodeProgressEventArgs e);
+
+ /// <summary>
+ /// Encode Progess Status
+ /// </summary>
+ /// <param name="sender">
+ /// The sender.
+ /// </param>
+ /// <param name="e">
+ /// The EncodeProgressEventArgs.
+ /// </param>
+ public delegate void EncodeCompletedStatus(object sender, EncodeCompletedEventArgs e);
+
+ /// <summary>
+ /// The IEncode Interface
+ /// </summary>
+ public interface IEncode
+ {
+ /// <summary>
+ /// Fires when a new CLI Job starts
+ /// </summary>
+ event EventHandler EncodeStarted;
+
+ /// <summary>
+ /// Fires when a CLI job finishes.
+ /// </summary>
+ event EncodeCompletedStatus EncodeCompleted;
+
+ /// <summary>
+ /// Encode process has progressed
+ /// </summary>
+ event EncodeProgessStatus EncodeStatusChanged;
+
+ /// <summary>
+ /// Gets a value indicating whether IsEncoding.
+ /// </summary>
+ bool IsEncoding { get; }
+
+ /// <summary>
+ /// Gets ActivityLog.
+ /// </summary>
+ string ActivityLog { get; }
+
+ /// <summary>
+ /// Gets the log index. The current log row counter.
+ /// </summary>
+ int LogIndex { get; }
+
+ /// <summary>
+ /// Gets a value indicating whether can pause.
+ /// </summary>
+ bool CanPause { get; }
+
+ /// <summary>
+ /// Gets a value indicating whether is pasued.
+ /// </summary>
+ bool IsPasued { get; }
+
+ /// <summary>
+ /// Start with a LibHb EncodeJob Object
+ /// </summary>
+ /// <param name="job">
+ /// The job.
+ /// </param>
+ void Start(QueueTask job);
+
+ /// <summary>
+ /// The pause.
+ /// </summary>
+ void Pause();
+
+ /// <summary>
+ /// The resume.
+ /// </summary>
+ void Resume();
+
+ /// <summary>
+ /// Kill the CLI process
+ /// </summary>
+ void Stop();
+
+ /// <summary>
+ /// Copy the log file to the desired destinations
+ /// </summary>
+ /// <param name="destination">
+ /// The destination.
+ /// </param>
+ /// <param name="configuration">
+ /// The configuration.
+ /// </param>
+ void ProcessLogs(string destination, HBConfiguration configuration);
+
+ /// <summary>
+ /// Shutdown the service.
+ /// </summary>
+ void Shutdown();
+ }
+} \ No newline at end of file
diff --git a/win/CS/HandBrake.ApplicationServices/Services/Encode/Interfaces/IEncodeServiceWrapper.cs b/win/CS/HandBrake.ApplicationServices/Services/Encode/Interfaces/IEncodeServiceWrapper.cs
new file mode 100644
index 000000000..6163bcffc
--- /dev/null
+++ b/win/CS/HandBrake.ApplicationServices/Services/Encode/Interfaces/IEncodeServiceWrapper.cs
@@ -0,0 +1,18 @@
+// --------------------------------------------------------------------------------------------------------------------
+// <copyright file="IEncodeServiceWrapper.cs" company="HandBrake Project (http://handbrake.fr)">
+// This file is part of the HandBrake source code - It may be used under the terms of the GNU General Public License.
+// </copyright>
+// <summary>
+// IEncodeServiceWrapper Interface
+// </summary>
+// --------------------------------------------------------------------------------------------------------------------
+
+namespace HandBrake.ApplicationServices.Services.Encode.Interfaces
+{
+ /// <summary>
+ /// EncodeServiceWrapper Interface
+ /// </summary>
+ public interface IEncodeServiceWrapper : IEncode
+ {
+ }
+}
diff --git a/win/CS/HandBrake.ApplicationServices/Services/Encode/LibEncode.cs b/win/CS/HandBrake.ApplicationServices/Services/Encode/LibEncode.cs
new file mode 100644
index 000000000..8a046e0af
--- /dev/null
+++ b/win/CS/HandBrake.ApplicationServices/Services/Encode/LibEncode.cs
@@ -0,0 +1,326 @@
+// --------------------------------------------------------------------------------------------------------------------
+// <copyright file="LibEncode.cs" company="HandBrake Project (http://handbrake.fr)">
+// This file is part of the HandBrake source code - It may be used under the terms of the GNU General Public License.
+// </copyright>
+// <summary>
+// LibHB Implementation of IEncode
+// </summary>
+// --------------------------------------------------------------------------------------------------------------------
+
+namespace HandBrake.ApplicationServices.Services.Encode
+{
+ using System;
+ using System.Diagnostics;
+
+ using HandBrake.ApplicationServices.Model;
+ using HandBrake.ApplicationServices.Services.Encode.Interfaces;
+ using HandBrake.ApplicationServices.Utilities;
+ using HandBrake.Interop;
+ using HandBrake.Interop.EventArgs;
+ using HandBrake.Interop.Interfaces;
+ using HandBrake.Interop.Model;
+
+ using EncodeCompletedEventArgs = HandBrake.ApplicationServices.Services.Encode.EventArgs.EncodeCompletedEventArgs;
+ using EncodeProgressEventArgs = HandBrake.ApplicationServices.Services.Encode.EventArgs.EncodeProgressEventArgs;
+
+ /// <summary>
+ /// LibHB Implementation of IEncode
+ /// </summary>
+ public class LibEncode : EncodeBase, IEncode
+ {
+ #region Private Variables
+
+ /// <summary>
+ /// Lock for the log file
+ /// </summary>
+ private static readonly object LogLock = new object();
+
+ /// <summary>
+ /// The instance.
+ /// </summary>
+ private IHandBrakeInstance instance;
+
+ /// <summary>
+ /// The Start time of the current Encode;
+ /// </summary>
+ private DateTime startTime;
+
+ /// <summary>
+ /// A flag to indicate if logging is enabled or not.
+ /// </summary>
+ private bool loggingEnabled;
+
+ /// <summary>
+ /// The Current Task
+ /// </summary>
+ private QueueTask currentTask;
+
+ #endregion
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="LibEncode"/> class.
+ /// </summary>
+ public LibEncode()
+ {
+ HandBrakeUtils.MessageLogged += this.HandBrakeInstanceMessageLogged;
+ HandBrakeUtils.ErrorLogged += this.HandBrakeInstanceErrorLogged;
+ }
+
+ /// <summary>
+ /// Gets a value indicating whether can pause.
+ /// </summary>
+ public bool CanPause
+ {
+ get
+ {
+ return true;
+ }
+ }
+
+ /// <summary>
+ /// Gets a value indicating whether is pasued.
+ /// </summary>
+ public bool IsPasued { get; private set; }
+
+ /// <summary>
+ /// Start with a LibHb EncodeJob Object
+ /// </summary>
+ /// <param name="job">
+ /// The job.
+ /// </param>
+ public void Start(QueueTask job)
+ {
+ // Setup
+ this.startTime = DateTime.Now;
+ this.loggingEnabled = job.Configuration.IsLoggingEnabled;
+ this.currentTask = job;
+
+ // Create a new HandBrake instance
+ // Setup the HandBrake Instance
+ this.instance = new HandBrakeInstance();
+ this.instance.Initialize(1);
+ this.instance.EncodeCompleted += this.InstanceEncodeCompleted;
+ this.instance.EncodeProgress += this.InstanceEncodeProgress;
+
+ try
+ {
+ // Sanity Checking and Setup
+ if (this.IsEncoding)
+ {
+ throw new Exception("HandBrake is already encoding.");
+ }
+
+ this.IsEncoding = true;
+
+ // Enable logging if required.
+ if (job.Configuration.IsLoggingEnabled)
+ {
+ try
+ {
+ this.SetupLogging(job);
+ }
+ catch (Exception)
+ {
+ this.IsEncoding = false;
+ throw;
+ }
+ }
+
+ // Verify the Destination Path Exists, and if not, create it.
+ this.VerifyEncodeDestinationPath(job);
+
+ // We have to scan the source again but only the title so the HandBrake instance is initialised correctly.
+ // Since the UI sends the crop params down, we don't have to do all the previews.
+ this.instance.StartScan(job.Task.Source, job.Configuration.PreviewScanCount, job.Task.Title);
+
+ this.instance.ScanCompleted += delegate
+ {
+ this.ScanCompleted(job, this.instance);
+ };
+ }
+ catch (Exception exc)
+ {
+ this.InvokeEncodeCompleted(new EncodeCompletedEventArgs(false, exc, "An Error has occured.", this.currentTask.Task.Destination));
+ }
+ }
+
+ /// <summary>
+ /// Pause the currently running encode.
+ /// </summary>
+ public void Pause()
+ {
+ if (this.instance != null)
+ {
+ this.instance.PauseEncode();
+ this.IsPasued = true;
+ }
+ }
+
+ /// <summary>
+ /// Resume the currently running encode.
+ /// </summary>
+ public void Resume()
+ {
+ if (this.instance != null)
+ {
+ this.instance.ResumeEncode();
+ this.IsPasued = false;
+ }
+ }
+
+ /// <summary>
+ /// Kill the CLI process
+ /// </summary>
+ public override void Stop()
+ {
+ try
+ {
+ this.IsEncoding = false;
+ this.instance.StopEncode();
+ }
+ catch (Exception)
+ {
+ // Do Nothing.
+ }
+ }
+
+ /// <summary>
+ /// Shutdown the service.
+ /// </summary>
+ public void Shutdown()
+ {
+ // Nothing to do for this implementation.
+ }
+
+ /// <summary>
+ /// The scan completed.
+ /// </summary>
+ /// <param name="job">
+ /// The job.
+ /// </param>
+ /// <param name="instance">
+ /// The instance.
+ /// </param>
+ private void ScanCompleted(QueueTask job, IHandBrakeInstance instance)
+ {
+ // Get an EncodeJob object for the Interop Library
+ EncodeJob encodeJob = InteropModelCreator.GetEncodeJob(job);
+
+ // Start the Encode
+ instance.StartEncode(encodeJob, job.Configuration.PreviewScanCount);
+
+ // Fire the Encode Started Event
+ this.InvokeEncodeStarted(System.EventArgs.Empty);
+
+ // Set the Process Priority
+ switch (job.Configuration.ProcessPriority)
+ {
+ case "Realtime":
+ Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.RealTime;
+ break;
+ case "High":
+ Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.High;
+ break;
+ case "Above Normal":
+ Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.AboveNormal;
+ break;
+ case "Normal":
+ Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.Normal;
+ break;
+ case "Low":
+ Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.Idle;
+ break;
+ default:
+ Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.BelowNormal;
+ break;
+ }
+ }
+
+ #region HandBrakeInstance Event Handlers.
+ /// <summary>
+ /// Log a message
+ /// </summary>
+ /// <param name="sender">
+ /// The sender.
+ /// </param>
+ /// <param name="e">
+ /// The MessageLoggedEventArgs.
+ /// </param>
+ private void HandBrakeInstanceErrorLogged(object sender, MessageLoggedEventArgs e)
+ {
+ if (this.loggingEnabled)
+ {
+ lock (LogLock)
+ {
+ this.ProcessLogMessage(e.Message);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Log a message
+ /// </summary>
+ /// <param name="sender">
+ /// The sender.
+ /// </param>
+ /// <param name="e">
+ /// The MessageLoggedEventArgs.
+ /// </param>
+ private void HandBrakeInstanceMessageLogged(object sender, MessageLoggedEventArgs e)
+ {
+ if (this.loggingEnabled)
+ {
+ lock (LogLock)
+ {
+ this.ProcessLogMessage(e.Message);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Encode Progress Event Handler
+ /// </summary>
+ /// <param name="sender">
+ /// The sender.
+ /// </param>
+ /// <param name="e">
+ /// The Interop.EncodeProgressEventArgs.
+ /// </param>
+ private void InstanceEncodeProgress(object sender, Interop.EventArgs.EncodeProgressEventArgs e)
+ {
+ EncodeProgressEventArgs args = new EncodeProgressEventArgs
+ {
+ AverageFrameRate = e.AverageFrameRate,
+ CurrentFrameRate = e.CurrentFrameRate,
+ EstimatedTimeLeft = e.EstimatedTimeLeft,
+ PercentComplete = e.FractionComplete * 100,
+ Task = e.Pass,
+ ElapsedTime = DateTime.Now - this.startTime,
+ };
+
+ this.InvokeEncodeStatusChanged(args);
+ }
+
+ /// <summary>
+ /// Encode Completed Event Handler
+ /// </summary>
+ /// <param name="sender">
+ /// The sender.
+ /// </param>
+ /// <param name="e">
+ /// The e.
+ /// </param>
+ private void InstanceEncodeCompleted(object sender, Interop.EventArgs.EncodeCompletedEventArgs e)
+ {
+ this.IsEncoding = false;
+
+ this.InvokeEncodeCompleted(
+ e.Error
+ ? new EncodeCompletedEventArgs(false, null, string.Empty, this.currentTask.Task.Destination)
+ : new EncodeCompletedEventArgs(true, null, string.Empty, this.currentTask.Task.Destination));
+
+ this.ShutdownFileWriter();
+ }
+ #endregion
+ }
+}