// --------------------------------------------------------------------------------------------------------------------
//
// 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.Interop
{
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Runtime.CompilerServices;
using System.Runtime.ExceptionServices;
using System.Runtime.InteropServices;
using HandBrake.Interop.Interop.EventArgs;
using HandBrake.Interop.Interop.HbLib;
using HandBrake.Interop.Interop.Json.Anamorphic;
using HandBrake.Interop.Interop.Json.Shared;
using Newtonsoft.Json;
///
/// HandBrake Interop Utilities
///
public static class HandBrakeUtils
{
///
/// The callback for log messages from HandBrake.
///
private static LoggingCallback loggingCallback;
///
/// The callback for error messages from HandBrake.
///
private static LoggingCallback errorCallback;
///
/// True if the global initialize function has been called.
///
private static bool globalInitialized;
private static bool initSuccess = false;
private static bool initNoHardware = false;
///
/// Fires when HandBrake has logged a message.
///
public static event EventHandler MessageLogged;
///
/// Fires when HandBrake has logged an error.
///
public static event EventHandler ErrorLogged;
///
/// Ensures the HB global initialize method has been called.
///
public static void EnsureGlobalInit(bool initNoHardwareMode)
{
if (!globalInitialized)
{
try
{
if (initNoHardwareMode)
{
initNoHardware = true;
if (HBFunctions.hb_global_init_no_hardware() == -1)
{
throw new InvalidOperationException("HB global init failed.");
}
initSuccess = true;
}
else
{
initSuccess = TryInit();
}
}
catch (Exception e)
{
initSuccess = false;
}
// Try without Hardware support. Bad drivers can sometimes cause issues.
if (!initSuccess)
{
if (HBFunctions.hb_global_init_no_hardware() == -1)
{
throw new InvalidOperationException("HB global init failed.");
}
}
globalInitialized = true;
}
}
///
/// Enables or disables LibDVDNav. If disabled libdvdread will be used instead.
///
///
/// True to enable LibDVDNav.
///
public static void SetDvdNav(bool enableDvdNav)
{
HBFunctions.hb_dvd_set_dvdnav(enableDvdNav ? 1 : 0);
}
///
/// Call before app shutdown. Performs global cleanup.
///
public static void DisposeGlobal()
{
HBFunctions.hb_global_close();
}
///
/// 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 = LoggingHandler;
errorCallback = 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)
{
message = message.TrimEnd();
if (!string.IsNullOrEmpty(message))
{
SendMessageEvent(message);
}
}
///
/// Handles errors from HandBrake.
///
///
/// The error message.
///
public static void ErrorHandler(string message)
{
if (!string.IsNullOrEmpty(message))
{
// These errors happen in normal operations. Log them as messages.
if (message == "dvd: ifoOpen failed" || message.Contains("avformat_seek_file failed") || message.Contains("nav_get_title_list"))
{
SendMessageEvent(message);
return;
}
SendErrorEvent(message);
}
}
///
/// Gets the standard x264 option name given the starting point.
///
///
/// The name.
///
///
/// The standard x264 option name.
///
public static string SanitizeX264OptName(string name)
{
IntPtr namePtr = Marshal.StringToHGlobalAnsi(name);
string sanitizedName = Marshal.PtrToStringAnsi(HBFunctions.hb_x264_encopt_name(namePtr));
Marshal.FreeHGlobal(namePtr);
return sanitizedName;
}
///
/// Checks to see if the given H.264 level is valid given the inputs.
///
///
/// The level to check.
///
///
/// The output picture width.
///
///
/// The output picture height.
///
///
/// The rate numerator.
///
///
/// The rate denominator.
///
///
/// True if x264 interlaced output is enabled.
///
///
/// True if x264 fake interlacing is enabled.
///
///
/// True if the level is valid.
///
public static bool IsH264LevelValid(string level, int width, int height, int fpsNumerator, int fpsDenominator, bool interlaced, bool fakeInterlaced)
{
return HBFunctions.hb_check_h264_level(
level,
width,
height,
fpsNumerator,
fpsDenominator,
interlaced ? 1 : 0,
fakeInterlaced ? 1 : 0) == 0;
}
///
/// Creates an X264 options string from the given settings.
///
///
/// The x264 preset.
///
///
/// The x264 tunes being used.
///
///
/// The extra options string.
///
///
/// The H.264 profile.
///
///
/// The H.264 level.
///
///
/// The width of the final picture.
///
///
/// The height of the final picture.
///
///
/// The full x264 options string from the given inputs.
///
public static string CreateX264OptionsString(
string preset,
IList tunes,
string extraOptions,
string profile,
string level,
int width,
int height)
{
if (width <= 0)
{
throw new ArgumentException("width must be positive.");
}
if (height <= 0)
{
throw new ArgumentException("height must be positive.");
}
IntPtr ptr = HBFunctions.hb_x264_param_unparse(
8,
preset,
string.Join(",", tunes),
extraOptions,
profile,
level,
width,
height); // TODO add bit-depth support.
string x264Settings = Marshal.PtrToStringAnsi(ptr);
return x264Settings;
}
///
/// Gets the final size and PAR of the video, given anamorphic inputs.
///
/// Anamorphic inputs.
/// The final size and PAR of the video.
public static Geometry GetAnamorphicSize(AnamorphicGeometry anamorphicGeometry)
{
string encode = JsonConvert.SerializeObject(anamorphicGeometry, Formatting.Indented, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });
IntPtr json = HBFunctions.hb_set_anamorphic_size_json(Marshal.StringToHGlobalAnsi(encode));
string result = Marshal.PtrToStringAnsi(json);
return JsonConvert.DeserializeObject(result);
}
public static void Reduce(long den, long num, out long x, out long y)
{
// find the greatest common divisor of num & den by Euclid's algorithm
long n = num, d = den;
while (d > 0)
{
long t = d;
d = n % d;
n = t;
}
// at this point n is the gcd. if it's non-zero remove it from num
// and den. Otherwise just return the original values.
if (n > 0)
{
num /= n;
den /= n;
}
x = num;
y = den;
}
///
/// Sends the message logged event to any registered listeners.
///
///
/// The message to send.
///
public static void SendMessageEvent(string message)
{
MessageLogged?.Invoke(null, new MessageLoggedEventArgs(message));
}
///
/// Sends the error logged event to any registered listeners.
///
///
/// The message to send
///
public static void SendErrorEvent(string message)
{
ErrorLogged?.Invoke(null, new MessageLoggedEventArgs(message));
}
public static bool IsInitialised()
{
return initSuccess;
}
public static bool IsInitNoHardware()
{
return initNoHardware;
}
[HandleProcessCorruptedStateExceptions]
private static bool TryInit()
{
try
{
if (HBFunctions.hb_global_init() == -1)
{
throw new InvalidOperationException("HB global init failed.");
}
}
catch (Exception e)
{
return false;
}
return true;
}
}
}