// --------------------------------------------------------------------------------------------------------------------
//
// 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;
///
/// 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 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.ffaac:
case AudioEncoder.AacPassthru:
case AudioEncoder.Vorbis:
return 1024;
case AudioEncoder.Lame:
case AudioEncoder.Mp3Passthru:
return 1152;
case AudioEncoder.Ac3:
case AudioEncoder.Passthrough:
case AudioEncoder.Ac3Passthrough:
case AudioEncoder.DtsPassthrough:
case AudioEncoder.DtsHDPassthrough:
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;
}
}
}