OSDN Git Service

CLI: Use default audio settings of 160kbps @ 48kHz, just like the MacGui and WinGui.
[handbrake-jp/handbrake-jp-git.git] / test / test.c
index daccad6..f4e4154 100644 (file)
@@ -1,7 +1,7 @@
 /* $Id: test.c,v 1.82 2005/11/19 08:25:54 titer Exp $
 
    This file is part of the HandBrake source code.
-   Homepage: <http://handbrake.m0k.org/>.
+   Homepage: <http://handbrake.fr/>.
    It may be used under the terms of the GNU General Public License. */
 
 #include <signal.h>
 #include "hb.h"
 #include "parsecsv.h"
 
+#ifdef __APPLE_CC__
+#import <CoreServices/CoreServices.h>
+#include <IOKit/IOKitLib.h>
+#include <IOKit/storage/IOMedia.h>
+#include <IOKit/storage/IODVDMedia.h>
+#endif
+
 /* Options */
 static int    debug       = HB_DEBUG_NONE;
 static int    update      = 0;
@@ -33,29 +40,38 @@ static int    denoise               = 0;
 static char * denoise_opt           = 0;
 static int    detelecine            = 0;
 static char * detelecine_opt        = 0;
+static int    decomb                = 0;
+static char * decomb_opt            = 0;
 static int    grayscale   = 0;
 static int    vcodec      = HB_VCODEC_FFMPEG;
 static int    h264_13     = 0;
 static int    h264_30     = 0;
-static char * audios      = NULL;
-static int    audio_mixdown = HB_AMIXDOWN_DOLBYPLII;
-static float  dynamic_range_compression = 0;
+static hb_list_t * audios = NULL;
+static hb_audio_config_t * audio = NULL;
+static int    num_audio_tracks = 0;
+static char * mixdowns    = NULL;
+static char * dynamic_range_compression = NULL;
+static char * arates      = NULL;
+static char * abitrates   = NULL;
+static char * acodecs     = NULL;
+static int    default_acodec = HB_ACODEC_FAAC;
+static int    default_arate = 48000;
+static int    default_abitrate = 160;
 static int    sub         = 0;
 static int    width       = 0;
 static int    height      = 0;
 static int    crop[4]     = { -1,-1,-1,-1 };
 static int    cpu         = 0;
 static int    vrate       = 0;
-static int    arate       = 0;
 static float  vquality    = -1.0;
 static int    vbitrate    = 0;
 static int    size        = 0;
-static int    abitrate    = 0;
 static int    mux         = 0;
-static int    acodec      = 0;
 static int    pixelratio  = 0;
 static int    loosePixelratio = 0;
 static int    modulus       = 0;
+static int    par_height    = 0;
+static int    par_width     = 0;
 static int    chapter_start = 0;
 static int    chapter_end   = 0;
 static int    chapter_markers = 0;
@@ -87,6 +103,17 @@ static int  ParseOptions( int argc, char ** argv );
 static int  CheckOptions( int argc, char ** argv );
 static int  HandleEvents( hb_handle_t * h );
 
+static int get_acodec_for_string( char *codec );
+static int is_sample_rate_valid(int rate);
+
+#ifdef __APPLE_CC__
+static char* bsd_name_for_path(char *path);
+static int device_is_dvd(char *device);
+static io_service_t get_iokit_service( char *device );
+static int is_dvd_service( io_service_t service );
+static is_whole_media_service( io_service_t service );
+#endif
+
 /* Only print the "Muxing..." message once */
 static int show_mux_warning = 1;
 
@@ -107,6 +134,8 @@ int main( int argc, char ** argv )
     int           build;
     char        * version;
 
+    audios = hb_list_init();
+
     /* Parse command line */
     if( ParseOptions( argc, argv ) ||
         CheckOptions( argc, argv ) )
@@ -121,7 +150,7 @@ int main( int argc, char ** argv )
     h = hb_init( debug, update );
 
     /* Show version */
-    fprintf( stderr, "HandBrake %s (%d) - http://handbrake.m0k.org/\n",
+    fprintf( stderr, "HandBrake %s (%d) - http://handbrake.fr/\n",
              hb_get_version( h ), hb_get_build( h ) );
 
     /* Check for update */
@@ -230,7 +259,20 @@ int main( int argc, char ** argv )
     if( input )  free( input );
     if( output ) free( output );
     if( format ) free( format );
-    if( audios ) free( audios );
+    if( audios )
+    {
+        while( ( audio = hb_list_item( audios, 0 ) ) )
+        {
+            hb_list_rem( audios, audio );
+            free( audio );
+        }
+        hb_list_close( &audios );
+    }
+    if( mixdowns ) free( mixdowns );
+    if( dynamic_range_compression ) free( dynamic_range_compression );
+    if( arates ) free( arates );
+    if( abitrates ) free( abitrates );
+    if( acodecs ) free( acodecs );
     if (native_language ) free (native_language );
        if( x264opts ) free (x264opts );
        if( x264opts2 ) free (x264opts2 );
@@ -253,7 +295,7 @@ static void ShowCommands()
 static void PrintTitleInfo( hb_title_t * title )
 {
     hb_chapter_t  * chapter;
-    hb_audio_t    * audio;
+    hb_audio_config_t    * audio;
     hb_subtitle_t * subtitle;
     int i;
 
@@ -282,15 +324,15 @@ static void PrintTitleInfo( hb_title_t * title )
     fprintf( stderr, "  + audio tracks:\n" );
     for( i = 0; i < hb_list_count( title->list_audio ); i++ )
     {
-        audio = hb_list_item( title->list_audio, i );
-        if( ( audio->codec & HB_ACODEC_AC3 ) || ( audio->codec & HB_ACODEC_DCA) )
+        audio = hb_list_audio_config_item( title->list_audio, i );
+        if( ( audio->in.codec == HB_ACODEC_AC3 ) || ( audio->in.codec == HB_ACODEC_DCA) )
         {
             fprintf( stderr, "    + %d, %s, %dHz, %dbps\n", i + 1,
-                     audio->lang, audio->rate, audio->bitrate );
+                     audio->lang.description, audio->in.samplerate, audio->in.bitrate );
         }
         else
         {
-            fprintf( stderr, "    + %d, %s\n", i + 1, audio->lang );
+            fprintf( stderr, "    + %d, %s\n", i + 1, audio->lang.description );
         }
     }
     fprintf( stderr, "  + subtitle tracks:\n" );
@@ -300,6 +342,13 @@ static void PrintTitleInfo( hb_title_t * title )
         fprintf( stderr, "    + %d, %s (iso639-2: %s)\n", i + 1, subtitle->lang,
             subtitle->iso639_2);
     }
