// -------------------------------------------------------------------------------------------------------------------- // // This file is part of the HandBrake source code - It may be used under the terms of the GNU General Public License. // // // Defines the HandBrakeUtils type. // // -------------------------------------------------------------------------------------------------------------------- using System.Runtime.InteropServices; namespace HandBrake.Interop { using System; using System.Collections.Generic; using HandBrake.Interop.HbLib; using HandBrake.Interop.Model; using HandBrake.Interop.Model.Encoding; using HandBrake.Interop.SourceData; /// /// HandBrake Interop Utilities /// public static class HandBrakeUtils { /// /// Estimated overhead in bytes for each frame in output container. /// internal const int ContainerOverheadPerFrame = 6; /// /// The callback for log messages from HandBrake. /// private static LoggingCallback loggingCallback; /// /// The callback for error messages from HandBrake. /// private static LoggingCallback errorCallback; /// /// Fires when HandBrake has logged a message. /// public static event EventHandler MessageLogged; /// /// Fires when HandBrake has logged an error. /// public static event EventHandler ErrorLogged; /// /// Register the logger. /// public static void RegisterLogger() { // Register the logger if we have not already if (loggingCallback == null) { // Keep the callback as a member to prevent it from being garbage collected. loggingCallback = new LoggingCallback(LoggingHandler); errorCallback = new LoggingCallback(ErrorHandler); HBFunctions.hb_register_logger(loggingCallback); HBFunctions.hb_register_error_handler(errorCallback); } } /// /// Handles log messages from HandBrake. /// /// The log message (including newline). public static void LoggingHandler(string message) { if (!string.IsNullOrEmpty(message)) { string[] messageParts = message.Split(new string[] { "\n" }, StringSplitOptions.RemoveEmptyEntries); if (messageParts.Length > 0) { if (MessageLogged != null) { MessageLogged(null, new MessageLoggedEventArgs { Message = messageParts[0] }); } System.Diagnostics.Debug.WriteLine(messageParts[0]); } } } /// /// Handles errors from HandBrake. /// /// The error message. public static void ErrorHandler(string message) { if (!string.IsNullOrEmpty(message)) { // This error happens in normal operations. Log it as a message. if (message == "dvd: ifoOpen failed") { if (MessageLogged != null) { MessageLogged(null, new MessageLoggedEventArgs { Message = message }); } return; } if (ErrorLogged != null) { ErrorLogged(null, new MessageLoggedEventArgs { Message = message }); } System.Diagnostics.Debug.WriteLine("ERROR: " + message); } } /// /// Gets the total number of seconds on the given encode job. /// /// The encode job to query. /// The title being encoded. /// The total number of seconds of video to encode. internal static double GetJobLengthSeconds(EncodeJob job, Title title) { switch (job.RangeType) { case VideoRangeType.Chapters: TimeSpan duration = TimeSpan.Zero; for (int i = job.ChapterStart; i <= job.ChapterEnd; i++) { duration += title.Chapters[i - 1].Duration; } return duration.TotalSeconds; case VideoRangeType.Seconds: return job.SecondsEnd - job.SecondsStart; case VideoRangeType.Frames: return (job.FramesEnd - job.FramesStart) / title.Framerate; } return 0; } /// /// Gets the number of audio samples used per frame for the given audio encoder. /// /// The encoder to query. /// The number of audio samples used per frame for the given /// audio encoder. internal static int GetAudioSamplesPerFrame(string encoderName) { switch (encoderName) { case "faac": case "ffaac": case "copy:aac": case "vorbis": return 1024; case "lame": case "copy:mp3": return 1152; case "ffac3": case "copy": case "copy:ac3": case "copy:dts": case "copy:dtshd": return 1536; } // Unknown encoder; make a guess. return 1536; } /// /// Gets the size in bytes for the audio with the given parameters. /// /// The encode job. /// The length of the encode in seconds. /// The title to encode. /// The list of tracks to encode. /// The size in bytes for the audio with the given parameters. internal static long GetAudioSize(EncodeJob job, double lengthSeconds, Title title, List> outputTrackList) { long audioBytes = 0; foreach (Tuple outputTrack in outputTrackList) { AudioEncoding encoding = outputTrack.Item1; AudioTrack track = title.AudioTracks[outputTrack.Item2 - 1]; int samplesPerFrame = HandBrakeUtils.GetAudioSamplesPerFrame(encoding.Encoder); int audioBitrate; HBAudioEncoder audioEncoder = Encoders.GetAudioEncoder(encoding.Encoder); if (audioEncoder.IsPassthrough) { // Input bitrate is in bits/second. audioBitrate = track.Bitrate / 8; } else if (encoding.EncodeRateType == AudioEncodeRateType.Quality) { // Can't predict size of quality targeted audio encoding. audioBitrate = 0; } else { int outputBitrate; if (encoding.Bitrate > 0) { outputBitrate = encoding.Bitrate; } else { outputBitrate = Encoders.GetDefaultBitrate( audioEncoder, encoding.SampleRateRaw == 0 ? track.SampleRate : encoding.SampleRateRaw, Encoders.SanitizeMixdown(Encoders.GetMixdown(encoding.Mixdown), audioEncoder, track.ChannelLayout)); } // Output bitrate is in kbps. audioBitrate = outputBitrate * 1000 / 8; } audioBytes += (long)(lengthSeconds * audioBitrate); // Audio overhead audioBytes += encoding.SampleRateRaw * ContainerOverheadPerFrame / samplesPerFrame; } return audioBytes; } } }