/* 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.Text;
using System.Threading;
using System.Windows.Forms;
using HandBrake.ApplicationServices.Functions;
using HandBrake.ApplicationServices.Model;
using HandBrake.ApplicationServices.Parsing;
using HandBrake.ApplicationServices.Properties;
using HandBrake.ApplicationServices.Services.Interfaces;
///
/// 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();
#endregion
/* Constructor */
///
/// Initializes a new instance of the class.
///
public Encode()
{
this.EncodeStarted += Encode_EncodeStarted;
}
#region Delegates and Event Handlers
///
/// Encode Progess Status
///
///
/// The sender.
///
///
/// The EncodeProgressEventArgs.
///
public delegate void EncodeProgessStatus(object sender, EncodeProgressEventArgs e);
/* Event Handlers */
///
/// Fires when a new CLI Job starts
///
public event EventHandler EncodeStarted;
///
/// Fires when a CLI job finishes.
///
public event EventHandler EncodeEnded;
///
/// Encode process has progressed
///
public event EncodeProgessStatus EncodeStatusChanged;
#endregion
/* 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)
{
ReadFile(); // Read the last log file back in if it exists
}
return string.IsNullOrEmpty(this.logBuffer.ToString()) ? "No log data available..." : this.logBuffer.ToString();
}
}
/* Public Methods */
///
/// Create a preview sample video
///
///
/// The CLI Query
///
public void CreatePreviewSample(string query)
{
this.Run(new Job { Query = query }, false);
}
///
/// Execute a HandBrakeCLI process.
///
///
/// The enc Job.
///
///
/// Enable Logging. When Disabled we onlt parse Standard Ouput for progress info. Standard Error log data is ignored.
///
protected void Run(Job encJob, bool enableLogging)
{
try
{
IsEncoding = true;
if (enableLogging)
SetupLogging(encJob);
if (Settings.Default.preventSleep)
{
Win32.PreventSleep();
}
string handbrakeCLIPath = Path.Combine(Application.StartupPath, "HandBrakeCLI.exe");
ProcessStartInfo cliStart = new ProcessStartInfo(handbrakeCLIPath, encJob.Query)
{
RedirectStandardOutput = true,
RedirectStandardError = enableLogging ? true : false,
UseShellExecute = false,
CreateNoWindow = !Settings.Default.showCliForInGuiEncodeStatus ? true : false
};
this.HbProcess = Process.Start(cliStart);
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 += HbProcess_Exited;
}
// Set the Process Priority
switch (Settings.Default.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)
{
Main.ShowExceptiowWindow("It would appear that HandBrakeCLI has not started correctly." +
"You should take a look at the Activity log as it may indicate the reason why.\n\nDetailed Error Information: error occured in runCli()",
exc.ToString());
}
}
///
/// Kill the CLI process
///
public void Stop()
{
try
{
if (this.HbProcess != null) this.HbProcess.Kill();
}
catch (Exception exc)
{
Main.ShowExceptiowWindow("Unable to stop HandBrakeCLI. It may not be running.", exc.ToString());
}
if (this.EncodeEnded != null)
this.EncodeEnded(this, new EventArgs());
}
///
/// 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 SafelyClose()
{
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");
//}
}
/* Helpers */
///
/// 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
///
protected void CopyLog(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 (Settings.Default.saveLogWithVideo)
File.Copy(tempLogFile, Path.Combine(encodeDestinationPath, encodeLogFile));
// Save a copy of the log file to a user specified location
if (Directory.Exists(Settings.Default.saveLogPath))
if (Settings.Default.saveLogPath != String.Empty && Settings.Default.saveLogToSpecifiedPath)
File.Copy(tempLogFile, Path.Combine(Settings.Default.saveLogPath, encodeLogFile));
}
catch (Exception exc)
{
Main.ShowExceptiowWindow("Unable to make a copy of the log file", exc.ToString());
}
}
///
/// The HandBrakeCLI process has exited.
///
///
/// The sender.
///
///
/// The EventArgs.
///
private void HbProcess_Exited(object sender, EventArgs e)
{
IsEncoding = false;
// ReadFile(null);
if (this.EncodeEnded != null)
this.EncodeEnded(this, new EventArgs());
if (windowsSeven.IsWindowsSeven)
{
windowsSeven.SetTaskBarProgressToNoProgress();
}
if (Properties.Settings.Default.preventSleep)
{
Win32.AllowSleep();
}
try
{
if (fileWriter != null)
fileWriter.Close();
}
catch (Exception exc)
{
Main.ShowExceptiowWindow("Unable to close the log file wrtier", exc.ToString());
}
}
///
/// Read the log file
///
///
/// The object.
///
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
{
// Make sure the application readable log file does not already exist. FileCopy fill fail if it does.
if (File.Exists(logFile2))
File.Delete(logFile2);
// 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
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();
sr.Dispose();
}
catch (Exception exc)
{
Main.ShowExceptiowWindow("Unable to read log file", exc.ToString());
}
}
}
///
/// Setup the logging.
///
private void SetupLogging(Job encodeJob)
{
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(Logging.CreateCliLogHeader(encodeJob));
logBuffer.AppendLine(Logging.CreateCliLogHeader(encodeJob));
}
catch (Exception exc)
{
if (fileWriter != null)
fileWriter.Close();
Main.ShowExceptiowWindow("Error", exc.ToString());
}
}
///
/// 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))
{
lock (logBuffer)
logBuffer.AppendLine(e.Data);
try
{
if (fileWriter != null)
fileWriter.WriteLine(e.Data);
}
catch (Exception exc)
{
Main.ShowExceptiowWindow("Unable to write log data...", exc.ToString());
}
}
}
#region Encode Status from Standard Output
///
/// Encode Started
///
///
/// The sender.
///
///
/// The EventArgs.
///
private void Encode_EncodeStarted(object sender, EventArgs e)
{
Thread monitor = new Thread(EncodeMonitor);
monitor.Start();
}
///
/// Monitor the Job
///
private void EncodeMonitor()
{
try
{
Parser encode = new Parser(HbProcess.StandardOutput.BaseStream);
encode.OnEncodeProgress += EncodeOnEncodeProgress;
while (!encode.EndOfStream)
encode.ReadEncodeStatus();
}
catch (Exception exc)
{
Main.ShowExceptiowWindow("An Unknown Error has occured", exc.ToString());
}
}
///
/// 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, TimeSpan timeRemaining)
{
EncodeProgressEventArgs eventArgs = new EncodeProgressEventArgs
{
AverageFrameRate = avg,
CurrentFrameRate = currentFps,
EstimatedTimeLeft = 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
}
}