+
+    if(title->detected_interlacing)
+    {
+        /* Interlacing was found in half or more of the preview frames */
+        fprintf( stderr, "  + combing detected, may be interlaced or telecined\n");
+    }
+
 }
 
 static int HandleEvents( hb_handle_t * h )
@@ -327,6 +376,15 @@ static int HandleEvents( hb_handle_t * h )
             hb_list_t  * list;
             hb_title_t * title;
             hb_job_t   * job;
+            int i;
+
+            /* Audio argument string parsing variables */
+            int acodec = 0;
+            int abitrate = 0;
+            int arate = 0;
+            int mixdown = HB_AMIXDOWN_DOLBYPLII;
+            double d_r_c = 0;
+            /* Audio argument string parsing variables */
 
             list = hb_get_titles( h );
 
@@ -412,9 +470,9 @@ static int HandleEvents( hb_handle_t * h )
                     mux = HB_MUX_MKV;
                     vcodec = HB_VCODEC_X264;
                     job->vbitrate = 1000;
-                    job->abitrate = 160;
-                    job->arate = 48000;
-                    acodec = HB_ACODEC_FAAC;
+                    default_abitrate = 160;
+                    default_arate = 48000;
+                    default_acodec = HB_ACODEC_FAAC;
                     x264opts = strdup("ref=5:mixed-refs:bframes=6:bime:weightb:b-rdo:direct=auto:b-pyramid:me=umh:subme=5:analyse=all:8x8dct:trellis=1:nr=150:no-fast-pskip:filter=2,2");
                     deinterlace = 1;
                     deinterlace_opt = "0";
@@ -426,15 +484,18 @@ static int HandleEvents( hb_handle_t * h )
 
                 if (!strcmp(preset_name, "AppleTV"))
                 {
+                    if (!acodecs)
+                    {
+                        acodecs = strdup("faac,ac3");
+                    }
+
                     mux = HB_MUX_MP4;
                     job->largeFileSize = 1;
                     vcodec = HB_VCODEC_X264;
                     job->vbitrate = 2500;
-                    job->abitrate = 160;
-                    job->arate = 48000;
-                    acodec = HB_ACODEC_FAAC;
-                    audio_mixdown = HB_AMIXDOWN_DOLBYPLII_AC3;
-                    arate = 48000;
+                    default_abitrate = 160;
+                    default_arate = 48000;
+                    default_acodec = HB_ACODEC_FAAC;
                     x264opts = strdup("bframes=3:ref=1:subme=5:me=umh:no-fast-pskip=1:trellis=1:cabac=0");
                     job->chapter_markers = 1;
                     pixelratio = 1;
@@ -445,7 +506,7 @@ static int HandleEvents( hb_handle_t * h )
                     mux = HB_MUX_MKV;
                     vcodec = HB_VCODEC_X264;
                     job->vbitrate = 1800;
-                    acodec = HB_ACODEC_AC3;
+                    default_acodec = HB_ACODEC_AC3;
                     x264opts = strdup("ref=16:mixed-refs:bframes=16:bime:weightb:b-rdo:direct=auto:b-pyramid:me=esa:subme=7:me-range=64:analyse=all:8x8dct:trellis=1:no-fast-pskip:no-dct-decimate:filter=-2,-1");
                     job->chapter_markers = 1;
                     pixelratio = 1;
@@ -457,9 +518,9 @@ static int HandleEvents( hb_handle_t * h )
                 {
                     mux = HB_MUX_MP4;
                     job->vbitrate = 512;
-                    job->abitrate = 128;
-                    job->arate = 48000;
-                    acodec = HB_ACODEC_FAAC;
+                    default_abitrate = 128;
+                    default_arate = 48000;
+                    default_acodec = HB_ACODEC_FAAC;
                     job->width = 512;
                     job->chapter_markers = 1;
                 }
@@ -469,9 +530,9 @@ static int HandleEvents( hb_handle_t * h )
                     mux = HB_MUX_MP4;
                     vcodec = HB_VCODEC_X264;
                     size = 695;
-                    job->abitrate = 128;
-                    job->arate = 48000;
-                    acodec = HB_ACODEC_FAAC;
+                    default_abitrate = 128;
+                    default_arate = 48000;
+                    default_acodec = HB_ACODEC_FAAC;
                     job->width = 640;
                     x264opts = strdup("ref=3:mixed-refs:bframes=16:bime:weightb:b-rdo:b-pyramid:direct=auto:me=umh:subme=6:trellis=1:analyse=all:8x8dct:no-fast-pskip");
                     job->chapter_markers = 1;
@@ -483,9 +544,9 @@ static int HandleEvents( hb_handle_t * h )
                 {
                     mux = HB_MUX_MP4;
                     job->vbitrate = 1000;
-                    job->abitrate = 160;
-                    job->arate = 48000;
-                    acodec = HB_ACODEC_FAAC;
+                    default_abitrate = 160;
+                    default_arate = 48000;
+                    default_acodec = HB_ACODEC_FAAC;
                 }
 
                 if (!strcmp(preset_name, "Constant Quality Rate"))
@@ -494,7 +555,7 @@ static int HandleEvents( hb_handle_t * h )
                     vcodec = HB_VCODEC_X264;
                     job->vquality = 0.64709997177124023;
                     job->crf = 1;
-                    acodec = HB_ACODEC_AC3;
+                    default_acodec = HB_ACODEC_AC3;
                     x264opts = strdup("ref=3:mixed-refs:bframes=3:b-pyramid:b-rdo:bime:weightb:filter=-2,-1:subme=6:trellis=1:analyse=all:8x8dct:me=umh");
                     job->chapter_markers = 1;
                     pixelratio = 1;
@@ -505,7 +566,7 @@ static int HandleEvents( hb_handle_t * h )
                     mux = HB_MUX_MKV;
                     vcodec = HB_VCODEC_X264;
                     job->vbitrate = 1600;
-                    acodec = HB_ACODEC_AC3;
+                    default_acodec = HB_ACODEC_AC3;
                     x264opts = strdup("ref=5:mixed-refs:bframes=3:bime:weightb:b-rdo:b-pyramid:me=umh:subme=7:trellis=1:analyse=all:8x8dct:no-fast-pskip");
                     job->chapter_markers = 1;
                     pixelratio = 1;
@@ -518,7 +579,7 @@ static int HandleEvents( hb_handle_t * h )
                     mux = HB_MUX_MKV;
                     vcodec = HB_VCODEC_X264;
                     job->vbitrate = 1800;
-                    acodec = HB_ACODEC_AC3;
+                    default_acodec = HB_ACODEC_AC3;
                     x264opts = strdup("ref=3:mixed-refs:bframes=6:bime:weightb:b-rdo:direct=auto:b-pyramid:me=umh:subme=7:analyse=all:8x8dct:trellis=1:no-fast-pskip");
                     job->chapter_markers = 1;
                     pixelratio = 1;
@@ -532,9 +593,9 @@ static int HandleEvents( hb_handle_t * h )
                     job->ipod_atom = 1;
                     vcodec = HB_VCODEC_X264;
                     job->vbitrate = 960;
-                    job->abitrate = 128;
-                    job->arate = 48000;
-                    acodec = HB_ACODEC_FAAC;
+                    default_abitrate = 128;
+                    default_arate = 48000;
+                    default_acodec = HB_ACODEC_FAAC;
                     job->width = 480;
                     x264opts = strdup("level=30:cabac=0:ref=1:analyse=all:me=umh:subme=6:no-fast-pskip=1:trellis=1");
                     job->chapter_markers = 1;
@@ -546,9 +607,9 @@ static int HandleEvents( hb_handle_t * h )
                     job->ipod_atom = 1;
                     vcodec = HB_VCODEC_X264;
                     job->vbitrate = 1500;
-                    job->abitrate = 160;
-                    job->arate = 48000;
-                    acodec = HB_ACODEC_FAAC;
+                    default_abitrate = 160;
+                    default_arate = 48000;
+                    default_acodec = HB_ACODEC_FAAC;
                     job->width = 640;
                     x264opts = strdup("level=30:bframes=0:cabac=0:ref=1:vbv-maxrate=1500:vbv-bufsize=2000:analyse=all:me=umh:subme=6:no-fast-pskip=1");
                     job->chapter_markers = 1;
@@ -560,9 +621,9 @@ static int HandleEvents( hb_handle_t * h )
                     job->ipod_atom = 1;
                     vcodec = HB_VCODEC_X264;
                     job->vbitrate = 700;
-                    job->abitrate = 160;
-                    job->arate = 48000;
-                    acodec = HB_ACODEC_FAAC;
+                    default_abitrate = 160;
+                    default_arate = 48000;
+                    default_acodec = HB_ACODEC_FAAC;
                     job->width = 320;
                     x264opts = strdup("level=30:bframes=0:cabac=0:ref=1:vbv-maxrate=768:vbv-bufsize=2000:analyse=all:me=umh:subme=6:no-fast-pskip=1");
                     job->chapter_markers = 1;
@@ -573,9 +634,9 @@ static int HandleEvents( hb_handle_t * h )
                     mux = HB_MUX_MP4;
                     vcodec = HB_VCODEC_X264;
                     job->vbitrate = 1500;
-                    job->abitrate = 160;
-                    job->arate = 48000;
-                    acodec = HB_ACODEC_FAAC;
+                    default_abitrate = 160;
+                    default_arate = 48000;
+                    default_acodec = HB_ACODEC_FAAC;
                     x264opts = strdup("ref=2:bframes=2:subme=5:me=umh");
                     job->chapter_markers = 1;
                     pixelratio = 1;
@@ -588,9 +649,9 @@ static int HandleEvents( hb_handle_t * h )
                     mux = HB_MUX_MP4;
                     vcodec = HB_VCODEC_X264;
                     job->vbitrate = 2500;
-                    job->abitrate = 160;
-                    job->arate = 48000;
-                    acodec = HB_ACODEC_FAAC;
+                    default_abitrate = 160;
+                    default_arate = 48000;
+                    default_acodec = HB_ACODEC_FAAC;
                     x264opts = strdup("level=41:subme=5:me=umh");
                     pixelratio = 1;
                 }
