/* common.c

   Copyright (c) 2003-2012 HandBrake Team
   This file is part of the HandBrake source code
   Homepage: <http://handbrake.fr/>.
   It may be used under the terms of the GNU General Public License v2.
   For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html
 */

#include <stdarg.h>
#include <time.h>
#include <ctype.h>
#include <sys/time.h>

#include "common.h"
#include "lang.h"
#include "hb.h"

/**********************************************************************
 * Global variables
 *********************************************************************/
hb_rate_t hb_video_rates[] =
{
    {  "5",     5400000 },
    { "10",     2700000 },
    { "12",     2250000 },
    { "15",     1800000 },
    { "23.976", 1126125 },
    { "24",     1125000 },
    { "25",     1080000 },
    { "29.97",   900900 },
    { "30",      900000 },
    { "50",      540000 },
    { "59.94",   450450 },
    { "60",      450000 },
};
int hb_video_rates_count = sizeof(hb_video_rates) / sizeof(hb_rate_t);

hb_rate_t hb_audio_rates[] =
{
    {  "8",      8000 },
    { "11.025", 11025 },
    { "12",     12000 },
    { "16",     16000 },
    { "22.05",  22050 },
    { "24",     24000 },
    { "32",     32000 },
    { "44.1",   44100 },
    { "48",     48000 },
};
int hb_audio_rates_count = sizeof(hb_audio_rates) / sizeof(hb_rate_t);

hb_rate_t hb_audio_bitrates[] =
{
    // AC3-compatible bitrates
    {   "32",   32 },
    {   "40",   40 },
    {   "48",   48 },
    {   "56",   56 },
    {   "64",   64 },
    {   "80",   80 },
    {   "96",   96 },
    {  "112",  112 },
    {  "128",  128 },
    {  "160",  160 },
    {  "192",  192 },
    {  "224",  224 },
    {  "256",  256 },
    {  "320",  320 },
    {  "384",  384 },
    {  "448",  448 },
    {  "512",  512 },
    {  "576",  576 },
    {  "640",  640 },
    // additional bitrates
    {  "768",  768 },
    {  "960",  960 },
    { "1152", 1152 },
    { "1344", 1344 },
    { "1536", 1536 },
};
int hb_audio_bitrates_count = sizeof(hb_audio_bitrates) / sizeof(hb_rate_t);

static hb_error_handler_t *error_handler = NULL;

hb_mixdown_t hb_audio_mixdowns[] =
{
    { "None",               "HB_AMIXDOWN_NONE",      "none",       HB_AMIXDOWN_NONE      },
    { "Mono",               "HB_AMIXDOWN_MONO",      "mono",       HB_AMIXDOWN_MONO      },
    { "Mono (Left Only)",   "HB_AMIXDOWN_LEFT",      "left_only",  HB_AMIXDOWN_LEFT      },
    { "Mono (Right Only)",  "HB_AMIXDOWN_RIGHT",     "right_only", HB_AMIXDOWN_RIGHT     },
    { "Stereo",             "HB_AMIXDOWN_STEREO",    "stereo",     HB_AMIXDOWN_STEREO    },
    { "Dolby Surround",     "HB_AMIXDOWN_DOLBY",     "dpl1",       HB_AMIXDOWN_DOLBY     },
    { "Dolby Pro Logic II", "HB_AMIXDOWN_DOLBYPLII", "dpl2",       HB_AMIXDOWN_DOLBYPLII },
    { "5.1 Channels",       "HB_AMIXDOWN_5POINT1",   "5point1",    HB_AMIXDOWN_5POINT1   },
    { "6.1 Channels",       "HB_AMIXDOWN_6POINT1",   "6point1",    HB_AMIXDOWN_6POINT1   },
    { "7.1 Channels",       "HB_AMIXDOWN_7POINT1",   "7point1",    HB_AMIXDOWN_7POINT1   },
    { "7.1 (5F/2R/LFE)",    "HB_AMIXDOWN_5_2_LFE",   "5_2_lfe",    HB_AMIXDOWN_5_2_LFE   },
};
int hb_audio_mixdowns_count = sizeof(hb_audio_mixdowns) / sizeof(hb_mixdown_t);

hb_encoder_t hb_video_encoders[] =
{
    { "H.264 (x264)",    "x264",    HB_VCODEC_X264,         HB_MUX_MP4|HB_MUX_MKV },
    { "MPEG-4 (FFmpeg)", "ffmpeg4", HB_VCODEC_FFMPEG_MPEG4, HB_MUX_MP4|HB_MUX_MKV },
    { "MPEG-2 (FFmpeg)", "ffmpeg2", HB_VCODEC_FFMPEG_MPEG2, HB_MUX_MP4|HB_MUX_MKV },
    { "VP3 (Theora)",    "theora",  HB_VCODEC_THEORA,                  HB_MUX_MKV },
};
int hb_video_encoders_count = sizeof(hb_video_encoders) / sizeof(hb_encoder_t);

hb_encoder_t hb_audio_encoders[] =
{
#ifdef __APPLE__
    { "AAC (CoreAudio)",    "ca_aac",     HB_ACODEC_CA_AAC,       HB_MUX_MP4|HB_MUX_MKV },
    { "HE-AAC (CoreAudio)", "ca_haac",    HB_ACODEC_CA_HAAC,      HB_MUX_MP4|HB_MUX_MKV },
#endif
    { "AAC (faac)",         "faac",       HB_ACODEC_FAAC,         HB_MUX_MP4|HB_MUX_MKV },
    { "AAC (ffmpeg)",       "ffaac",      HB_ACODEC_FFAAC,        HB_MUX_MP4|HB_MUX_MKV },
    { "AAC Passthru",       "copy:aac",   HB_ACODEC_AAC_PASS,     HB_MUX_MP4|HB_MUX_MKV },
    { "AC3 (ffmpeg)",       "ffac3",      HB_ACODEC_AC3,          HB_MUX_MP4|HB_MUX_MKV },
    { "AC3 Passthru",       "copy:ac3",   HB_ACODEC_AC3_PASS,     HB_MUX_MP4|HB_MUX_MKV },
    { "DTS Passthru",       "copy:dts",   HB_ACODEC_DCA_PASS,     HB_MUX_MP4|HB_MUX_MKV },
    { "DTS-HD Passthru",    "copy:dtshd", HB_ACODEC_DCA_HD_PASS,  HB_MUX_MP4|HB_MUX_MKV },
    { "MP3 (lame)",         "lame",       HB_ACODEC_LAME,         HB_MUX_MP4|HB_MUX_MKV },
    { "MP3 Passthru",       "copy:mp3",   HB_ACODEC_MP3_PASS,     HB_MUX_MP4|HB_MUX_MKV },
    { "Vorbis (vorbis)",    "vorbis",     HB_ACODEC_VORBIS,                  HB_MUX_MKV },
    { "FLAC (ffmpeg)",      "ffflac",     HB_ACODEC_FFFLAC,                  HB_MUX_MKV },
    { "Auto Passthru",      "copy",       HB_ACODEC_AUTO_PASS,    HB_MUX_MP4|HB_MUX_MKV },
};
int hb_audio_encoders_count = sizeof(hb_audio_encoders) / sizeof(hb_encoder_t);

/* Expose values for PInvoke */
hb_rate_t*    hb_get_video_rates()          { return hb_video_rates;          }
int           hb_get_video_rates_count()    { return hb_video_rates_count;    }
hb_rate_t*    hb_get_audio_rates()          { return hb_audio_rates;          }
int           hb_get_audio_rates_count()    { return hb_audio_rates_count;    }
hb_rate_t*    hb_get_audio_bitrates()       { return hb_audio_bitrates;       }
int           hb_get_audio_bitrates_count() { return hb_audio_bitrates_count; }
hb_mixdown_t* hb_get_audio_mixdowns()       { return hb_audio_mixdowns;       }
int           hb_get_audio_mixdowns_count() { return hb_audio_mixdowns_count; }
hb_encoder_t* hb_get_video_encoders()       { return hb_video_encoders;       }
int           hb_get_video_encoders_count() { return hb_video_encoders_count; }
hb_encoder_t* hb_get_audio_encoders()       { return hb_audio_encoders;       }
int           hb_get_audio_encoders_count() { return hb_audio_encoders_count; }

int hb_mixdown_is_supported(int mixdown, uint32_t codec, uint64_t layout)
{
    return (hb_mixdown_has_codec_support(mixdown, codec) &&
            hb_mixdown_has_remix_support(mixdown, layout));
}

