/* 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.Diagnostics;
using System.IO;
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.Base;
using HandBrake.ApplicationServices.Services.Interfaces;
///
/// Class which handles the CLI
///
public class Encode : EncodeBase, IEncode
{
#region Private Variables
///
/// The User Setting Service
///
private IUserSettingService userSettingService = ServiceManager.UserSettingService;
///
/// Gets The Process Handle
///
private IntPtr processHandle;
///
/// Gets the Process ID
///
private int processId;
///
/// The Start time of the current Encode;
///
private DateTime startTime;
///
/// The Current Task
///
private QueueTask currentTask;
#endregion
///
/// Initializes a new instance of the class.
///
public Encode()
{
this.EncodeStarted += this.EncodeEncodeStarted;
GrowlCommunicator.Register();
}
#region Properties
///
/// Gets or sets The HB Process
///
protected Process HbProcess { get; set; }
#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
{
this.currentTask = encodeQueueTask;
if (this.IsEncoding)
{
throw new Exception("HandBrake is already encodeing.");
}
this.IsEncoding = true;
if (enableLogging)
{
try
{
this.SetupLogging(currentTask);
}
catch (Exception)
{
this.IsEncoding = false;
throw;
}
}
if (this.userSettingService.GetUserSetting(UserSettingConstants.PreventSleep))
{
Win32.PreventSleep();
}
// Make sure the path exists, attempt to create it if it doesn't
this.VerifyEncodeDestinationPath(currentTask);
string handbrakeCLIPath = Path.Combine(Application.StartupPath, "HandBrakeCLI.exe");
ProcessStartInfo cliStart = new ProcessStartInfo(handbrakeCLIPath, currentTask.Query)
{
RedirectStandardOutput = true,
RedirectStandardError = enableLogging ? true : false,
UseShellExecute = false,
CreateNoWindow = !this.userSettingService.GetUserSetting(UserSettingConstants.ShowCLI) ? true : false
};
this.HbProcess = new Process { StartInfo = cliStart };
this.HbProcess.Start();
this.startTime = DateTime.Now;
if (enableLogging)
{
this.HbProcess.ErrorDataReceived += this.HbProcErrorDataReceived;
this.HbProcess.BeginErrorReadLine();
}
this.processId = this.HbProcess.Id;
this.processHandle = this.HbProcess.Handle;
// Set the process Priority
if (this.processId != -1)
{
this.HbProcess.EnableRaisingEvents = true;
this.HbProcess.Exited += this.HbProcessExited;
}
// Set the Process Priority
switch (this.userSettingService.GetUserSetting(UserSettingConstants.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.Invoke_encodeStarted(EventArgs.Empty);
}
catch (Exception exc)
{
this.Invoke_encodeCompleted(
new EncodeCompletedEventArgs(
false, exc, "An Error occured when trying to encode this source. "));
}
}
///
/// 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 override 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.
}
this.Invoke_encodeCompleted(
exc == null
? new EncodeCompletedEventArgs(true, null, string.Empty)
: new EncodeCompletedEventArgs(false, exc, "An Unknown Error has occured when trying to Stop this encode."));
}
///
/// 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");
//}*/
}
#endregion
#region Private Helper Methods
///
/// The HandBrakeCLI process has exited.
///
///
/// The sender.
///
///
/// The EventArgs.
///
private void HbProcessExited(object sender, EventArgs e)
{
this.IsEncoding = false;
if (this.WindowsSeven.IsWindowsSeven)
{
this.WindowsSeven.SetTaskBarProgressToNoProgress();
}
if (this.userSettingService.GetUserSetting(UserSettingConstants.PreventSleep))
{
Win32.AllowSleep();
}
try
{
// 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();
this.ShutdownFileWriter();
}
catch (Exception exc)
{
// This exception doesn't warrent user interaction, but it should be logged (TODO)
}
this.currentTask.Status = QueueItemStatus.Completed;
this.Invoke_encodeCompleted(new EncodeCompletedEventArgs(true, null, string.Empty));
}
///
/// 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))
{
this.ProcessLogMessage(e.Data);
}
}
///
/// Encode Started
///
///
/// The sender.
///
///
/// The EventArgs.
///
private void EncodeEncodeStarted(object sender, EventArgs e)
{
Thread monitor = new Thread(this.EncodeMonitor);
monitor.Start();
}
///
/// Monitor the QueueTask
///
private void EncodeMonitor()
{
try
{
Parser encode = new Parser(this.HbProcess.StandardOutput.BaseStream);
encode.OnEncodeProgress += this.EncodeOnEncodeProgress;
while (!encode.EndOfStream)
{
encode.ReadEncodeStatus();
}
}
catch (Exception)
{
this.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,
ElapsedTime = DateTime.Now - this.startTime,
};
this.Invoke_encodeStatusChanged(eventArgs);
if (this.WindowsSeven.IsWindowsSeven)
{
int percent;
int.TryParse(Math.Round(percentComplete).ToString(), out percent);
this.WindowsSeven.SetTaskBarProgress(percent);
}
}
#endregion
}
}