@@ -599,9 +660,9 @@ static int HandleEvents( hb_handle_t * h )
                 {
                     mux = HB_MUX_MP4;
                     job->vbitrate = 1024;
-                    job->abitrate = 128;
-                    job->arate = 48000;
-                    acodec = HB_ACODEC_FAAC;
+                    default_abitrate = 128;
+                    default_arate = 48000;
+                    default_acodec = HB_ACODEC_FAAC;
                     job->width = 368;
                     job->height = 208;
                     job->chapter_markers = 1;
@@ -612,9 +673,9 @@ static int HandleEvents( hb_handle_t * h )
                     mux = HB_MUX_MP4;
                     vcodec = HB_VCODEC_X264;
                     job->vbitrate = 2000;
-                    job->abitrate = 160;
-                    job->arate = 48000;
-                    acodec = HB_ACODEC_FAAC;
+                    default_abitrate = 160;
+                    default_arate = 48000;
+                    default_acodec = HB_ACODEC_FAAC;
                     x264opts = strdup("ref=3:mixed-refs:bframes=3:bime:weightb:b-rdo:direct=auto:me=umh:subme=5:analyse=all:trellis=1:no-fast-pskip");
                     job->chapter_markers = 1;
                     pixelratio = 1;
@@ -627,9 +688,9 @@ static int HandleEvents( hb_handle_t * h )
                     mux = HB_MUX_MKV;
                     vcodec = HB_VCODEC_X264;
                     job->vbitrate = 1300;
-                    job->abitrate = 160;
-                    job->arate = 48000;
-                    acodec = HB_ACODEC_FAAC;
+                    default_abitrate = 160;
+                    default_arate = 48000;
+                    default_acodec = HB_ACODEC_FAAC;
                     x264opts = strdup("ref=3:mixed-refs:bframes=6:bime:weightb:direct=auto:b-pyramid:me=umh:subme=6:analyse=all:8x8dct:trellis=1:nr=150:no-fast-pskip");
                     deinterlace = 1;
                     deinterlace_opt = "0";
@@ -645,9 +706,9 @@ static int HandleEvents( hb_handle_t * h )
                     mux = HB_MUX_MP4;
                     vcodec = HB_VCODEC_X264;
                     job->vbitrate = 2000;
-                    job->abitrate = 160;
-                    job->arate = 48000;
-                    acodec = HB_ACODEC_FAAC;
+                    default_abitrate = 160;
+                    default_arate = 48000;
+                    default_acodec = HB_ACODEC_FAAC;
                     x264opts = strdup("level=40:ref=2:mixed-refs:bframes=3:bime:weightb:b-rdo:direct=auto:b-pyramid:me=umh:subme=5:analyse=all:no-fast-pskip:filter=-2,-1");
                     pixelratio = 1;
                 }
@@ -703,6 +764,21 @@ static int HandleEvents( hb_handle_t * h )
                         hb_close_csv_file( file );
                     }
                 }
+                else
+                {
+                    /* No marker file */
+                    
+                    int number_of_chapters = hb_list_count(job->title->list_chapter);
+                    int chapter;
+                    
+                    for(chapter = 0; chapter <= number_of_chapters - 1 ; chapter++)
+                    {                        
+                        hb_chapter_t * chapter_s;
+                        chapter_s = hb_list_item( job->title->list_chapter, chapter);
+                        snprintf( chapter_s->title, 1023, "Chapter %i", chapter + 1 );
+                        chapter_s->title[1023] = '\0';
+                    }
+                }
                        }
 
             if( crop[0] >= 0 && crop[1] >= 0 &&
@@ -720,11 +796,24 @@ static int HandleEvents( hb_handle_t * h )
                 {
                     job->modulus = modulus;
                 }
+                if( par_width && par_height )
+                {
+                    job->pixel_ratio = 3;
+                    job->pixel_aspect_width = par_width;
+                    job->pixel_aspect_height = par_height;
+                }
             }
             else
             {
                 job->pixel_ratio = pixelratio;
             }