int hb_mixdown_has_codec_support(int mixdown, uint32_t codec)
{
    // Passthru, only "None" mixdown is supported
    if (codec & HB_ACODEC_PASS_FLAG)
        return (mixdown == HB_AMIXDOWN_NONE);

    // Not passthru, "None" mixdown never supported
    if (mixdown == HB_AMIXDOWN_NONE)
        return 0;

    switch (codec)
    {
        case HB_ACODEC_FFFLAC:
        case HB_ACODEC_VORBIS:
            return (mixdown <= HB_AMIXDOWN_7POINT1);

        case HB_ACODEC_LAME:
        case HB_ACODEC_FFAAC:
            return (mixdown <= HB_AMIXDOWN_DOLBYPLII);

        case HB_ACODEC_FAAC:
        case HB_ACODEC_CA_AAC:
        case HB_ACODEC_CA_HAAC:
            return ((mixdown <= HB_AMIXDOWN_5POINT1) ||
                    (mixdown == HB_AMIXDOWN_5_2_LFE));

        default:
            return (mixdown <= HB_AMIXDOWN_5POINT1);
    }
}

int hb_mixdown_has_remix_support(int mixdown, uint64_t layout)
{
    switch (mixdown)
    {
        // stereo + front left/right of center
        case HB_AMIXDOWN_5_2_LFE:
            return ((layout & AV_CH_FRONT_LEFT_OF_CENTER) &&
                    (layout & AV_CH_FRONT_RIGHT_OF_CENTER) &&
                    (layout & AV_CH_LAYOUT_STEREO) == AV_CH_LAYOUT_STEREO);

        // 7.0 or better
        case HB_AMIXDOWN_7POINT1:
            return ((layout & AV_CH_LAYOUT_7POINT0) == AV_CH_LAYOUT_7POINT0);

        // 6.0 or better
        case HB_AMIXDOWN_6POINT1:
            return ((layout & AV_CH_LAYOUT_7POINT0) == AV_CH_LAYOUT_7POINT0 ||
                    (layout & AV_CH_LAYOUT_6POINT0) == AV_CH_LAYOUT_6POINT0 ||
                    (layout & AV_CH_LAYOUT_HEXAGONAL) == AV_CH_LAYOUT_HEXAGONAL);

        // stereo + either of front center, side or back left/right, back center
        case HB_AMIXDOWN_5POINT1:
            return ((layout & AV_CH_LAYOUT_2_1) == AV_CH_LAYOUT_2_1 ||
                    (layout & AV_CH_LAYOUT_2_2) == AV_CH_LAYOUT_2_2 ||
                    (layout & AV_CH_LAYOUT_QUAD) == AV_CH_LAYOUT_QUAD ||
                    (layout & AV_CH_LAYOUT_SURROUND) == AV_CH_LAYOUT_SURROUND);

        // stereo + either of side or back left/right, back center
        // also, allow Dolby Surrounbd output if the input is already Dolby
        case HB_AMIXDOWN_DOLBY:
        case HB_AMIXDOWN_DOLBYPLII:
            return ((layout & AV_CH_LAYOUT_2_1) == AV_CH_LAYOUT_2_1 ||
                    (layout & AV_CH_LAYOUT_2_2) == AV_CH_LAYOUT_2_2 ||
                    (layout & AV_CH_LAYOUT_QUAD) == AV_CH_LAYOUT_QUAD ||
                    (layout == AV_CH_LAYOUT_STEREO_DOWNMIX &&
                     mixdown == HB_AMIXDOWN_DOLBY));

        // more than 1 channel
        case HB_AMIXDOWN_STEREO:
            return (av_get_channel_layout_nb_channels(layout) > 1);

        // regular stereo (not Dolby)
        case HB_AMIXDOWN_LEFT:
        case HB_AMIXDOWN_RIGHT:
            return (layout == AV_CH_LAYOUT_STEREO);

        // mono remix always supported
        // HB_AMIXDOWN_NONE always supported (for Passthru)
        case HB_AMIXDOWN_MONO:
        case HB_AMIXDOWN_NONE:
            return 1;

        // unknown mixdown, should never happen
        default:
            return 0;
    }
}

int hb_mixdown_get_discrete_channel_count(int amixdown)
{
    switch (amixdown)
    {
        case HB_AMIXDOWN_5_2_LFE:
        case HB_AMIXDOWN_7POINT1:
            return 8;

        case HB_AMIXDOWN_6POINT1:
            return 7;

        case HB_AMIXDOWN_5POINT1:
            return 6;

        case HB_AMIXDOWN_MONO:
        case HB_AMIXDOWN_LEFT:
        case HB_AMIXDOWN_RIGHT:
            return 1;

        case HB_AMIXDOWN_NONE:
            return 0;

        default:
            return 2;
    }
}

int hb_mixdown_get_low_freq_channel_count(int amixdown)
{
    switch (amixdown)
    {
        case HB_AMIXDOWN_5POINT1:
        case HB_AMIXDOWN_6POINT1:
        case HB_AMIXDOWN_7POINT1:
        case HB_AMIXDOWN_5_2_LFE:
            return 1;

        default:
            return 0;
    }
}

int hb_mixdown_get_mixdown_from_short_name(const char *short_name)
{
    int i;
    for (i = 0; i < hb_audio_mixdowns_count; i++)
    {
        if (!strcmp(hb_audio_mixdowns[i].short_name, short_name))
        {
            return hb_audio_mixdowns[i].amixdown;
        }
    }
    return 0;
}

const char* hb_mixdown_get_short_name_from_mixdown(int amixdown)
{
    int i;
    for (i = 0; i < hb_audio_mixdowns_count; i++)
    {
        if (hb_audio_mixdowns[i].amixdown == amixdown)
        {
            return hb_audio_mixdowns[i].short_name;
        }
    }
    return "";
}

void hb_autopassthru_apply_settings( hb_job_t * job, hb_title_t * title )
{
    int i, j, already_printed;
    hb_audio_t * audio;
    for( i = 0, already_printed = 0; i < hb_list_count( title->list_audio ); )
    {
        audio = hb_list_item( title->list_audio, i );
        if( audio->config.out.codec == HB_ACODEC_AUTO_PASS )
        {
            if( !already_printed )
                hb_autopassthru_print_settings( job );
            already_printed = 1;
            audio->config.out.codec = hb_autopassthru_get_encoder( audio->config.in.codec,
                                                                   job->acodec_copy_mask,
                                                                   job->acodec_fallback,
                                                                   job->mux );
            if( !( audio->config.out.codec & HB_ACODEC_PASS_FLAG ) &&
                !( audio->config.out.codec & HB_ACODEC_MASK ) )
            {
                hb_log( "Auto Passthru: passthru not possible and no valid fallback specified, dropping track %d",
                        audio->config.out.track );
                hb_list_rem( title->list_audio, audio );
                free( audio );
                continue;
            }
            audio->config.out.samplerate = audio->config.in.samplerate;
            if( !( audio->config.out.codec & HB_ACODEC_PASS_FLAG ) )
            {
                if( audio->config.out.codec == job->acodec_fallback )
                {
                    hb_log( "Auto Passthru: passthru not possible for track %d, using fallback",
                            audio->config.out.track );
                }
                else
                {
                    hb_log( "Auto Passthru: passthru and fallback not possible for track %d, using default encoder",
                            audio->config.out.track );
                }
                audio->config.out.mixdown = hb_get_default_mixdown( audio->config.out.codec,
                                                                    audio->config.in.channel_layout );
                audio->config.out.bitrate = hb_get_default_audio_bitrate( audio->config.out.codec,
                                                                          audio->config.out.samplerate,
                                                                          audio->config.out.mixdown );
                audio->config.out.compression_level = hb_get_default_audio_compression( audio->config.out.codec );
            }
            else
            {
                for( j = 0; j < hb_audio_encoders_count; j++ )
                {
                    if( hb_audio_encoders[j].encoder == audio->config.out.codec )
                    {
                        hb_log( "Auto Passthru: using %s for track %d",
                                hb_audio_encoders[j].human_readable_name,
                                audio->config.out.track );
                        break;
                    }
                }
            }
        }
        /* Adjust output track number, in case we removed one.
         * Output tracks sadly still need to be in sequential order.
         * Note: out.track starts at 1, i starts at 0 */
        audio->config.out.track = ++i;
    }
}

void hb_autopassthru_print_settings( hb_job_t * job )
{
    int i, codec_len;
    char *mask = NULL, *tmp, *fallback = NULL;
    for( i = 0; i < hb_audio_encoders_count; i++ )
    {
        if( ( hb_audio_encoders[i].encoder & HB_ACODEC_PASS_FLAG ) &&
            ( hb_audio_encoders[i].encoder != HB_ACODEC_AUTO_PASS ) &&
            ( hb_audio_encoders[i].encoder & job->acodec_copy_mask ) )
        {
            if( mask )
            {
                tmp = hb_strncat_dup( mask, ", ", 2 );
                if( tmp )
                {
                    free( mask );
                    mask = tmp;
                }
            }
            // passthru name without " Passthru"
            codec_len = strlen( hb_audio_encoders[i].human_readable_name ) - 9;
            tmp = hb_strncat_dup( mask, hb_audio_encoders[i].human_readable_name, codec_len );
            if( tmp )
            {
                free( mask );
                mask = tmp;
            }
        }
        else if( !( hb_audio_encoders[i].encoder & HB_ACODEC_PASS_FLAG ) &&
                  ( hb_audio_encoders[i].encoder == job->acodec_fallback ) )
        {
            fallback = hb_audio_encoders[i].human_readable_name;
        }
    }
    if( !mask )
        hb_log( "Auto Passthru: no codecs allowed" );
    else
        hb_log( "Auto Passthru: allowed codecs are %s", mask );
    if( !fallback )
        hb_log( "Auto Passthru: no valid fallback specified" );
    else
        hb_log( "Auto Passthru: fallback is %s", fallback );
}

