// --------------------------------------------------------------------------------------------------------------------
//
// 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;
}
}
}