+
+            if (vfr)
+            {
+                detelecine = 1;
+                job->vfr = 1;
+            }
+
             /* Add selected filters */
             job->filters = hb_list_init();
             if( detelecine )
@@ -732,6 +821,11 @@ static int HandleEvents( hb_handle_t * h )
                 hb_filter_detelecine.settings = detelecine_opt;
                 hb_list_add( job->filters, &hb_filter_detelecine );
             }
+            if( decomb )
+            {
+                hb_filter_decomb.settings = decomb_opt;
+                hb_list_add( job->filters, &hb_filter_decomb );
+            }
             if( deinterlace )
             {
                 hb_filter_deinterlace.settings = deinterlace_opt;
@@ -775,7 +869,7 @@ static int HandleEvents( hb_handle_t * h )
                 hb_fix_aspect( job, HB_KEEP_WIDTH );
             }
 
-            if( vquality >= 0.0 && vquality <= 1.0 )
+            if( vquality >= 0.0 && ( ( vquality <= 1.0 ) || ( vcodec == HB_VCODEC_X264 ) ) )
             {
                 job->vquality = vquality;
                 job->vbitrate = 0;
@@ -802,74 +896,259 @@ static int HandleEvents( hb_handle_t * h )
                 job->vrate = 27000000;
                 job->vrate_base = vrate;
             }
-            if( arate )
+
+            /* Parse audio tracks */
+            if( hb_list_count(audios) == 0 )
             {
-                job->arate = arate;
+                /* Create a new audio track with default settings */
+                audio = calloc(1, sizeof(*audio));
+                hb_audio_config_init(audio);
+                /* Add it to our audios */
+                hb_list_add(audios, audio);
+            }
+
+            num_audio_tracks = hb_list_count(audios);
+            for (i = 0; i < num_audio_tracks; i++)
+            {
+                audio = hb_list_item(audios, 0);
+                if( (audio == NULL) || (audio->in.track == -1) ||
+                    (audio->out.track == -1) || (audio->out.codec == 0) )
+                {
+                    num_audio_tracks--;
+                }
+                else
+                {
+                    hb_audio_add( job, audio );
+                }
+                hb_list_rem(audios, audio);
+                if( audio != NULL)
+                    free( audio );
             }
+            /* Audio Tracks */
 
-            if( audios )
+            /* Audio Codecs */
+            i = 0;
+            if( acodecs )
             {
-                if( strcasecmp( audios, "none" ) )
+                char * token = strtok(acodecs, ",");
+                if( token == NULL )
+                    token = acodecs;
+                while ( token != NULL )
                 {
-                    int    audio_count = 0;
-                    char * tmp         = audios;
-                    while( *tmp )
+                    if ((acodec = get_acodec_for_string(token)) == -1)
                     {
-                        if( *tmp < '0' || *tmp > '9' )
+                        fprintf(stderr, "Invalid codec %s, using default for container.\n", token);
+                        acodec = default_acodec;
+                    }
+                    if( i < num_audio_tracks )
+                    {
+                        audio = hb_list_audio_config_item(job->list_audio, i);                        
+                        audio->out.codec = acodec;
+                    }
+                    if( i >= num_audio_tracks )
+                    {
+                        int last_track = hb_list_count(job->list_audio);
+                        fprintf(stderr, "More audio codecs than audio tracks, copying track %i and using encoder %s\n",
+                            last_track, token);
+                        hb_audio_config_t * last_audio = hb_list_audio_config_item( job->list_audio, last_track - 1 );
+                        audio = calloc(1, sizeof(*audio));
+                        hb_audio_config_init(audio);
+                        audio->in.track = last_audio->in.track;
+                        audio->out.track = num_audio_tracks++;
+                        audio->out.codec = acodec;
+                        hb_audio_add(job, audio);
+                        free( audio );
+                    }
+                    token = strtok(NULL, ",");
+                    i++;
+                }
+            }
+            if( i < num_audio_tracks )
+            {
+                /* We have fewer inputs than audio tracks, use the default codec for
+                 * this container for the remaining tracks. Unless we only have one input
+                 * then use that codec instead.
+                 */
+                if (i != 1)
+                    acodec = default_acodec;
+                for ( ; i < num_audio_tracks; i++)
+                {
+                    audio = hb_list_audio_config_item(job->list_audio, i);
+                    audio->out.codec = acodec;
+                }
+            }
+            /* Audio Codecs */
+
+            /* Sample Rate */
+            i = 0;
+            if( arates )
+            {
+                char * token = strtok(arates, ",");
+                if (token == NULL)
+                    token = arates;
+                while ( token != NULL )
+                {
+                    arate = atoi(token);
+                    audio = hb_list_audio_config_item(job->list_audio, i);
+                    if (audio->out.codec == HB_ACODEC_AC3 || audio->out.codec == HB_ACODEC_DCA)
+                    {
+                        /* Would probably be better to have this handled in libhb. */
+                        audio->out.samplerate = audio->in.samplerate;
+                    }
+                    else
+                    {
+                        int j;
+                        for( j = 0; j < hb_audio_rates_count; j++ )
+                        {
+                            if( !strcmp( token, hb_audio_rates[j].string ) )
+                            {
+                                arate = hb_audio_rates[j].rate;
+                                break;
+                            }
+                        }
+                        
+                        if (!is_sample_rate_valid(arate))
                         {
-                            /* Skip non numeric char */
-                            tmp++;
-                            continue;
+                            fprintf(stderr, "Invalid sample rate %d, using default %d\n", arate, default_arate);
+                            arate = default_arate;
                         }
-                                               job->audio_mixdowns[audio_count] = audio_mixdown;
-                        job->audios[audio_count++] =
-                            strtol( tmp, &tmp, 0 ) - 1;
+                        
+                        audio->out.samplerate = arate;
                     }
-                    job->audios[audio_count] = -1;
+                    if( (++i) >= num_audio_tracks )
+                        break;  /* We have more inputs than audio tracks, oops */
+                    token = strtok(NULL, ",");
                 }
-                else
+            }
+            if (i < num_audio_tracks)
+            {
+                /* We have fewer inputs than audio tracks, use default sample rate.
+                 * Unless we only have one input, then use that for all tracks.
+                 */
+                if (i != 1)
+                    arate = default_arate;
+                for ( ; i < num_audio_tracks; i++)
                 {
-                    job->audios[0] = -1;
+                    audio = hb_list_audio_config_item(job->list_audio, i);
+                    if (audio->out.codec == HB_ACODEC_AC3 || audio->out.codec == HB_ACODEC_DCA)
+                    {
+                        audio->out.samplerate = audio->in.samplerate;
+                        continue;
+                    }
+                    audio->out.samplerate = arate;
                 }
             }