int hb_autopassthru_get_encoder( int in_codec, int copy_mask, int fallback, int muxer )
{
    int i;
    int out_codec = ( copy_mask & in_codec ) | HB_ACODEC_PASS_FLAG;
    // sanitize fallback encoder and selected passthru
    // note: invalid fallbacks are caught in hb_autopassthru_apply_settings
    for( i = 0; i < hb_audio_encoders_count; i++ )
    {
        if( ( hb_audio_encoders[i].encoder == fallback ) &&
           !( hb_audio_encoders[i].muxers & muxer ) )
        {
            // fallback not possible with current muxer
            // use the default audio encoder instead
#ifndef __APPLE__
            if( muxer == HB_MUX_MKV )
                // Lame is the default for MKV
                fallback = HB_ACODEC_LAME;
            else
#endif          // Core Audio or faac
                fallback = hb_audio_encoders[0].encoder;
            break;
        }
    }
    for( i = 0; i < hb_audio_encoders_count; i++ )
    {
        if( ( hb_audio_encoders[i].encoder == out_codec ) &&
           !( hb_audio_encoders[i].muxers & muxer ) )
        {
            // selected passthru not possible with current muxer
            out_codec = fallback;
            break;
        }
    }
    if( !( out_codec & HB_ACODEC_PASS_MASK ) )
        return fallback;
    return out_codec;
}

// Given an input bitrate, find closest match in the set of allowed bitrates
int hb_find_closest_audio_bitrate(int bitrate)
{
    // Check if bitrate mode was disabled
    if (bitrate <= 0)
        return bitrate;

    int ii, result;
    // result is highest rate if none found during search.
    // rate returned will always be <= rate asked for.
    result = hb_audio_bitrates[0].rate;
    for (ii = hb_audio_bitrates_count - 1; ii > 0; ii--)
    {
        if (bitrate >= hb_audio_bitrates[ii].rate)
        {
            result = hb_audio_bitrates[ii].rate;
            break;
        }
    }
    return result;
}

/* Get the bitrate low and high limits for a codec/samplerate/mixdown triplet.

Encoder    1.0 channel    2.0 channels    5.1 channels    6.1 channels    7.1 channels
--------------------------------------------------------------------------------------

faac
----
supported samplerates: 8 - 48 kHz
libfaac/util.c defines the bitrate limits:
MinBitrate() -> 8000 bps (per channel, incl. LFE).
MaxBitrate() -> (6144 * samplerate / 1024) bps (per channel, incl. LFE).
But output bitrates don't go as high as the theoretical maximums:
12 kHz        43  (72)        87 (144)      260  (432)      303  (504)      342  (576)
24 kHz        87 (144)       174 (288)      514  (864)      595 (1008)      669 (1152)
48 kHz       174 (288)       347 (576)      970 (1728)     1138 (2016)     1287 (2304)
Also, faac isn't a great encoder, so you don't want to allow too low a bitrate.
Limits: minimum of  32 Kbps per channel
        maximum of 192 Kbps per channel at 32-48 kHz, adjusted for sr_shift


ffaac
-----
supported samplerates: 8 - 48 kHz
libavcodec/aacenc.c defines a maximum bitrate:
-> 6144 * samplerate / 1024 bps (per channel, incl. LFE).
But output bitrates don't go as high as the theoretical maximums:
12 kHz        61  (72)       123 (144)
24 kHz       121 (144)       242 (288)
48 kHz       236 (288)       472 (576)
Also, ffaac isn't a great encoder, so you don't want to allow too low a bitrate.
Limits: minimum of  32 Kbps per channel
        maximum of 192 Kbps per channel at 32 kHz, adjusted for sr_shift
        maximum of 256 Kbps per channel at 44.1-48 kHz, adjusted for sr_shift

vorbis
------
supported samplerates: 8 - 48 kHz
lib/modes/setup_*.h provides a range of allowed bitrates for various configurations.
for each samplerate, the highest minimums and lowest maximums are:
 8 kHz        Minimum  8 Kbps, maximum  32 Kbps (per channel, incl. LFE).
12 kHz        Minimum 14 Kbps, maximum  44 Kbps (per channel, incl. LFE).
16 kHz        Minimum 16 Kbps, maximum  86 Kbps (per channel, incl. LFE).
24 kHz        Minimum 22 Kbps, maximum  86 Kbps (per channel, incl. LFE).
32 kHz        Minimum 26 Kbps, maximum 190 Kbps (per channel, incl. LFE).
48 kHz        Minimum 28 Kbps, maximum 240 Kbps (per channel, incl. LFE).
Limits: minimum of 14/22/28 Kbps per channel (8-12, 16-24, 32-48 kHz)
        maximum of 32/86/190/240 Kbps per channel (8-12, 16-24, 32, 44.1-48 kHz)

lame
----
supported samplerates: 8 - 48 kHz
lame_init_params() allows the following bitrates:
12 kHz        Minimum  8 Kbps, maximum  64 Kbps
24 kHz        Minimum  8 Kbps, maximum 160 Kbps
48 kHz        Minimum 32 Kbps, maximum 320 Kbps
Limits: minimum of 8/8/32 Kbps (8-12, 16-24, 32-48 kHz)
        maximum of 64/160/320 Kbps (8-12, 16-24, 32-48 kHz)

ffac3
-----
supported samplerates: 32 - 48 kHz (< 32 kHz disabled for compatibility reasons)
Dolby's encoder has a min. of 224 Kbps for 5 full-bandwidth channels (5.0, 5.1)
The maximum AC3 bitrate is 640 Kbps
Limits: minimum of 224/5 Kbps per full-bandwidth channel, maximum of 640 Kbps

ca_aac
------
supported samplerates: 8 - 48 kHz
Core Audio API provides a range of allowed bitrates:
 8 kHz         8 -  24        16 -  48        40 - 112        48 - 144        56 - 160
12 kHz        12 -  32        24 -  64        64 - 160        72 - 192        96 - 224
16 kHz        12 -  48        24 -  96        64 - 224        72 - 288        96 - 320
24 kHz        16 -  64        32 - 128        80 - 320        96 - 384       112 - 448
32 kHz        24 -  96        48 - 192       128 - 448       144 - 576       192 - 640
48 kHz        32 - 256        64 - 320       160 - 768       192 - 960       224 - 960
Limits:
 8 kHz -> minimum of  8 Kbps and maximum of  24 Kbps per full-bandwidth channel
12 kHz -> minimum of 12 Kbps and maximum of  32 Kbps per full-bandwidth channel
16 kHz -> minimum of 12 Kbps and maximum of  48 Kbps per full-bandwidth channel
24 kHz -> minimum of 16 Kbps and maximum of  64 Kbps per full-bandwidth channel
32 kHz -> minimum of 24 Kbps and maximum of  96 Kbps per full-bandwidth channel
48 kHz -> minimum of 32 Kbps and maximum of 160 Kbps per full-bandwidth channel
48 kHz ->                        maximum of +96 Kbps for Mono
Note: encCoreAudioInit() will sanitize any mistake made here.

ca_haac
-------
supported samplerates: 32 - 48 kHz
Core Audio API provides a range of allowed bitrates:
32 kHz         12 - 40         24 - 80        64 - 192          N/A           96 - 256
48 kHz         16 - 40         32 - 80        80 - 192          N/A          112 - 256
Limits: minimum of 12 (+ 4 if rate >= 44100) Kbps per full-bandwidth channel
        maximum of 40 Kbps per full-bandwidth channel
Note: encCoreAudioInit() will sanitize any mistake made here.
*/

