// -------------------------------------------------------------------------------------------------------------------- // // This file is part of the HandBrake source code - It may be used under the terms of the GNU General Public License. // // // LibHB Implementation of IEncode // // -------------------------------------------------------------------------------------------------------------------- namespace HandBrake.ApplicationServices.Services.Encode { using System; using System.Diagnostics; using System.Linq; using HandBrake.ApplicationServices.Model; using HandBrake.ApplicationServices.Services.Encode.Interfaces; using HandBrake.ApplicationServices.Services.Scan; using HandBrake.ApplicationServices.Services.Scan.Model; using HandBrake.ApplicationServices.Utilities; using HandBrake.Interop; using HandBrake.Interop.EventArgs; using HandBrake.Interop.Interfaces; using HandBrake.Interop.Model; /// /// LibHB Implementation of IEncode /// public class LibEncode : EncodeBase, IEncode { #region Private Variables /// /// Lock for the log file /// private static readonly object LogLock = new object(); /// /// The instance. /// private IHandBrakeInstance instance; /// /// The Start time of the current Encode; /// private DateTime startTime; /// /// The Current Task /// private QueueTask currentTask; /// /// A local instance of the scanned source. /// private Source scannedSource; #endregion /// /// Initializes a new instance of the class. /// public LibEncode() { HandBrakeUtils.MessageLogged += this.HandBrakeInstanceMessageLogged; HandBrakeUtils.ErrorLogged += this.HandBrakeInstanceErrorLogged; } /// /// Gets a value indicating whether can pause. /// public bool CanPause { get { return true; } } /// /// Gets a value indicating whether is pasued. /// public bool IsPasued { get; private set; } /// /// Start with a LibHb EncodeJob Object /// /// /// The job. /// public void Start(QueueTask job) { // Setup this.startTime = DateTime.Now; this.currentTask = job; // Create a new HandBrake instance // Setup the HandBrake Instance this.instance = HandBrakeInstanceManager.GetEncodeInstance(job.Configuration.Verbosity); 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. try { this.SetupLogging(job, true); } 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.ScanCompleted += delegate { // Process into internal structures. this.scannedSource = new Source { Titles = LibScan.ConvertTitles(this.instance.Titles, this.instance.FeatureTitle) }; // TODO work around the bad Internal API. this.ScanCompleted(job, this.instance); }; HandBrakeUtils.SetDvdNav(!job.Configuration.IsDvdNavDisabled); ServiceLogMessage("Scanning title for encoding ... "); this.instance.StartScan(job.ScannedSource.ScanPath, job.Configuration.PreviewScanCount, job.Task.Title); } catch (Exception exc) { ServiceLogMessage("Scan Failed ... " + Environment.NewLine + exc); this.InvokeEncodeCompleted(new EventArgs.EncodeCompletedEventArgs(false, exc, "An Error has occured.", this.currentTask.Task.Destination)); } } /// /// Pause the currently running encode. /// public void Pause() { if (this.instance != null) { this.instance.PauseEncode(); ServiceLogMessage("Encode Paused"); this.IsPasued = true; } } /// /// Resume the currently running encode. /// public void Resume() { if (this.instance != null) { this.instance.ResumeEncode(); ServiceLogMessage("Encode Resumed"); this.IsPasued = false; } } /// /// Kill the CLI process /// public override void Stop() { try { this.IsEncoding = false; this.instance.StopEncode(); ServiceLogMessage("Encode Stopped"); } catch (Exception) { // Do Nothing. } } /// /// Shutdown the service. /// public void Shutdown() { // Nothing to do for this implementation. } /// /// The scan completed. /// /// /// The job. /// /// /// The instance. /// private void ScanCompleted(QueueTask job, IHandBrakeInstance instance) { ServiceLogMessage("Scan Completed. Setting up the job for encoding ..."); // Get an EncodeJob object for the Interop Library EncodeJob encodeJob = InteropModelCreator.GetEncodeJob(job); // Start the Encode Title title = this.scannedSource.Titles.FirstOrDefault(t => t.TitleNumber == job.Task.Title); if (title == null) { ServiceLogMessage("Title not found."); throw new Exception("Unable to get title for encoding. Encode Failed."); } Interop.Model.Scan.Title scannedTitle = new Interop.Model.Scan.Title { Resolution = new Size(title.Resolution.Width, title.Resolution.Height), ParVal = new Size(title.ParVal.Width, title.ParVal.Height), FramerateDenominator = title.FramerateDenominator, FramerateNumerator = title.FramerateNumerator, }; // TODO fix this tempory hack to pass in the required title information into the factory. try { ServiceLogMessage("Starting Encode ..."); instance.StartEncode(encodeJob, scannedTitle, job.Configuration.PreviewScanCount); } catch (Exception exc) { ServiceLogMessage("Failed to start encoding ..." + Environment.NewLine + exc); this.InvokeEncodeCompleted(new EventArgs.EncodeCompletedEventArgs(false, exc, "Unable to start encoding", job.Task.Source)); } // 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. /// /// Log a message /// /// /// The sender. /// /// /// The MessageLoggedEventArgs. /// private void HandBrakeInstanceErrorLogged(object sender, MessageLoggedEventArgs e) { lock (LogLock) { this.ProcessLogMessage(e.Message); } } /// /// Log a message /// /// /// The sender. /// /// /// The MessageLoggedEventArgs. /// private void HandBrakeInstanceMessageLogged(object sender, MessageLoggedEventArgs e) { lock (LogLock) { this.ProcessLogMessage(e.Message); } } /// /// Encode Progress Event Handler /// /// /// The sender. /// /// /// The Interop.EncodeProgressEventArgs. /// private void InstanceEncodeProgress(object sender, EncodeProgressEventArgs e) { EventArgs.EncodeProgressEventArgs args = new EventArgs.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); } /// /// Encode Completed Event Handler /// /// /// The sender. /// /// /// The e. /// private void InstanceEncodeCompleted(object sender, EncodeCompletedEventArgs e) { this.IsEncoding = false; ServiceLogMessage("Encode Completed ..."); this.InvokeEncodeCompleted( e.Error ? new EventArgs.EncodeCompletedEventArgs(false, null, string.Empty, this.currentTask.Task.Destination) : new EventArgs.EncodeCompletedEventArgs(true, null, string.Empty, this.currentTask.Task.Destination)); this.ShutdownFileWriter(); } #endregion } }