-                       else
-                       {
-                           /* default to the first audio track if none has been specified */
-                           job->audios[0] = 0;
-                           job->audio_mixdowns[0] = audio_mixdown;
-                       }
+            /* Sample Rate */
 
-                       if( audio_mixdown == HB_AMIXDOWN_DOLBYPLII_AC3)
-                       {
-               int i;
-               for( i = 3 ; i > 0; i--)
-               {
-                   job->audios[i*2+1] = job->audios[i];
-                   job->audios[i*2] = job->audios[i];
-                   if(job->audios[i] != -1  )
-                   {
-                       job->audio_mixdowns[i*2+1] = HB_AMIXDOWN_AC3;
-                       job->audio_mixdowns[i*2] = HB_AMIXDOWN_DOLBYPLII;
-                   }
-               }
-
-               job->audios[1] = job->audios[0];
-               job->audio_mixdowns[1] = HB_AMIXDOWN_AC3;
-               job->audio_mixdowns[0] = HB_AMIXDOWN_DOLBYPLII;
-            }
-
-            if( abitrate )
+            /* Audio Bitrate */
+            i = 0;
+            if( abitrates )
             {
-                job->abitrate = abitrate;
+                char * token = strtok(abitrates, ",");
+                if (token == NULL)
+                    token = abitrates;
+                while ( token != NULL )
+                {
+                    abitrate = atoi(token);
+                    audio = hb_list_audio_config_item(job->list_audio, i);
+                    if (audio->out.codec == HB_ACODEC_AC3 || audio->out.codec == HB_ACODEC_DCA)
+                    {
+                        audio->out.bitrate = audio->in.bitrate;   /* See note above about arate. */
+                    }
+                    else
+                    {
+                        audio->out.bitrate = abitrate;
+                    }
+                    if( (++i) >= num_audio_tracks )
+                        break;  /* We have more inputs than audio tracks, oops */
+                    token = strtok(NULL, ",");
+                }
             }
-            if( acodec )
+            if (i < num_audio_tracks)
             {
-                job->acodec = acodec;
+                /* We have fewer inputs than audio tracks, use the default bitrate
+                 * for the remaining tracks. Unless we only have one input, then use
+                 * that for all tracks.
+                 */
+                if (i != 1)
+                    abitrate = default_abitrate;
+                for (; i < num_audio_tracks; i++)
+                {
+                    audio = hb_list_audio_config_item(job->list_audio, i);
+                    if (audio->out.codec == HB_ACODEC_AC3 || audio->out.codec == HB_ACODEC_DCA)
+                    {
+                        audio->out.bitrate = audio->in.bitrate;
+                        continue;
+                    }
+                    audio->out.bitrate = abitrate;
+                }
             }
+            /* Audio Bitrate */
+
+            /* Audio DRC */
+            i = 0;
             if ( dynamic_range_compression )
             {
-                job->dynamic_range_compression = dynamic_range_compression;
+                char * token = strtok(dynamic_range_compression, ",");
+                if (token == NULL)
+                    token = dynamic_range_compression;
+                while ( token != NULL )
+                {
+                    d_r_c = atof(token);
+                    audio = hb_list_audio_config_item(job->list_audio, i);
+                    audio->out.dynamic_range_compression = d_r_c;
+                    if( (++i) >= num_audio_tracks )
+                        break;  /* We have more inputs than audio tracks, oops */
+                    token = strtok(NULL, ",");
+                }
+            }
+            if (i < num_audio_tracks)
+            {
+                /* We have fewer inputs than audio tracks, use no DRC for the remaining
+                 * tracks. Unless we only have one input, then use the same DRC for all
+                 * tracks.
+                 */
+                if (i != 1)
+                    d_r_c = 0;
+                for (; i < num_audio_tracks; i++)
+                {
+                    audio = hb_list_audio_config_item(job->list_audio, i);
+                    audio->out.dynamic_range_compression = d_r_c;
+                }
+            }
+            /* Audio DRC */
+
+            /* Audio Mixdown */
+            i = 0;
+            if ( mixdowns )
+            {
+                char * token = strtok(mixdowns, ",");
+                if (token == NULL)
+                    token = mixdowns;
+                while ( token != NULL )
+                {
+                    mixdown = hb_mixdown_get_mixdown_from_short_name(token);
+                    audio = hb_list_audio_config_item(job->list_audio, i);
+                    audio->out.mixdown = mixdown;
+                    if( (++i) >= num_audio_tracks )
+                        break;  /* We have more inputs than audio tracks, oops */
+                    token = strtok(NULL, ",");
+                }
+            }
+            if (i < num_audio_tracks)
+            {
+                /* We have fewer inputs than audio tracks, use DPLII for the rest. Unless
+                 * we only have one input, then use that.
+                 */
+                if (i != 1)
+                    mixdown = HB_AMIXDOWN_DOLBYPLII;
+                for (; i < num_audio_tracks; i++)
+                {
+                   audio = hb_list_audio_config_item(job->list_audio, i);
+                   audio->out.mixdown = mixdown;
+                }
             }