void hb_get_audio_bitrate_limits(uint32_t codec, int samplerate, int mixdown,
                                 int *low, int *high)
{
    if (codec & HB_ACODEC_PASS_FLAG)
    {
        // Bitrates don't apply to passthrough audio, but may apply if we
        // fallback to an encoder when the source can't be passed through.
        *low = hb_audio_bitrates[0].rate;
        *high = hb_audio_bitrates[hb_audio_bitrates_count-1].rate;
        return;
    }

    /* samplerate, sr_shift */
    int sr_shift;
    samplerate = hb_get_best_samplerate(codec, samplerate, &sr_shift);

    /* LFE, full-bandwidth channels */
    int lfe_count, nchannels;
    lfe_count = hb_mixdown_get_low_freq_channel_count(mixdown);
    nchannels = hb_mixdown_get_discrete_channel_count(mixdown) - lfe_count;

    switch (codec)
    {
        // Bitrates don't apply to "lossless" audio
        case HB_ACODEC_FFFLAC:
            *low = *high = -1;
            return;

        case HB_ACODEC_AC3:
            *low  = 224 * nchannels / 5;
            *high = 640;
            break;

        case HB_ACODEC_CA_AAC:
        {
            switch (samplerate)
            {
                case 8000:
                    *low  = nchannels *  8;
                    *high = nchannels * 24;
                    break;
                case 11025:
                case 12000:
                    *low  = nchannels * 12;
                    *high = nchannels * 32;
                    break;
                case 16000:
                    *low  = nchannels * 12;
                    *high = nchannels * 48;
                    break;
                case 22050:
                case 24000:
                    *low  = nchannels * 16;
                    *high = nchannels * 64;
                    break;
                case 32000:
                    *low  = nchannels * 24;
                    *high = nchannels * 96;
                    break;
                case 44100:
                case 48000:
                default:
                    *low  = nchannels * 32;
                    *high = nchannels * (160 + (96 * (nchannels == 1)));
                    break;
            }
        } break;

        case HB_ACODEC_CA_HAAC:
            *low  = nchannels * (12 + (4 * (samplerate >= 44100)));
            *high = nchannels * 40;
            break;

        case HB_ACODEC_FAAC:
            *low  = (nchannels + lfe_count) * 32;
            *high = (nchannels + lfe_count) * (192 >> sr_shift);
            break;

        case HB_ACODEC_FFAAC:
            *low  = ((nchannels + lfe_count) * 32);
            *high = ((nchannels + lfe_count) *
                     ((192 + (64 * ((samplerate << sr_shift) >= 44100)))
                      >> sr_shift));
            break;

        case HB_ACODEC_LAME:
            *low  =  8 + (24 * (sr_shift < 1));
            *high = 64 + (96 * (sr_shift < 2)) + (160 * (sr_shift < 1));
            break;

        case HB_ACODEC_VORBIS:
            *low  = (nchannels + lfe_count) * (14 +
                                               (8 * (sr_shift < 2)) +
                                               (6 * (sr_shift < 1)));
            *high = (nchannels + lfe_count) * (32 +
                                               ( 54 * (sr_shift < 2)) +
                                               (104 * (sr_shift < 1)) +
                                               ( 50 * (samplerate >= 44100)));
            break;

        default:
            *low  = hb_audio_bitrates[0].rate;
            *high = hb_audio_bitrates[hb_audio_bitrates_count-1].rate;
            break;
    }
    // sanitize max. bitrate
    if (*high < hb_audio_bitrates[0].rate)
        *high = hb_audio_bitrates[0].rate;
    if (*high > hb_audio_bitrates[hb_audio_bitrates_count-1].rate)
        *high = hb_audio_bitrates[hb_audio_bitrates_count-1].rate;
}

// Given an input bitrate, sanitize it.
// Check low and high limits and make sure it is in the set of allowed bitrates.
int hb_get_best_audio_bitrate(uint32_t codec, int bitrate, int samplerate,
                              int mixdown)
{
    int low, high;
    hb_get_audio_bitrate_limits(codec, samplerate, mixdown, &low, &high);
    if (bitrate > high)
        bitrate = high;
    if (bitrate < low)
        bitrate = low;
    return hb_find_closest_audio_bitrate(bitrate);
}

// Get the default bitrate for a given codec/samplerate/mixdown triplet.
int hb_get_default_audio_bitrate(uint32_t codec, int samplerate, int mixdown)
{
    if (codec & HB_ACODEC_PASS_FLAG)
    {
        return -1;
    }

    int bitrate, nchannels, sr_shift;
    /* full-bandwidth channels, sr_shift */
    nchannels = (hb_mixdown_get_discrete_channel_count(mixdown) -
                 hb_mixdown_get_low_freq_channel_count(mixdown));
    hb_get_best_samplerate(codec, samplerate, &sr_shift);

    switch (codec)
    {
        case HB_ACODEC_FFFLAC:
            return -1;

        // 96, 224, 640 Kbps
        case HB_ACODEC_AC3:
            bitrate = (nchannels * 128) - (32 * (nchannels < 5));
            break;

        case HB_ACODEC_CA_HAAC:
            bitrate = nchannels * 32;
            break;

        default:
            bitrate = nchannels * 80;
            break;
    }
    // sample_rate adjustment
    bitrate >>= sr_shift;
    return hb_get_best_audio_bitrate(codec, bitrate, samplerate, mixdown);
}

// Get limits and hints for the UIs.
//
// granularity sets the minimum step increments that should be used
// (it's ok to round up to some nice multiple if you like)
//
// direction says whether 'low' limit is highest or lowest 
// quality (direction 0 == lowest value is worst quality)
void hb_get_audio_quality_limits(uint32_t codec, float *low, float *high,
                                 float *granularity, int *direction)
{
    switch (codec)
    {
        case HB_ACODEC_LAME:
            *direction = 1;
            *granularity = 0.5;
            *low = 0.;
            *high = 10.0;
            break;

        case HB_ACODEC_VORBIS:
            *direction = 0;
            *granularity = 0.5;
            *low = -2.0;
            *high = 10.0;
            break;

        case HB_ACODEC_CA_AAC:
            *direction = 0;
            *granularity = 9;
            *low = 1.;
            *high = 127.0;
            break;

        default:
            *direction = 0;
            *granularity = 1;
            *low = *high = HB_INVALID_AUDIO_QUALITY;
            break;
    }
}

float hb_get_best_audio_quality(uint32_t codec, float quality)
{
    float low, high, granularity;
    int direction;
    hb_get_audio_quality_limits(codec, &low, &high, &granularity, &direction);
    if (quality > high)
        quality = high;
    if (quality < low)
        quality = low;
    return quality;
}

float hb_get_default_audio_quality( uint32_t codec )
{
    float quality;
    switch( codec )
    {
        case HB_ACODEC_LAME:
            quality = 2.;
            break;

        case HB_ACODEC_VORBIS:
            quality = 5.;
            break;

        case HB_ACODEC_CA_AAC:
            quality = 91.;
            break;

        default:
            quality = HB_INVALID_AUDIO_QUALITY;
            break;
    }
    return quality;
}

// Get limits and hints for the UIs.
//
// granularity sets the minimum step increments that should be used
// (it's ok to round up to some nice multiple if you like)
//
// direction says whether 'low' limit is highest or lowest 
// compression level (direction 0 == lowest value is worst compression level)
void hb_get_audio_compression_limits(uint32_t codec, float *low, float *high,
                                     float *granularity, int *direction)
{
    switch (codec)
    {
        case HB_ACODEC_FFFLAC:
            *direction = 0;
            *granularity = 1;
            *high = 12;
            *low = 0;
            break;

        case HB_ACODEC_LAME:
            *direction = 1;
            *granularity = 1;
            *high = 9;
            *low = 0;
            break;

        default:
            *direction = 0;
            *granularity = 1;
            *low = *high = -1;
            break;
    }
}

float hb_get_best_audio_compression(uint32_t codec, float compression)
{
    float low, high, granularity;
    int direction;
    hb_get_audio_compression_limits(codec, &low, &high, &granularity, &direction);
    if( compression > high )
        compression = high;
    if( compression < low )
        compression = low;
    return compression;
}

float hb_get_default_audio_compression(uint32_t codec)
{
    switch (codec)
    {
        case HB_ACODEC_FFFLAC:
            return 5.;

        case HB_ACODEC_LAME:
            return 2.;

        default:
            return -1.;
    }
}

int hb_get_best_mixdown(uint32_t codec, uint64_t layout, int mixdown)
{
    // Passthru, only "None" mixdown is supported
    if (codec & HB_ACODEC_PASS_FLAG)
        return HB_AMIXDOWN_NONE;

    // caller requested the best available mixdown
    if (mixdown == HB_INVALID_AMIXDOWN)
        mixdown = hb_audio_mixdowns[hb_audio_mixdowns_count].amixdown;

    int ii;
    // test all mixdowns until an authorized, supported mixdown is found
    // stop before we reach the "worst" non-None mixdown (index == 1)
    for (ii = hb_audio_mixdowns_count; ii > 1; ii--)
        if (hb_audio_mixdowns[ii].amixdown <= mixdown &&
            hb_mixdown_is_supported(hb_audio_mixdowns[ii].amixdown, codec, layout))
            break;
    return hb_audio_mixdowns[ii].amixdown;
}

int hb_get_default_mixdown(uint32_t codec, uint64_t layout)
{
    int mixdown;
    switch (codec)
    {
        // the FLAC encoder defaults to the best mixdown up to 7.1
        case HB_ACODEC_FFFLAC:
            mixdown = HB_AMIXDOWN_7POINT1;
            break;
        // the AC3 encoder defaults to the best mixdown up to 5.1
        case HB_ACODEC_AC3:
            mixdown = HB_AMIXDOWN_5POINT1;
            break;
        // other encoders default to the best mixdown up to DPLII
        default:
            mixdown = HB_AMIXDOWN_DOLBYPLII;
            break;
    }
    // return the best available mixdown up to the selected default
    return hb_get_best_mixdown(codec, layout, mixdown);
}

