// -------------------------------------------------------------------------------------------------------------------- // // 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. // // -------------------------------------------------------------------------------------------------------------------- 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; 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; 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 default mixdown for the given audio encoder and channel layout. /// /// The output codec to be used. /// The input channel layout. /// The default mixdown for the given codec and channel layout. public static Mixdown GetDefaultMixdown(AudioEncoder encoder, int layout) { int defaultMixdown = HBFunctions.hb_get_default_mixdown(Converters.AudioEncoderToNative(encoder), layout); return Converters.NativeToMixdown(defaultMixdown); } /// /// Gets the bitrate limits for the given audio codec, sample rate and mixdown. /// /// The audio encoder used. /// The sample rate used (Hz). /// The mixdown used. /// Limits on the audio bitrate for the given settings. public static Limits GetBitrateLimits(AudioEncoder encoder, int sampleRate, Mixdown mixdown) { if (mixdown == Mixdown.Auto) { throw new ArgumentException("Mixdown cannot be Auto."); } int low = 0; int high = 0; HBFunctions.hb_get_audio_bitrate_limits(Converters.AudioEncoderToNative(encoder), sampleRate, Converters.MixdownToNative(mixdown), ref low, ref high); return new Limits { Low = low, High = high }; } /// /// Sanitizes a mixdown given the output codec and input channel layout. /// /// The desired mixdown. /// The output encoder to be used. /// The input channel layout. /// A sanitized mixdown value. public static Mixdown SanitizeMixdown(Mixdown mixdown, AudioEncoder encoder, int layout) { int sanitizedMixdown = HBFunctions.hb_get_best_mixdown(Converters.AudioEncoderToNative(encoder), layout, Converters.MixdownToNative(mixdown)); return Converters.NativeToMixdown(sanitizedMixdown); } /// /// Sanitizes an audio bitrate given the output codec, sample rate and mixdown. /// /// The desired audio bitrate. /// The output encoder to be used. /// The output sample rate to be used. /// The mixdown to be used. /// A sanitized audio bitrate. public static int SanitizeAudioBitrate(int audioBitrate, AudioEncoder encoder, int sampleRate, Mixdown mixdown) { return HBFunctions.hb_get_best_audio_bitrate(Converters.AudioEncoderToNative(encoder), audioBitrate, sampleRate, Converters.MixdownToNative(mixdown)); } /// /// 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(AudioEncoder encoder) { switch (encoder) { case AudioEncoder.Faac: case AudioEncoder.Vorbis: return 1024; case AudioEncoder.Lame: return 1152; case AudioEncoder.Ac3: case AudioEncoder.Passthrough: case AudioEncoder.Ac3Passthrough: return 1536; } System.Diagnostics.Debug.Assert(true, "Audio encoder unrecognized."); return 0; } /// /// 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; if (Utilities.IsPassthrough(encoding.Encoder)) { // Input bitrate is in bits/second. audioBitrate = track.Bitrate / 8; } else { // Output bitrate is in kbps. audioBitrate = encoding.Bitrate * 1000 / 8; } audioBytes += (long)(lengthSeconds * audioBitrate); // Audio overhead audioBytes += encoding.SampleRateRaw * ContainerOverheadPerFrame / samplesPerFrame; } return audioBytes; } } }