+            /* Audio Mixdown */
 
             if( size )
             {
@@ -926,9 +1205,6 @@ static int HandleEvents( hb_handle_t * h )
             if (maxHeight)
                 job->maxHeight = maxHeight;
 
-            if (vfr)
-                job->vfr = 1;
-
             if( subtitle_force )
             {
                 job->subtitle_force = subtitle_force;
@@ -1205,29 +1481,35 @@ static void ShowHelp()
      "          <weak/medium/strong>\n"
      "    -9, --detelecine        Detelecine video with pullup filter\n"
      "          <L:R:T:B:SB:MP>   (default 1:1:4:4:0:0)\n"
+     "    -5, --decomb           Selectively deinterlaces when it detects combing\n"
+     "          <M:EQ:DF:TR:EQ:DF:TR>     (default: 4:10:15:9:10:35:9)\n"
     "    -g, --grayscale         Grayscale encoding\n"
     "    -p, --pixelratio        Store pixel aspect ratio in video stream\n"
     "    -P, --loosePixelratio   Store pixel aspect ratio with specified width\n"
-    "           <modulus>        Takes as optional argument what number you want\n"
+    "          <MOD:PARX:PARY>   Takes as optional arguments what number you want\n"
     "                            the dimensions to divide cleanly by (default 16)\n"
+    "                            and the pixel ratio to use (default autodetected)\n)"
 
 
        "\n"
 
 
        "### Audio Options-----------------------------------------------------------\n\n"
-       "    -E, --aencoder <string> Audio encoder (faac/lame/vorbis/ac3/aac+ac3) \n"
-       "                            ac3 meaning passthrough, aac+ac3 meaning an\n"
-       "                            aac dpl2 mixdown paired with ac3 pass-thru\n"
-       "                            (default: guessed)\n"
-       "    -B, --ab <kb/s>         Set audio bitrate (default: 128)\n"
        "    -a, --audio <string>    Select audio channel(s), separated by commas\n"
-       "                            (\"none\" for no audio, \"1,2,3\" for multiple\n"
-       "                             tracks, default: first one,\n"
-       "                             max 8 normally, max 4 with aac+ac3)\n"
-    "    -6, --mixdown <string>  Format for surround sound downmixing\n"
+    "                            More than one output track can be used for one\n"
+    "                            input.\n"
+    "                            (\"none\" for no audio, \"1,2,3\" for multiple\n"
+    "                             tracks, default: first one)\n"
+    "    -E, --aencoder <string> Audio encoder(s) (faac/lame/vorbis/ac3) \n"
+       "                            ac3 meaning passthrough\n"
+       "                            Seperated by commas for more than one audio track.\n"
+       "                            (default: guessed)\n"
+       "    -B, --ab <kb/s>         Set audio bitrate(s)  (default: 128)\n"
+    "                            Seperated by commas for more than one audio track.\n"
+       "    -6, --mixdown <string>  Format(s) for surround sound downmixing\n"
+    "                            Seperated by commas for more than one audio track.\n"
     "                            (mono/stereo/dpl1/dpl2/6ch, default: dpl2)\n"
-    "    -R, --arate             Set audio samplerate (" );
+    "    -R, --arate             Set audio samplerate(s) (" );
     for( i = 0; i < hb_audio_rates_count; i++ )
     {
         fprintf( stderr, hb_audio_rates[i].string );
@@ -1235,9 +1517,11 @@ static void ShowHelp()
             fprintf( stderr, "/" );
     }
     fprintf( stderr, " kHz)\n"
+    "                            Seperated by commas for more than one audio track.\n"
     "    -D, --drc <float>       Apply extra dynamic range compression to the audio,\n"
     "                            making soft sounds louder. Range is 1.0 to 4.0\n"
     "                            (too loud), with 1.5 - 2.5 being a useful range.\n"
+    "                            Seperated by commas for more than one audio track.\n"
 
 
        "\n"
@@ -1262,7 +1546,7 @@ static void ShowPresets()
 {
     printf("\n+ Animation:  -e x264 -b 1000 -B 160 -R 48 -E faac -f mkv --deinterlace=\"slower\" -m -p -2 -T -x ref=5:mixed-refs:bframes=6:bime:weightb:b-rdo:direct=auto:b-pyramid:me=umh:subme=5:analyse=all:8x8dct:trellis=1:nr=150:no-fast-pskip:filter=2,2\n");
 
-    printf("\n+ AppleTV:  -e x264 -b 2500 -B 160 -R 48 -E aac+ac3 -f mp4 -4 -m -p -x bframes=3:ref=1:subme=5:me=umh:no-fast-pskip=1:trellis=1:cabac=0\n");
+    printf("\n+ AppleTV:  -e x264 -b 2500 -B 160 -R 48 -E faac,ac3 -f mp4 -4 -m -p -x bframes=3:ref=1:subme=5:me=umh:no-fast-pskip=1:trellis=1:cabac=0\n");
 
     printf("\n+ Bedlam:  -e x264 -b 1800 -E ac3 -f mkv -m -p -2 -T -x ref=16:mixed-refs:bframes=16:bime:weightb:b-rdo:direct=auto:b-pyramid:me=esa:subme=7:me-range=64:analyse=all:8x8dct:trellis=1:no-fast-pskip:no-dct-decimate:filter=-2,-1\n");
 
@@ -1337,6 +1621,7 @@ static int ParseOptions( int argc, char ** argv )
             { "deblock",     optional_argument, NULL,    '7' },
             { "denoise",     optional_argument, NULL,    '8' },
             { "detelecine",  optional_argument, NULL,    '9' },
+            { "decomb",      optional_argument, NULL,    '5' },            
             { "grayscale",   no_argument,       NULL,    'g' },
             { "pixelratio",  no_argument,       NULL,    'p' },
             { "loosePixelratio", optional_argument,   NULL,    'P' },
@@ -1366,7 +1651,7 @@ static int ParseOptions( int argc, char ** argv )
         int c;
 
                c = getopt_long( argc, argv,
-                                                "hvuC:f:4i:Io:t:Lc:m::a:6:s:UFN:e:E:2dD:789gpOP::w:l:n:b:q:S:B:r:R:Qx:TY:X:VZ:z",
+                                                "hvuC:f:4i:Io:t:Lc:m::a:6:s:UFN:e:E:2dD:7895gpOP::w:l:n:b:q:S:B:r:R:Qx:TY:X:VZ:z",
                          long_options, &option_index );
         if( c < 0 )
         {
@@ -1401,6 +1686,20 @@ static int ParseOptions( int argc, char ** argv )
                 break;
             case 'i':
                 input = strdup( optarg );
+                #ifdef __APPLE_CC__
+                char *devName = bsd_name_for_path( input );
+                if( devName == NULL )
+                {
+                    return 0;
+                }
+                if( device_is_dvd( devName ) )
+                {
+                    char *newInput = malloc( strlen("/dev/") + strlen( devName ) + 1);
+                    sprintf( newInput, "/dev/%s", devName );
+                    free(input);
+                    input = newInput;
+                }
+                #endif
                 break;
             case 'o':
                 output = strdup( optarg );
@@ -1450,32 +1749,67 @@ static int ParseOptions( int argc, char ** argv )
                 chapter_markers = 1;
                 break;
             case 'a':
-                audios = strdup( optarg );
-                break;
-            case '6':
-                if( !strcasecmp( optarg, "mono" ) )
-                {
-                    audio_mixdown = HB_AMIXDOWN_MONO;
-                }
-                else if( !strcasecmp( optarg, "stereo" ) )
-                {
-                    audio_mixdown = HB_AMIXDOWN_STEREO;
-                }
-                else if( !strcasecmp( optarg, "dpl1" ) )
-                {
-                    audio_mixdown = HB_AMIXDOWN_DOLBY;
-                }
-                else if( !strcasecmp( optarg, "dpl2" ) )
+            {
+                char * token = strtok(optarg, ",");
+                if (token == NULL)
+                    token = optarg;
+                int track_start, track_end;
+                while( token != NULL )
                 {
-                    audio_mixdown = HB_AMIXDOWN_DOLBYPLII;
+                    audio = calloc(1, sizeof(*audio));
+                    hb_audio_config_init(audio);
+                    if (strlen(token) >= 3)
+                    {
+                        if (sscanf(token, "%d-%d", &track_start, &track_end) == 2)
+                        {
+                            int i;
+                            for (i = track_start - 1; i < track_end; i++)
+                            {
+                                if (i != track_start - 1)
+                                {
+                                    audio = calloc(1, sizeof(*audio));
+                                    hb_audio_config_init(audio);
+                                }
+                                audio->in.track = i;
+                                audio->out.track = num_audio_tracks++;
+                                hb_list_add(audios, audio);
+                            }
+                        }
+                        else if( !strcasecmp(token, "none" ) )
+                        {
+                            audio->in.track = audio->out.track = -1;
+                            audio->out.codec = 0;
+                            hb_list_add(audios, audio);
+                            break;
+                        }
+                        else
+                        {
+                            fprintf(stderr, "ERROR: Unable to parse audio input \"%s\", skipping.",
+                                    token);
+                            free(audio);
+                        }
+                    }
+                    else
+                    {
+                        audio->in.track = atoi(token) - 1;
+                        audio->out.track = num_audio_tracks++;
+                        hb_list_add(audios, audio);
+                    }
+                    token = strtok(NULL, ",");
                 }
-                else if( !strcasecmp( optarg, "6ch" ) )
+                break;
+            }
+            case '6':
+                if( optarg != NULL )
                 {
-                    audio_mixdown = HB_AMIXDOWN_6CH;
+                    mixdowns = strdup( optarg );
                 }
                 break;
             case 'D':
-                dynamic_range_compression = atof( optarg );
+                if( optarg != NULL )
+                {
+                    dynamic_range_compression = strdup( optarg );
+                }
                 break;
             case 's':
                 sub = atoi( optarg );
@@ -1550,6 +1884,13 @@ static int ParseOptions( int argc, char ** argv )
                 }
                 detelecine = 1;
                 break;
+            case '5':
+                if( optarg != NULL )
+                {
+                    decomb_opt = strdup( optarg );
+                }
+                decomb = 1;
+                break;
             case 'g':
                 grayscale = 1;
                 break;
@@ -1560,7 +1901,7 @@ static int ParseOptions( int argc, char ** argv )
                 loosePixelratio = 1;
                 if( optarg != NULL )
                 {
-                    modulus = atoi( optarg );
+                    sscanf( optarg, "%i:%i:%i", &modulus, &par_width, &par_height );
                 }
                 break;
             case 'e':
@@ -1597,27 +1938,9 @@ static int ParseOptions( int argc, char ** argv )
                 }
                 break;
             case 'E':
-                if( !strcasecmp( optarg, "ac3" ) )
-                {
-                    acodec = HB_ACODEC_AC3;
-                }
-                else if( !strcasecmp( optarg, "lame" ) )
-                {
-                    acodec = HB_ACODEC_LAME;
-                }
-                else if( !strcasecmp( optarg, "faac" ) )
-                {
-                    acodec = HB_ACODEC_FAAC;
-                }
-                else if( !strcasecmp( optarg, "vorbis") )
-                {
-                    acodec = HB_ACODEC_VORBIS;
-                }
-                else if( !strcasecmp( optarg, "aac+ac3") )
+                if( optarg != NULL )
                 {
-                    acodec = HB_ACODEC_FAAC;
-                    audio_mixdown = HB_AMIXDOWN_DOLBYPLII_AC3;
-                    arate = 48000;
+                    acodecs = strdup( optarg );
                 }
                 break;
             case 'w':
@@ -1658,23 +1981,11 @@ static int ParseOptions( int argc, char ** argv )
                 break;
             }
             case 'R':