int hb_get_best_samplerate(uint32_t codec, int samplerate, int *sr_shift)
{
    int ii, best_samplerate, samplerate_shift;
    if ((samplerate < 32000) &&
        (codec == HB_ACODEC_CA_HAAC || codec == HB_ACODEC_AC3))
    {
        // ca_haac can't do samplerates < 32 kHz
        // AC-3 < 32 kHz suffers from poor hardware compatibility
        best_samplerate  = 32000;
        samplerate_shift = 0;
    }
    else
    {
        best_samplerate = samplerate;
        for (ii = hb_audio_rates_count - 1; ii >= 0; ii--)
        {
            // valid samplerate
            if (best_samplerate == hb_audio_rates[ii].rate)
                break;

            // samplerate is higher than the next valid samplerate,
            // or lower than the lowest valid samplerate
            if (best_samplerate > hb_audio_rates[ii].rate || ii == 0)
            {
                best_samplerate = hb_audio_rates[ii].rate;
                break;
            }
        }
        /* sr_shift: 0 -> 48000, 44100, 32000 Hz
         *           1 -> 24000, 22050, 16000 Hz
         *           2 -> 12000, 11025,  8000 Hz
         *
         * also, since samplerates are sanitized downwards:
         *
         * (samplerate < 32000) implies (samplerate <= 24000)
         */
        samplerate_shift = ((best_samplerate < 16000) ? 2 :
                            (best_samplerate < 32000) ? 1 : 0);
    }
    if (sr_shift != NULL)
    {
        *sr_shift = samplerate_shift;
    }
    return best_samplerate;
}

/**********************************************************************
 * hb_reduce
 **********************************************************************
 * Given a numerator (num) and a denominator (den), reduce them to an
 * equivalent fraction and store the result in x and y.
 *********************************************************************/
void hb_reduce( int *x, int *y, int num, int den )
{
    // find the greatest common divisor of num & den by Euclid's algorithm
    int n = num, d = den;
    while ( d )
    {
        int 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 )
    {
        *x = num / n;
        *y = den / n;
    }
    else
    {
        *x = num;
        *y = den;
    }
}

/**********************************************************************
 * hb_reduce64
 **********************************************************************
 * Given a numerator (num) and a denominator (den), reduce them to an
 * equivalent fraction and store the result in x and y.
 *********************************************************************/
void hb_reduce64( int64_t *x, int64_t *y, int64_t num, int64_t den )
{
    // find the greatest common divisor of num & den by Euclid's algorithm
    int64_t n = num, d = den;
    while ( d )
    {
        int64_t 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 )
    {
        num /= n;
        den /= n;
    }

    *x = num;
    *y = den;

}

void hb_limit_rational64( int64_t *x, int64_t *y, int64_t num, int64_t den, int64_t limit )
{
    hb_reduce64( &num, &den, num, den );
    if ( num < limit && den < limit )
    {
        *x = num;
        *y = den;
        return;
    }

    if ( num > den )
    {
        double div = (double)limit / num;
        num = limit;
        den *= div;
    }
    else
    {
        double div = (double)limit / den;
        den = limit;
        num *= div;
    }
    *x = num;
    *y = den;
}

/**********************************************************************
 * hb_fix_aspect
 **********************************************************************
 * Given the output width (if HB_KEEP_WIDTH) or height
 * (HB_KEEP_HEIGHT) and the current crop values, calculates the
 * correct height or width in order to respect the DVD aspect ratio
 *********************************************************************/
void hb_fix_aspect( hb_job_t * job, int keep )
{
    hb_title_t * title = job->title;
    int          i;
    int  min_width;
    int min_height;
    int    modulus;

    /* don't do anything unless the title has complete size info */
    if ( title->height == 0 || title->width == 0 || title->aspect == 0 )
    {
        hb_log( "hb_fix_aspect: incomplete info for title %d: "
                "height = %d, width = %d, aspect = %.3f",
                title->index, title->height, title->width, title->aspect );
        return;
    }

    // min_width and min_height should be multiples of modulus
    min_width    = 32;
    min_height   = 32;
    modulus      = job->modulus ? job->modulus : 16;

    for( i = 0; i < 4; i++ )
    {
        // Sanity check crop values are zero or positive multiples of 2
        if( i < 2 )
        {
            // Top, bottom
            job->crop[i] = MIN( EVEN( job->crop[i] ), EVEN( ( title->height / 2 ) - ( min_height / 2 ) ) );
            job->crop[i] = MAX( 0, job->crop[i] );
        }
        else
        {
            // Left, right
            job->crop[i] = MIN( EVEN( job->crop[i] ), EVEN( ( title->width / 2 ) - ( min_width / 2 ) ) );
            job->crop[i] = MAX( 0, job->crop[i] );
        }
    }

    double par = (double)title->width / ( (double)title->height * title->aspect );
    double cropped_sar = (double)( title->height - job->crop[0] - job->crop[1] ) /
                         (double)( title->width - job->crop[2] - job->crop[3] );
    double ar = par * cropped_sar;

    // Dimensions must be greater than minimum and multiple of modulus
    if( keep == HB_KEEP_WIDTH )
    {
        job->width  = MULTIPLE_MOD( job->width, modulus );
        job->width  = MAX( min_width, job->width );
        job->height = MULTIPLE_MOD( (uint64_t)( (double)job->width * ar ), modulus );
        job->height = MAX( min_height, job->height );
    }
    else
    {
        job->height = MULTIPLE_MOD( job->height, modulus );
        job->height = MAX( min_height, job->height );
        job->width  = MULTIPLE_MOD( (uint64_t)( (double)job->height / ar ), modulus );
        job->width  = MAX( min_width, job->width );
    }
}

/**********************************************************************
 * hb_list implementation
 **********************************************************************
 * Basic and slow, but enough for what we need
 *********************************************************************/

#define HB_LIST_DEFAULT_SIZE 20

struct hb_list_s
{
    /* Pointers to items in the list */
    void ** items;

    /* How many (void *) allocated in 'items' */
    int     items_alloc;

    /* How many valid pointers in 'items' */
    int     items_count;
};

/**********************************************************************
 * hb_list_init
 **********************************************************************
 * Allocates an empty list ready for HB_LIST_DEFAULT_SIZE items
 *********************************************************************/
hb_list_t * hb_list_init()
{
    hb_list_t * l;

    l              = calloc( sizeof( hb_list_t ), 1 );
    l->items       = calloc( HB_LIST_DEFAULT_SIZE * sizeof( void * ), 1 );
    l->items_alloc = HB_LIST_DEFAULT_SIZE;

    return l;
}

/**********************************************************************
 * hb_list_count
 **********************************************************************
 * Returns the number of items currently in the list
 *********************************************************************/
int hb_list_count( hb_list_t * l )
{
    return l->items_count;
}

/**********************************************************************
 * hb_list_add
 **********************************************************************
 * Adds an item at the end of the list, making it bigger if necessary.
 * Can safely be called with a NULL pointer to add, it will be ignored.
 *********************************************************************/
void hb_list_add( hb_list_t * l, void * p )
{
    if( !p )
    {
        return;
    }

    if( l->items_count == l->items_alloc )
    {
        /* We need a bigger boat */
        l->items_alloc += HB_LIST_DEFAULT_SIZE;
        l->items        = realloc( l->items,
                                   l->items_alloc * sizeof( void * ) );
    }

    l->items[l->items_count] = p;
    (l->items_count)++;
}

/**********************************************************************
 * hb_list_insert
 **********************************************************************
 * Adds an item at the specifiec position in the list, making it bigger
 * if necessary.
 * Can safely be called with a NULL pointer to add, it will be ignored.
 *********************************************************************/
void hb_list_insert( hb_list_t * l, int pos, void * p )
{
    if( !p )
    {
        return;
    }

    if( l->items_count == l->items_alloc )
    {
        /* We need a bigger boat */
        l->items_alloc += HB_LIST_DEFAULT_SIZE;
        l->items        = realloc( l->items,
                                   l->items_alloc * sizeof( void * ) );
    }

    if ( l->items_count != pos )
    {
        /* Shift all items after it sizeof( void * ) bytes earlier */
        memmove( &l->items[pos+1], &l->items[pos],
                 ( l->items_count - pos ) * sizeof( void * ) );
    }


    l->items[pos] = p;
    (l->items_count)++;
}

/**********************************************************************
 * hb_list_rem
 **********************************************************************
 * Remove an item from the list. Bad things will happen if called
 * with a NULL pointer or if the item is not in the list.
 *********************************************************************/
void hb_list_rem( hb_list_t * l, void * p )
{
    int i;

    /* Find the item in the list */
    for( i = 0; i < l->items_count; i++ )
    {
        if( l->items[i] == p )
        {
            /* Shift all items after it sizeof( void * ) bytes earlier */
            memmove( &l->items[i], &l->items[i+1],
                     ( l->items_count - i - 1 ) * sizeof( void * ) );

            (l->items_count)--;
            break;
        }
    }
}

/**********************************************************************
 * hb_list_item
 **********************************************************************
 * Returns item at position i, or NULL if there are not that many
 * items in the list
 *********************************************************************/
void * hb_list_item( hb_list_t * l, int i )
{
    if( i < 0 || i >= l->items_count )
    {
        return NULL;
    }

    return l->items[i];
}

/**********************************************************************
 * hb_list_bytes
 **********************************************************************
 * Assuming all items are of type hb_buffer_t, returns the total
 * number of bytes in the list
 *********************************************************************/
int hb_list_bytes( hb_list_t * l )
{
    hb_buffer_t * buf;
    int           ret;
    int           i;

    ret = 0;
    for( i = 0; i < hb_list_count( l ); i++ )
    {
        buf  = hb_list_item( l, i );
        ret += buf->size - buf->offset;
    }

    return ret;
}

/**********************************************************************
 * hb_list_seebytes
 **********************************************************************
 * Assuming all items are of type hb_buffer_t, copy <size> bytes from
 * the list to <dst>, keeping the list unmodified.
 *********************************************************************/
void hb_list_seebytes( hb_list_t * l, uint8_t * dst, int size )
{
    hb_buffer_t * buf;
    int           copied;
    int           copying;
    int           i;

    for( i = 0, copied = 0; copied < size; i++ )
    {
        buf     = hb_list_item( l, i );
        copying = MIN( buf->size - buf->offset, size - copied );
        memcpy( &dst[copied], &buf->data[buf->offset], copying );
        copied += copying;
    }
}

/**********************************************************************
 * hb_list_getbytes
 **********************************************************************
 * Assuming all items are of type hb_buffer_t, copy <size> bytes from
 * the list to <dst>. What's copied is removed from the list.
 * The variable pointed by <pts> is set to the PTS of the buffer the
 * first byte has been got from.
 * The variable pointed by <pos> is set to the position of that byte
 * in that buffer.
 *********************************************************************/
void hb_list_getbytes( hb_list_t * l, uint8_t * dst, int size,
                       uint64_t * pts, uint64_t * pos )
{
    hb_buffer_t * buf;
    int           copied;
    int           copying;
    uint8_t       has_pts;

    /* So we won't have to deal with NULL pointers */
     uint64_t dummy1, dummy2;

    if( !pts ) pts = &dummy1;
    if( !pos ) pos = &dummy2;

    for( copied = 0, has_pts = 0; copied < size;  )
    {
        buf     = hb_list_item( l, 0 );
        copying = MIN( buf->size - buf->offset, size - copied );
        memcpy( &dst[copied], &buf->data[buf->offset], copying );

        if( !has_pts )
        {
            *pts    = buf->s.start;
            *pos    = buf->offset;
            has_pts = 1;
        }

        buf->offset += copying;
        if( buf->offset >= buf->size )
        {
            hb_list_rem( l, buf );
            hb_buffer_close( &buf );
        }

        copied += copying;
    }
}

/**********************************************************************
 * hb_list_empty
 **********************************************************************
 * Assuming all items are of type hb_buffer_t, close them all and
 * close the list.
 *********************************************************************/
void hb_list_empty( hb_list_t ** _l )
{
    hb_list_t * l = *_l;
    hb_buffer_t * b;

    while( ( b = hb_list_item( l, 0 ) ) )
    {
        hb_list_rem( l, b );
        hb_buffer_close( &b );
    }

    hb_list_close( _l );
}

/**********************************************************************
 * hb_list_close
 **********************************************************************
 * Free memory allocated by hb_list_init. Does NOT free contents of
 * items still in the list.
 *********************************************************************/
void hb_list_close( hb_list_t ** _l )
{
    hb_list_t * l = *_l;

    free( l->items );
    free( l );

    *_l = NULL;
}

int global_verbosity_level; //Necessary for hb_deep_log
/**********************************************************************
 * hb_valog
 **********************************************************************
 * If verbose mode is one, print message with timestamp. Messages
 * longer than 180 characters are stripped ;p
 *********************************************************************/
void hb_valog( hb_debug_level_t level, const char * prefix, const char * log, va_list args)
{
    char        string[362]; /* 360 chars + \n + \0 */
    time_t      _now;
    struct tm * now;

    if( !getenv( "HB_DEBUG" ) )
    {
        /* We don't want to print it */
        return;
    }

    if( global_verbosity_level < level )
    {
        /* Hiding message */
        return;
    }

    /* Get the time */
    _now = time( NULL );
    now  = localtime( &_now );
    if ( prefix && *prefix )
    {
        // limit the prefix length
        snprintf( string, 40, "[%02d:%02d:%02d] %s ",
                 now->tm_hour, now->tm_min, now->tm_sec, prefix );
    }
    else
    {
        sprintf( string, "[%02d:%02d:%02d] ",
                 now->tm_hour, now->tm_min, now->tm_sec );
    }
    int end = strlen( string );

    /* Convert the message to a string */
    vsnprintf( string + end, 361 - end, log, args );

    /* Add the end of line */
    strcat( string, "\n" );

    /* Print it */
    fprintf( stderr, "%s", string );
}

/**********************************************************************
 * hb_log
 **********************************************************************
 * If verbose mode is one, print message with timestamp. Messages
 * longer than 180 characters are stripped ;p
 *********************************************************************/
void hb_log( char * log, ... )
{
    va_list     args;

    va_start( args, log );
    hb_valog( 0, NULL, log, args );
    va_end( args );
}

/**********************************************************************
 * hb_deep_log
 **********************************************************************
 * If verbose mode is >= level, print message with timestamp. Messages
 * longer than 360 characters are stripped ;p
 *********************************************************************/
void hb_deep_log( hb_debug_level_t level, char * log, ... )
{
    va_list     args;

    va_start( args, log );
    hb_valog( level, NULL, log, args );
    va_end( args );
}

/**********************************************************************
 * hb_error
 **********************************************************************
 * Using whatever output is available display this error.
 *********************************************************************/
void hb_error( char * log, ... )
{
    char        string[181]; /* 180 chars + \0 */
    char        rep_string[181];
    static char last_string[181];
    static int  last_error_count = 0;
    static uint64_t last_series_error_time = 0;
    static hb_lock_t *mutex = 0;
    va_list     args;
    uint64_t time_now;

    /* Convert the message to a string */
    va_start( args, log );
    vsnprintf( string, 180, log, args );
    va_end( args );

    if( !mutex )
    {
        mutex = hb_lock_init();
    }

    hb_lock( mutex );

    time_now = hb_get_date();

    if( strcmp( string, last_string) == 0 )
    {
        /*
         * The last error and this one are the same, don't log it
         * just count it instead, unless it was more than one second
         * ago.
         */
        last_error_count++;
        if( last_series_error_time + ( 1000 * 1 ) > time_now )
        {
            hb_unlock( mutex );
            return;
        } 
    }
    
    /*
     * A new error, or the same one more than 10sec since the last one
     * did we have any of the same counted up?
     */
    if( last_error_count > 0 )
    {
        /*
         * Print out the last error to ensure context for the last 
         * repeated message.
         */
        if( error_handler )
        {
            error_handler( last_string );
        } else {
            hb_log( "%s", last_string );
        }
        
        if( last_error_count > 1 )
        {
            /*
             * Only print out the repeat message for more than 2 of the
             * same, since we just printed out two of them already.
             */
            snprintf( rep_string, 180, "Last error repeated %d times", 
                      last_error_count - 1 );
            
            if( error_handler )
            {
                error_handler( rep_string );
            } else {
                hb_log( "%s", rep_string );
            }
        }
        
        last_error_count = 0;
    }

    last_series_error_time = time_now;

    strcpy( last_string, string );

    /*
     * Got the error in a single string, send it off to be dispatched.
     */
    if( error_handler )
    {
        error_handler( string );
    } else {
        hb_log( "%s", string );
    }

    hb_unlock( mutex );
}

void hb_register_error_handler( hb_error_handler_t * handler )
{
    error_handler = handler;
}

/**********************************************************************
 * hb_title_init
 **********************************************************************
 *
 *********************************************************************/
hb_title_t * hb_title_init( char * path, int index )
{
    hb_title_t * t;

    t = calloc( sizeof( hb_title_t ), 1 );

    t->index         = index;
    t->playlist      = -1;
    t->list_audio    = hb_list_init();
    t->list_chapter  = hb_list_init();
    t->list_subtitle = hb_list_init();
    t->list_attachment = hb_list_init();
    strcat( t->path, path );
    // default to decoding mpeg2
    t->video_id      = 0xE0;
    t->video_codec   = WORK_DECMPEG2;
    t->angle_count   = 1;
    t->pixel_aspect_width = 1;
    t->pixel_aspect_height = 1;

    return t;
}

/**********************************************************************
 * hb_title_close
 **********************************************************************
 *
 *********************************************************************/