-            {
-                int i;
-                arate = 0;
-                for( i = 0; i < hb_audio_rates_count; i++ )
-                {
-                    if( !strcmp( optarg, hb_audio_rates[i].string ) )
-                    {
-                        arate = hb_audio_rates[i].rate;
-                        break;
-                    }
-                }
-                if( !arate )
+                if( optarg != NULL )
                 {
-                    fprintf( stderr, "invalid framerate %s\n", optarg );
+                    arates = strdup( optarg );
                 }
                 break;
-            }
             case 'b':
                 vbitrate = atoi( optarg );
                 break;
@@ -1685,7 +1996,10 @@ static int ParseOptions( int argc, char ** argv )
                 size = atoi( optarg );
                 break;
             case 'B':
-                abitrate = atoi( optarg );
+                if( optarg != NULL )
+                {
+                    abitrates = strdup( optarg );
+                }
                 break;
             case 'Q':
                 crf = 0;
@@ -1747,6 +2061,7 @@ static int CheckOptions( int argc, char ** argv )
             if( p && !strcasecmp( p, ".avi" ) )
             {
                 mux = HB_MUX_AVI;
+                default_acodec = HB_ACODEC_LAME;
             }
             else if( p && ( !strcasecmp( p, ".mp4" )  ||
                             !strcasecmp( p, ".m4v" ) ) )
@@ -1755,15 +2070,18 @@ static int CheckOptions( int argc, char ** argv )
                     mux = HB_MUX_IPOD;
                 else
                     mux = HB_MUX_MP4;
+                default_acodec = HB_ACODEC_FAAC;
             }
             else if( p && ( !strcasecmp( p, ".ogm" ) ||
                             !strcasecmp( p, ".ogg" ) ) )
             {
                 mux = HB_MUX_OGM;
+                default_acodec = HB_ACODEC_VORBIS;
             }
             else if( p && !strcasecmp(p, ".mkv" ) )
             {
                 mux = HB_MUX_MKV;
+                default_acodec = HB_ACODEC_AC3;
             }
             else
             {
@@ -1775,22 +2093,27 @@ static int CheckOptions( int argc, char ** argv )
         else if( !strcasecmp( format, "avi" ) )
         {
             mux = HB_MUX_AVI;
+            default_acodec = HB_ACODEC_LAME;
         }
-        else if( !strcasecmp( format, "mp4" ) )
+        else if( !strcasecmp( format, "mp4" ) ||
+                 !strcasecmp( format, "m4v" ) )
         {
             if ( h264_30 == 1)
                 mux = HB_MUX_IPOD;
             else
                 mux = HB_MUX_MP4;
+            default_acodec = HB_ACODEC_FAAC;
         }
         else if( !strcasecmp( format, "ogm" ) ||
                  !strcasecmp( format, "ogg" ) )
         {
             mux = HB_MUX_OGM;
+            default_acodec = HB_ACODEC_VORBIS;
         }
         else if( !strcasecmp( format, "mkv" ) )
         {
             mux = HB_MUX_MKV;
+            default_acodec = HB_ACODEC_AC3;
         }
         else
         {
@@ -1798,28 +2121,203 @@ static int CheckOptions( int argc, char ** argv )
                      "choices are avi, mp4, m4v, ogm, ogg and mkv\n.", format );
             return 1;
         }