void hb_title_close( hb_title_t ** _t )
{
    hb_title_t * t = *_t;
    hb_audio_t * audio;
    hb_chapter_t * chapter;
    hb_subtitle_t * subtitle;
    hb_attachment_t * attachment;

    while( ( audio = hb_list_item( t->list_audio, 0 ) ) )
    {
        hb_list_rem( t->list_audio, audio );
        free( audio );
    }
    hb_list_close( &t->list_audio );

    while( ( chapter = hb_list_item( t->list_chapter, 0 ) ) )
    {
        hb_list_rem( t->list_chapter, chapter );
        free( chapter );
    }
    hb_list_close( &t->list_chapter );

    while( ( subtitle = hb_list_item( t->list_subtitle, 0 ) ) )
    {
        hb_list_rem( t->list_subtitle, subtitle );
        if ( subtitle->extradata )
        {
            free( subtitle->extradata );
            subtitle->extradata = NULL;
        }
        free( subtitle );
    }
    hb_list_close( &t->list_subtitle );
    
    while( ( attachment = hb_list_item( t->list_attachment, 0 ) ) )
    {
        hb_list_rem( t->list_attachment, attachment );
        if ( attachment->name )
        {
            free( attachment->name );
            attachment->name = NULL;
        }
        if ( attachment->data )
        {
            free( attachment->data );
            attachment->data = NULL;
        }
        free( attachment );
    }
    hb_list_close( &t->list_attachment );

    if( t->metadata )
    {
        if( t->metadata->coverart )
        {
            free( t->metadata->coverart );
        }
        free( t->metadata );
    }

    if ( t->video_codec_name )
    {
        free( t->video_codec_name );
    }

    free( t );
    *_t = NULL;
}

hb_filter_object_t * hb_filter_copy( hb_filter_object_t * filter )
{
    if( filter == NULL )
        return NULL;

    hb_filter_object_t * filter_copy = malloc( sizeof( hb_filter_object_t ) );
    memcpy( filter_copy, filter, sizeof( hb_filter_object_t ) );
    if( filter->settings )
        filter_copy->settings = strdup( filter->settings );
    return filter_copy;
}

/**
 * Gets a filter object with the given type
 * @param filter_id The type of filter to get.
 * @returns The requested filter object.
 */
hb_filter_object_t * hb_filter_init( int filter_id )
{
    hb_filter_object_t * filter;
    switch( filter_id )
    {
        case HB_FILTER_DETELECINE:
            filter = &hb_filter_detelecine;
            break;

        case HB_FILTER_DECOMB:
            filter = &hb_filter_decomb;
            break;

        case HB_FILTER_DEINTERLACE:
            filter = &hb_filter_deinterlace;
            break;

        case HB_FILTER_VFR:
            filter = &hb_filter_vfr;
            break;

        case HB_FILTER_DEBLOCK:
            filter = &hb_filter_deblock;
            break;

        case HB_FILTER_DENOISE:
            filter = &hb_filter_denoise;
            break;

        case HB_FILTER_RENDER_SUB:
            filter = &hb_filter_render_sub;
            break;

        case HB_FILTER_CROP_SCALE:
            filter = &hb_filter_crop_scale;
            break;

        case HB_FILTER_ROTATE:
            filter = &hb_filter_rotate;
            break;

        default:
            filter = NULL;
            break;
    }
    return hb_filter_copy( filter );
}

/**********************************************************************
 * hb_filter_close
 **********************************************************************
 *
 *********************************************************************/
void hb_filter_close( hb_filter_object_t ** _f )
{
    hb_filter_object_t * f = *_f;

    if( f->settings )
        free( f->settings );

    free( f );
    *_f = NULL;
}

/**********************************************************************
 * hb_audio_copy
 **********************************************************************
 *
 *********************************************************************/
hb_audio_t *hb_audio_copy(const hb_audio_t *src)
{
    hb_audio_t *audio = NULL;

    if( src )
    {
        audio = calloc(1, sizeof(*audio));
        memcpy(audio, src, sizeof(*audio));
    }
    return audio;
}

/**********************************************************************
 * hb_audio_new
 **********************************************************************
 *
 *********************************************************************/
void hb_audio_config_init(hb_audio_config_t * audiocfg)
{
    /* Set read-only paramaters to invalid values */
    audiocfg->in.codec = 0;
    audiocfg->in.codec_param = 0;
    audiocfg->in.reg_desc = 0;
    audiocfg->in.stream_type = 0;
    audiocfg->in.substream_type = 0;
    audiocfg->in.version = 0;
    audiocfg->in.flags = 0;
    audiocfg->in.mode = 0;
    audiocfg->in.samplerate = -1;
    audiocfg->in.samples_per_frame = -1;
    audiocfg->in.bitrate = -1;
    audiocfg->in.channel_layout = -1;
    audiocfg->in.channel_map = NULL;
    audiocfg->lang.description[0] = 0;
    audiocfg->lang.simple[0] = 0;
    audiocfg->lang.iso639_2[0] = 0;

    /* Initalize some sensible defaults */
    audiocfg->in.track = audiocfg->out.track = 0;
    audiocfg->out.codec = hb_audio_encoders[0].encoder;
    audiocfg->out.samplerate = -1;
    audiocfg->out.samples_per_frame = -1;
    audiocfg->out.bitrate = -1;
    audiocfg->out.quality = HB_INVALID_AUDIO_QUALITY;
    audiocfg->out.compression_level = -1;
    audiocfg->out.mixdown = HB_INVALID_AMIXDOWN;
    audiocfg->out.dynamic_range_compression = 0;
    audiocfg->out.gain = 0;
    audiocfg->out.normalize_mix_level = 0;
    audiocfg->out.name = NULL;
}

/**********************************************************************
 * hb_audio_add
 **********************************************************************
 *
 *********************************************************************/
int hb_audio_add(const hb_job_t * job, const hb_audio_config_t * audiocfg)
{
    hb_title_t *title = job->title;
    hb_audio_t *audio;

    audio = hb_audio_copy( hb_list_item( title->list_audio, audiocfg->in.track ) );
    if( audio == NULL )
    {
        /* We fail! */
        return 0;
    }

    if( (audiocfg->in.bitrate != -1) && (audiocfg->in.codec != 0xDEADBEEF) )
    {
        /* This most likely means the client didn't call hb_audio_config_init
         * so bail. */
        return 0;
    }

    /* Set the job's "in track" to the value passed in audiocfg.
     * HandBrakeCLI assumes this value is preserved in the jobs
     * audio list, but in.track in the title's audio list is not 
     * required to be the same. */
    audio->config.in.track = audiocfg->in.track;

    /* Really shouldn't ignore the passed out track, but there is currently no
     * way to handle duplicates or out-of-order track numbers. */
    audio->config.out.track = hb_list_count(job->list_audio) + 1;
    audio->config.out.codec = audiocfg->out.codec;
    if((audiocfg->out.codec & HB_ACODEC_PASS_FLAG) &&
       ((audiocfg->out.codec == HB_ACODEC_AUTO_PASS) ||
        (audiocfg->out.codec & audio->config.in.codec & HB_ACODEC_PASS_MASK)))
    {
        /* Pass-through, copy from input. */
        audio->config.out.samplerate = audio->config.in.samplerate;
        audio->config.out.bitrate = audio->config.in.bitrate;
        audio->config.out.mixdown = HB_AMIXDOWN_NONE;
        audio->config.out.dynamic_range_compression = 0;
        audio->config.out.gain = 0;
        audio->config.out.normalize_mix_level = 0;
        audio->config.out.compression_level = -1;
        audio->config.out.quality = HB_INVALID_AUDIO_QUALITY;
    }
    else
    {
        /* Non pass-through, use what is given. */
        audio->config.out.codec &= ~HB_ACODEC_PASS_FLAG;
        audio->config.out.samplerate = audiocfg->out.samplerate;
        audio->config.out.bitrate = audiocfg->out.bitrate;
        audio->config.out.compression_level = audiocfg->out.compression_level;
        audio->config.out.quality = audiocfg->out.quality;
        audio->config.out.dynamic_range_compression = audiocfg->out.dynamic_range_compression;
        audio->config.out.mixdown = audiocfg->out.mixdown;
        audio->config.out.gain = audiocfg->out.gain;
        audio->config.out.normalize_mix_level = audiocfg->out.normalize_mix_level;
    }
    if (audiocfg->out.name && *audiocfg->out.name)
    {
        audio->config.out.name = audiocfg->out.name;
    }

    hb_list_add(job->list_audio, audio);
    return 1;
}

hb_audio_config_t * hb_list_audio_config_item(hb_list_t * list, int i)
{
    hb_audio_t *audio = NULL;

    if( (audio = hb_list_item(list, i)) )
        return &(audio->config);

    return NULL;
}

/**********************************************************************
 * hb_subtitle_copy
 **********************************************************************
 *
 *********************************************************************/
hb_subtitle_t *hb_subtitle_copy(const hb_subtitle_t *src)
{
    hb_subtitle_t *subtitle = NULL;

    if( src )
    {
        subtitle = calloc(1, sizeof(*subtitle));
        memcpy(subtitle, src, sizeof(*subtitle));
        if ( src->extradata )
        {
            subtitle->extradata = malloc( src->extradata_size );
            memcpy( subtitle->extradata, src->extradata, src->extradata_size );
        }
    }
    return subtitle;
}

/**********************************************************************
 * hb_subtitle_add
 **********************************************************************
 *
 *********************************************************************/
int hb_subtitle_add(const hb_job_t * job, const hb_subtitle_config_t * subtitlecfg, int track)
{
    hb_title_t *title = job->title;
    hb_subtitle_t *subtitle;

    subtitle = hb_subtitle_copy( hb_list_item( title->list_subtitle, track ) );
    if( subtitle == NULL )
    {
        /* We fail! */
        return 0;
    }
    subtitle->config = *subtitlecfg;
    subtitle->out_track = hb_list_count(job->list_subtitle) + 1;
    hb_list_add(job->list_subtitle, subtitle);
    return 1;
}

int hb_srt_add( const hb_job_t * job, 
                const hb_subtitle_config_t * subtitlecfg, 
                const char *lang )
{
    hb_subtitle_t *subtitle;
    iso639_lang_t *language = NULL;
    int retval = 0;

    subtitle = calloc( 1, sizeof( *subtitle ) );
    
    subtitle->id = (hb_list_count(job->list_subtitle) << 8) | 0xFF;
    subtitle->format = TEXTSUB;
    subtitle->source = SRTSUB;
    subtitle->codec = WORK_DECSRTSUB;

    language = lang_for_code2( lang );

    if( language )
    {

        strcpy( subtitle->lang, language->eng_name );
        strncpy( subtitle->iso639_2, lang, 4 );
        
        subtitle->config = *subtitlecfg;
        subtitle->config.dest = PASSTHRUSUB;

        hb_list_add(job->list_subtitle, subtitle);
        retval = 1;
    }
    return retval;
}

int hb_subtitle_can_force( int source )
{
    return source == VOBSUB || source == PGSSUB;
}

int hb_subtitle_can_burn( int source )
{
    return source == VOBSUB || source == PGSSUB || source == SSASUB;
}

int hb_subtitle_can_pass( int source, int mux )
{
    if ( mux == HB_MUX_MKV )
    {
        switch( source )
        {
            case PGSSUB:
            case VOBSUB:
            case SSASUB:
            case SRTSUB:
            case UTF8SUB:
            case TX3GSUB:
            case CC608SUB:
            case CC708SUB:
                return 1;

            default:
                return 0;
        }
    }
    else if ( mux == HB_MUX_MP4 )
    {
        switch( source )
        {
            case VOBSUB:
            case SSASUB:
            case SRTSUB:
            case UTF8SUB:
            case TX3GSUB:
            case CC608SUB:
            case CC708SUB:
                return 1;

            default:
                return 0;
        }
    }
    else
    {
        // Internal error. Should never get here.
        hb_error("internel error.  Bad mux %d\n", mux);
        return 0;
    }
}

char * hb_strdup_printf( const char * fmt, ... )
{
    int       len;
    va_list   ap;
    int       size = 256;
    char    * str;
    char    * tmp;

    str = malloc( size );
    if ( str == NULL )
        return NULL;

    while (1) 
    {
        /* Try to print in the allocated space. */
        va_start( ap, fmt );
        len = vsnprintf( str, size, fmt, ap );
        va_end( ap );

        /* If that worked, return the string. */
        if ( len > -1 && len < size )
        {
            return str;
        }

        /* Else try again with more space. */
        if ( len > -1 )     /* glibc 2.1 */
            size = len + 1; /* precisely what is needed */
        else                /* glibc 2.0 */
            size *= 2;      /* twice the old size */
        tmp = realloc( str, size );
        if ( tmp == NULL )
        {
            free( str );
            return NULL;
        }
        else
            str = tmp;
    }
}

char * hb_strncat_dup( const char * s1, const char * s2, size_t n )
{
    size_t len;
    char * str;

    len = 0;
    if( s1 )
        len += strlen( s1 );
    if( s2 )
        len += MAX( strlen( s2 ), n );
    if( !len )
        return NULL;

    str = malloc( len + 1 );
    if( !str )
        return NULL;

    if( s1 )
        strcpy( str, s1 );
    else
        strcpy( str, "" );
    strncat( str, s2, n );
    return str;
}

/**********************************************************************
 * hb_attachment_copy
 **********************************************************************
 *
 *********************************************************************/
hb_attachment_t *hb_attachment_copy(const hb_attachment_t *src)
{
    hb_attachment_t *attachment = NULL;

    if( src )
    {
        attachment = calloc(1, sizeof(*attachment));
        memcpy(attachment, src, sizeof(*attachment));
        if ( src->name )
        {
            attachment->name = strdup( src->name );
        }
        if ( src->data )
        {
            attachment->data = malloc( src->size );
            memcpy( attachment->data, src->data, src->size );
        }
    }
    return attachment;
}

/**********************************************************************
 * hb_yuv2rgb
 **********************************************************************
 * Converts a YCrCb pixel to an RGB pixel.
 * 
 * This conversion is lossy (due to rounding and clamping).
 * 
 * Algorithm:
 *   http://en.wikipedia.org/w/index.php?title=YCbCr&oldid=361987695#Technical_details
 *********************************************************************/
int hb_yuv2rgb(int yuv)
{
    double y, Cr, Cb;
    int r, g, b;

    y  = (yuv >> 16) & 0xff;
    Cr = (yuv >>  8) & 0xff;
    Cb = (yuv      ) & 0xff;

    r = 1.164 * (y - 16)                      + 1.596 * (Cr - 128);
    g = 1.164 * (y - 16) - 0.392 * (Cb - 128) - 0.813 * (Cr - 128);
    b = 1.164 * (y - 16) + 2.017 * (Cb - 128);
    
    r = (r < 0) ? 0 : r;
    g = (g < 0) ? 0 : g;
    b = (b < 0) ? 0 : b;
    
    r = (r > 255) ? 255 : r;
    g = (g > 255) ? 255 : g;
    b = (b > 255) ? 255 : b;
    
    return (r << 16) | (g << 8) | b;
}

/**********************************************************************
 * hb_rgb2yuv
 **********************************************************************
 * Converts an RGB pixel to a YCrCb pixel.
 * 
 * This conversion is lossy (due to rounding and clamping).
 * 
 * Algorithm:
 *   http://en.wikipedia.org/w/index.php?title=YCbCr&oldid=361987695#Technical_details
 *********************************************************************/
int hb_rgb2yuv(int rgb)
{
    double r, g, b;
    int y, Cr, Cb;
    
    r = (rgb >> 16) & 0xff;
    g = (rgb >>  8) & 0xff;
    b = (rgb      ) & 0xff;

    y  =  16. + ( 0.257 * r) + (0.504 * g) + (0.098 * b);
    Cb = 128. + (-0.148 * r) - (0.291 * g) + (0.439 * b);
    Cr = 128. + ( 0.439 * r) - (0.368 * g) - (0.071 * b);
    
    y = (y < 0) ? 0 : y;
    Cb = (Cb < 0) ? 0 : Cb;
    Cr = (Cr < 0) ? 0 : Cr;
    
    y = (y > 255) ? 255 : y;
    Cb = (Cb > 255) ? 255 : Cb;
    Cr = (Cr > 255) ? 255 : Cr;
    
    return (y << 16) | (Cr << 8) | Cb;
}

const char * hb_subsource_name( int source )
{
    switch (source)
    {
        case VOBSUB:
            return "VOBSUB";
        case SRTSUB:
            return "SRT";
        case CC608SUB:
            return "CC";
        case CC708SUB:
            return "CC";
        case UTF8SUB:
            return "UTF-8";
        case TX3GSUB:
            return "TX3G";
        case SSASUB:
            return "SSA";
        case PGSSUB:
            return "PGS";
        default:
            return "Unknown";
    }
}

void hb_hexdump( hb_debug_level_t level, const char * label, const uint8_t * data, int len )
{
    int ii;
    char line[80], ascii[19], *p;

    ascii[18] = 0;
    ascii[0] = '|';
    ascii[17] = '|';
    memset(&ascii[1], '.', 16);
    p = line;
    if( label )
        hb_deep_log(level, "++++ %s ++++", label);
    else
        hb_deep_log(level, "++++++++++++");
    for( ii = 0; ii < len; ii++ )
    {
        if( ( ii & 0x0f ) == 0x0f )
        {
            p += sprintf( p, "%02x", data[ii] );
            hb_deep_log( level, "    %-50s%20s", line, ascii );
            memset(&ascii[1], '.', 16);
            p = line;
        }
        else if( ( ii & 0x07 ) == 0x07 )
        {
            p += sprintf( p, "%02x  ", data[ii] );
        }
        else
        {
            p += sprintf( p, "%02x ", data[ii] );
        }
        if( isgraph( data[ii] ) )
            ascii[(ii & 0x0f) + 1] = data[ii];
        else
            ascii[(ii & 0x0f) + 1] = '.';
    }
    ascii[ii] = 0;
    if( p != line )
    {
        hb_deep_log( level, "    %-50s%20s", line, ascii );
    }
}