+    }
+
+    return 0;
+}
 
-        if( !acodec )
+static int get_acodec_for_string( char *codec )
+{
+    if( !strcasecmp( codec, "ac3" ) )
+    {
+        return HB_ACODEC_AC3;
+    }
+    else if( !strcasecmp( codec, "dts" ) || !strcasecmp( codec, "dca" ) )
+    {
+        return HB_ACODEC_DCA;
+    }
+    else if( !strcasecmp( codec, "lame" ) )
+    {
+        return HB_ACODEC_LAME;
+    }
+    else if( !strcasecmp( codec, "faac" ) )
+    {
+        return HB_ACODEC_FAAC;
+    }
+    else if( !strcasecmp( codec, "vorbis") )
+    {
+        return HB_ACODEC_VORBIS;
+    }
+    else
+    {
+        return -1;
+    }
+}
+
+static int is_sample_rate_valid(int rate)
+{
+    int i;
+    for( i = 0; i < hb_audio_rates_count; i++ )
+    {
+            if (rate == hb_audio_rates[i].rate)
+                return 1;
+    }
+    return 0;
+}
+
+#ifdef __APPLE_CC__
+/****************************************************************************
+ * bsd_name_for_path
+ *
+ * Returns the BSD device name for the block device that contains the
+ * passed-in path. Returns NULL on failure.
+ ****************************************************************************/
+static char* bsd_name_for_path(char *path)
+{
+    OSStatus err;
+    FSRef ref;
+    err = FSPathMakeRef( (const UInt8 *) input, &ref, NULL );
+    if( err != noErr )
+    {
+        return NULL;
+    }
+
+    // Get the volume reference number.
+    FSCatalogInfo catalogInfo;
+    err = FSGetCatalogInfo( &ref, kFSCatInfoVolume, &catalogInfo, NULL, NULL,
+                            NULL);
+    if( err != noErr )
+    {
+        return NULL;
+    }
+    FSVolumeRefNum volRefNum = catalogInfo.volume;
+
+    // Now let's get the device name
+    GetVolParmsInfoBuffer volumeParms;
+    err = FSGetVolumeParms( volRefNum, &volumeParms, sizeof( volumeParms ) );
+    if( err != noErr )
+    {
+        return NULL;
+    }
+
+    // A version 4 GetVolParmsInfoBuffer contains the BSD node name in the
+    // vMDeviceID field. It is actually a char * value. This is mentioned in the
+    // header CoreServices/CarbonCore/Files.h.
+    return volumeParms.vMDeviceID;
+}
+
+/****************************************************************************
+ * device_is_dvd
+ *
+ * Returns whether or not the passed in BSD device represents a DVD, or other
+ * optical media.
+ ****************************************************************************/
+static int device_is_dvd(char *device)
+{
+    io_service_t service = get_iokit_service(device);
+    if( service == IO_OBJECT_NULL )
+    {
+        return 0;
+    }
+    int result = is_dvd_service(service);
+    IOObjectRelease(service);
+    return result;
+}
+
+/****************************************************************************
+ * get_iokit_service
+ *
+ * Returns the IOKit service object for the passed in BSD device name.
+ ****************************************************************************/
+static io_service_t get_iokit_service( char *device )
+{
+    CFMutableDictionaryRef matchingDict;
+    matchingDict = IOBSDNameMatching( kIOMasterPortDefault, 0, device );
+    if( matchingDict == NULL )
+    {
+        return IO_OBJECT_NULL;
+    }
+    // Fetch the object with the matching BSD node name. There should only be
+    // one match, so IOServiceGetMatchingService is used instead of
+    // IOServiceGetMatchingServices to simplify the code.
+    return IOServiceGetMatchingService( kIOMasterPortDefault, matchingDict );
+}
+
+/****************************************************************************
+ * is_dvd_service
+ *
+ * Returns whether or not the service passed in is a DVD.
+ *
+ * Searches for an IOMedia object that represents the entire (whole) media that
+ * the volume is on. If the volume is on partitioned media, the whole media
+ * object will be a parent of the volume's media object. If the media is not
+ * partitioned, the volume's media object will be the whole media object.
+ ****************************************************************************/
+static int is_dvd_service( io_service_t service )
+{
+    kern_return_t  kernResult;
+    io_iterator_t  iter;
+
+    // Create an iterator across all parents of the service object passed in.
+    kernResult = IORegistryEntryCreateIterator( service,
+                                                kIOServicePlane,
+                                                kIORegistryIterateRecursively | kIORegistryIterateParents,
+                                                &iter );
+    if( kernResult != KERN_SUCCESS )
+    {
+        return 0;
+    }
+    if( iter == IO_OBJECT_NULL )
+    {
+        return 0;
+    }
+
+    // A reference on the initial service object is released in the do-while
+    // loop below, so add a reference to balance.
+    IOObjectRetain( service );
+
+    int result = 0;
+    do
+    {
+        if( is_whole_media_service( service ) &&
+            IOObjectConformsTo( service, kIODVDMediaClass) )
         {
-            if( mux == HB_MUX_MP4 || mux == HB_MUX_IPOD )
-            {
-                acodec = HB_ACODEC_FAAC;
-            }
-            else if( mux == HB_MUX_AVI )
-            {
-                acodec = HB_ACODEC_LAME;
-            }
-            else if( mux == HB_MUX_OGM )
-            {
-                acodec = HB_ACODEC_VORBIS;
-            }
-            else if( mux == HB_MUX_MKV )
-            {
-                acodec = HB_ACODEC_AC3;
-            }
+            result = 1;
         }
+        IOObjectRelease( service );
+    } while( !result && (service = IOIteratorNext( iter )) );
+    IOObjectRelease( iter );
+
+    return result;
+}
+
+/****************************************************************************
+ * is_whole_media_service
+ *
+ * Returns whether or not the service passed in is an IOMedia service and
+ * represents the "whole" media instead of just a partition.
+ *
+ * The whole media object is indicated in the IORegistry by the presence of a
+ * property with the key "Whole" and value "Yes".
+ ****************************************************************************/
+static is_whole_media_service( io_service_t service )
+{
+    int result = 0;
 
+    if( IOObjectConformsTo( service, kIOMediaClass ) )
+    {
+        CFTypeRef wholeMedia = IORegistryEntryCreateCFProperty( service,
+                                                                CFSTR( kIOMediaWholeKey ),
+                                                                kCFAllocatorDefault,
+                                                                0 );
+        if ( !wholeMedia )
+        {
+            return 0;
+        }
+        result = CFBooleanGetValue( (CFBooleanRef)wholeMedia );
+        CFRelease( wholeMedia );
     }
 
-    return 0;
+    return result;
 }
+#endif // __APPLE_CC__