OSDN Git Service

Implement transport and program stream support. With these changes it's now possible...
authorawk <awk@b64f7644-9d1e-0410-96f1-a4d463321fa5>
Wed, 4 Jul 2007 02:14:42 +0000 (02:14 +0000)
committerawk <awk@b64f7644-9d1e-0410-96f1-a4d463321fa5>
Wed, 4 Jul 2007 02:14:42 +0000 (02:14 +0000)
git-svn-id: svn://localhost/HandBrake/trunk@648 b64f7644-9d1e-0410-96f1-a4d463321fa5

libhb/Jamfile
libhb/Makefile
libhb/common.h
libhb/deca52.c
libhb/decmpeg2.c
libhb/internal.h
libhb/lang.h
libhb/reader.c
libhb/scan.c
libhb/stream.c [new file with mode: 0755]
macosx/HandBrake.xcodeproj/project.pbxproj

index 7765d01..f301451 100644 (file)
@@ -8,7 +8,7 @@ SubDir TOP libhb ;
 
 LIBHB_SRC =
 ipodutil.cpp common.c hb.c ports.c scan.c work.c decmpeg2.c encavcodec.c update.c
-demuxmpeg.c fifo.c render.c reader.c muxcommon.c muxmp4.c sync.c
+demuxmpeg.c fifo.c render.c reader.c muxcommon.c muxmp4.c sync.c stream.c
 decsub.c deca52.c decdca.c encfaac.c declpcm.c encx264.c decavcodec.c encxvid.c
 muxavi.c enclame.c muxogm.c encvorbis.c dvd.c ;
 
index 6604ec4..cf92b48 100644 (file)
@@ -22,7 +22,7 @@ ifeq ($(SYSTEM),Linux)
 endif
 
 SRCS = common.c hb.c ports.c scan.c work.c decmpeg2.c encavcodec.c \
-          update.c demuxmpeg.c fifo.c render.c reader.c muxcommon.c \
+          update.c demuxmpeg.c fifo.c render.c reader.c muxcommon.c stream.c \
           muxmp4.c sync.c decsub.c deca52.c decdca.c encfaac.c declpcm.c encx264.c \
           decavcodec.c encxvid.c muxavi.c enclame.c muxogm.c encvorbis.c \
           dvd.c  ipodutil.cpp
index a6ea8ac..123e528 100644 (file)
@@ -28,6 +28,8 @@
 #define EVEN( a )        ( (a) + ( (a) & 1 ) )
 #define MULTIPLE_16( a ) ( 16 * ( ( (a) + 8 ) / 16 ) )
 
+#define HB_DVD_READ_BUFFER_SIZE 2048
+
 typedef struct hb_handle_s hb_handle_t;
 typedef struct hb_list_s hb_list_t;
 typedef struct hb_rate_s hb_rate_t;
@@ -338,6 +340,8 @@ struct hb_audio_s
        /* amixdown is the mixdown format to be used for this audio track */
        int amixdown;
 
+       /* Source PID is only valid for MPEG Transport Streams */
+       int source_pid;
 #endif
 };
 
index 2a8c53c..88974d5 100644 (file)
@@ -25,6 +25,8 @@ struct hb_work_private_s
     int           sync;
     int           size;
 
+    int64_t       next_expected_pts;
+
     uint8_t       frame[3840];
 
     hb_list_t   * list;
@@ -76,7 +78,8 @@ int deca52Init( hb_work_object_t * w, hb_job_t * job )
        pv->out_discrete_channels = HB_AMIXDOWN_GET_DISCRETE_CHANNEL_COUNT(w->amixdown);
 
     pv->level     = 32768.0;
-
+    pv->next_expected_pts = 0;
+    
     return 0;
 }
 
@@ -191,9 +194,15 @@ static hb_buffer_t * Decode( hb_work_object_t * w )
 
     /* 6 blocks per frame, 256 samples per block, channelsused channels */
     buf        = hb_buffer_init( 6 * 256 * pv->out_discrete_channels * sizeof( float ) );
+    if (pts == -1)
+    {
+      pts = pv->next_expected_pts;
+    }
     buf->start = pts + ( pos / pv->size ) * 6 * 256 * 90000 / pv->rate;
     buf->stop  = buf->start + 6 * 256 * 90000 / pv->rate;
 
+    pv->next_expected_pts = buf->stop;
+    
     for( i = 0; i < 6; i++ )
     {
         sample_t * samples_in;
index 4fb6ad7..65293c6 100644 (file)
@@ -20,6 +20,7 @@ struct hb_libmpeg2_s
     int                  width;
     int                  height;
     int                  rate;
+    int                  aspect_ratio;
     int                  got_iframe;
     int                  look_for_break;
     int64_t              last_pts;
@@ -88,6 +89,28 @@ int hb_libmpeg2_decode( hb_libmpeg2_t * m, hb_buffer_t * buf_es,
                     m->rate = 1126125;
                 }
             }
+            if ( m->aspect_ratio <= 0 )
+            {
+              // We can parse out the aspect ratio from the Sequence Start Header data in buf_es->data
+              
+              // Make sure we have the correct data in the buffer
+              if ((buf_es->data[0] == 0x00) && (buf_es->data[1] == 0x00) && (buf_es->data[2] == 0x01) && (buf_es->data[3] == 0xb3))
+              {
+                unsigned char ar_fr = buf_es->data[7];    // Top 4 bits == aspect ratio flag  - bottom 4 bits == rate flags
+                switch ((ar_fr & 0xf0) >> 4)
+                {
+                  case 2:
+                    m->aspect_ratio = HB_ASPECT_BASE * 4 / 3;   // 4:3
+                    break;
+                  case 3:
+                    m->aspect_ratio = HB_ASPECT_BASE * 16 / 9;  // 16:9
+                    break;
+                  default:
+                    hb_log("hb_libmpeg2_decode - STATE_SEQUENCE unexpected aspect ratio/frame rate 0x%x\n", ar_fr);
+                    break;
+                }
+              } 
+            }
         }
         else if( state == STATE_GOP && m->look_for_break == 2)
         {
@@ -170,11 +193,12 @@ int hb_libmpeg2_decode( hb_libmpeg2_t * m, hb_buffer_t * buf_es,
  * 
  *********************************************************************/
 void hb_libmpeg2_info( hb_libmpeg2_t * m, int * width, int * height,
-                        int * rate )
+                        int * rate, int *aspect_ratio )
 {
     *width  = m->width;
     *height = m->height;
     *rate   = m->rate;
+    *aspect_ratio = m->aspect_ratio;
 }
 
 /**********************************************************************
index 34bca3d..60fb80c 100644 (file)
@@ -100,7 +100,7 @@ int              hb_libmpeg2_decode( hb_libmpeg2_t *,
                                       hb_buffer_t * es_buf,
                                       hb_list_t * raw_list );
 void             hb_libmpeg2_info( hb_libmpeg2_t * m, int * width,
-                                    int * height, int * rate );
+                                    int * height, int * rate, int * aspect_ratio );
 void             hb_libmpeg2_close( hb_libmpeg2_t ** );
 
 /***********************************************************************
@@ -112,6 +112,7 @@ int hb_demux_ps( hb_buffer_t * ps_buf, hb_list_t * es_list );
  * dvd.c
  **********************************************************************/
 typedef struct hb_dvd_s hb_dvd_t;
+typedef struct hb_stream_s hb_stream_t;
 
 hb_dvd_t *   hb_dvd_init( char * path );
 int          hb_dvd_title_count( hb_dvd_t * );
@@ -124,6 +125,15 @@ int          hb_dvd_chapter( hb_dvd_t * );
 int          hb_dvd_is_break( hb_dvd_t * d );
 void         hb_dvd_close( hb_dvd_t ** );
 
+int         hb_stream_is_stream_type( char * path);
+hb_stream_t * hb_stream_open( char * path );
+void            hb_stream_close( hb_stream_t ** );
+hb_title_t * hb_stream_title_scan( hb_stream_t *);
+int          hb_stream_read( hb_stream_t *, hb_buffer_t *);
+int          hb_stream_seek( hb_stream_t *, float );
+void            hb_stream_update_audio( hb_stream_t *, hb_audio_t *);
+void            hb_stream_set_selected_audio_pid_index(hb_stream_t *, int);
+
 /***********************************************************************
  * Work objects
  **********************************************************************/
index ac734a7..a1cab4d 100644 (file)
@@ -205,7 +205,7 @@ static const iso639_lang_t languages[] =
   { "Zulu", "", "zu", "zul" },
   { NULL, NULL, NULL } };
 
-iso639_lang_t * lang_for_code( int code )
+static iso639_lang_t * lang_for_code( int code )
 {
     char code_string[2];
     iso639_lang_t * lang;
index 4767f87..335dfb8 100644 (file)
@@ -14,6 +14,7 @@ typedef struct
 
     hb_dvd_t     * dvd;
     hb_buffer_t  * ps;
+    hb_stream_t  * stream;
 
 } hb_reader_t;
 
@@ -53,25 +54,43 @@ static void ReaderFunc( void * _r )
     hb_fifo_t    * fifo;
     hb_buffer_t  * buf;
     hb_list_t    * list;
-    int            chapter;
+    int            chapter = -1;
 
     if( !( r->dvd = hb_dvd_init( r->title->dvd ) ) )
     {
-        return;
+        if ( !(r->stream = hb_stream_open(r->title->dvd) ) )
+        {
+          return;
+        }
     }
 
-    if( !hb_dvd_start( r->dvd, r->title->index, r->job->chapter_start ) )
+    if (r->dvd)
     {
-        hb_dvd_close( &r->dvd );
-        return;
+      if( !hb_dvd_start( r->dvd, r->title->index, r->job->chapter_start ) )
+      {
+          hb_dvd_close( &r->dvd );
+          return;
+      }
     }
-
+    
+       if (r->stream)
+       {
+               // At this point r->audios[0] gives us the index of the selected audio track for output track 0
+               // we cannot effectively demux multiple PID's into the seperate output tracks unfortunately
+               // so we'll just specifiy things here for a single track.
+               hb_stream_set_selected_audio_pid_index(r->stream, r->job->audios[0]);
+       }
+       
     list  = hb_list_init();
-    r->ps = hb_buffer_init( 2048 );
+    r->ps = hb_buffer_init( HB_DVD_READ_BUFFER_SIZE );
 
     while( !*r->die && !r->job->done )
     {
-        chapter = hb_dvd_chapter( r->dvd );
+        if (r->dvd)
+          chapter = hb_dvd_chapter( r->dvd );
+        else if (r->stream)
+          chapter = 1;
+          
         if( chapter < 0 )
         {
             hb_log( "reader: end of the title reached" );
@@ -84,11 +103,21 @@ static void ReaderFunc( void * _r )
             break;
         }
 
-        if( !hb_dvd_read( r->dvd, r->ps ) )
+        if (r->dvd)
+        {
+          if( !hb_dvd_read( r->dvd, r->ps ) )
+          {
+              break;
+          }
+        }
+        else if (r->stream)
         {
+          if ( !hb_stream_read( r->stream, r->ps ) )
+          {
             break;
+          }
         }
-
+        
         hb_demux_ps( r->ps, list );
 
         while( ( buf = hb_list_item( list, 0 ) ) )
@@ -113,8 +142,16 @@ static void ReaderFunc( void * _r )
 
     hb_list_empty( &list );
     hb_buffer_close( &r->ps );
-    hb_dvd_stop( r->dvd );
-    hb_dvd_close( &r->dvd );
+    if (r->dvd)
+    {
+      hb_dvd_stop( r->dvd );
+      hb_dvd_close( &r->dvd );
+    }
+    else if (r->stream)
+    {
+      hb_stream_close(&r->stream);
+    }
+    
     free( r );
     _r = NULL;
 
index 45da1b7..97a4370 100644 (file)
@@ -17,7 +17,8 @@ typedef struct
     hb_list_t   * list_title;
     
     hb_dvd_t    * dvd;
-
+       hb_stream_t * stream;
+       
 } hb_scan_t;
 
 static void ScanFunc( void * );
@@ -44,6 +45,9 @@ static void ScanFunc( void * _data )
     hb_title_t * title;
     int          i;
 
+       data->dvd = NULL;
+       data->stream = NULL;
+       
     /* Try to open the path as a DVD. If it fails, try as a file */
     hb_log( "scan: trying to open with libdvdread" );
     if( ( data->dvd = hb_dvd_init( data->path ) ) )
@@ -68,18 +72,15 @@ static void ScanFunc( void * _data )
     }
     else
     {
-       /* Open as a VOB file */
-        FILE * file;
-        hb_log( "scan: trying to open as VOB file" );
-        file = fopen( data->path, "rb" );
-        if( file )
+        if ( hb_stream_is_stream_type(data->path) )
         {
-            /* XXX */
-            fclose( file );
+          hb_log( "scan: trying to open as MPEG-2 Stream");
+                 data->stream = hb_stream_open (data->path);
+          hb_list_add( data->list_title, hb_stream_title_scan( data->stream ) );
         }
         else
         {
-            hb_log( "scan: fopen failed" );
+            hb_log( "scan: unrecognized file type" );
             return;
         }
     }
@@ -123,7 +124,7 @@ static void ScanFunc( void * _data )
         /* Update the UI */
         state.state   = HB_STATE_SCANNING;
         p.title_cur   = title->index;
-        p.title_count = hb_dvd_title_count( data->dvd );
+        p.title_count = data->dvd ? hb_dvd_title_count( data->dvd ) : hb_list_count(data->list_title);
         hb_set_state( data->h, &state );
 #undef p
 
@@ -135,7 +136,33 @@ static void ScanFunc( void * _data )
             hb_list_rem( data->list_title, title );
             continue;
         }
-
+        
+               if (data->stream)
+               {
+                       // Stream based processing uses PID's to handle the different audio options for a given title
+                       for( j = 0; j < hb_list_count( title->list_audio ); j++ )
+                       {
+                               audio = hb_list_item( title->list_audio, j );
+                               hb_stream_update_audio(data->stream, audio);
+                       }
+               }
+               else if (data->dvd)
+               {
+                       /* Make sure we found AC3 rates and bitrates */
+                       for( j = 0; j < hb_list_count( title->list_audio ); )
+                       {
+                               audio = hb_list_item( title->list_audio, j );
+                               if( audio->codec == HB_ACODEC_AC3 &&
+                                       !audio->bitrate )
+                               {
+                                       hb_list_rem( title->list_audio, audio );
+                                       free( audio );
+                                       continue;
+                               }
+                               j++;
+                       }
+               }
+               
         /* Make sure we found AC3 / DCA rates and bitrates */
         for( j = 0; j < hb_list_count( title->list_audio ); )
         {
@@ -241,6 +268,10 @@ static void ScanFunc( void * _data )
     {
         hb_dvd_close( &data->dvd );
     }
+       if (data->stream)
+       {
+               hb_stream_close(&data->stream);
+       }
     free( data->path );
     free( data );
     _data = NULL;
@@ -259,35 +290,56 @@ static int DecodePreviews( hb_scan_t * data, hb_title_t * title )
     hb_buffer_t   * buf_ps, * buf_es, * buf_raw;
     hb_list_t     * list_es, * list_raw;
     hb_libmpeg2_t * mpeg2;
-
-    buf_ps   = hb_buffer_init( 2048 );
+    
+    buf_ps   = hb_buffer_init( HB_DVD_READ_BUFFER_SIZE );
     list_es  = hb_list_init();
     list_raw = hb_list_init();
 
     hb_log( "scan: decoding previews for title %d", title->index );
 
-    hb_dvd_start( data->dvd, title->index, 1 );
-
+    if (data->dvd)
+      hb_dvd_start( data->dvd, title->index, 1 );
+      
     for( i = 0; i < 10; i++ )
     {
         int j, k;
         FILE * file_preview;
         char   filename[1024];
 
-        if( !hb_dvd_seek( data->dvd, (float) ( i + 1 ) / 11.0 ) )
+        if (data->dvd)
         {
+          if( !hb_dvd_seek( data->dvd, (float) ( i + 1 ) / 11.0 ) )
+          {
+              goto error;
+          }
+        }
+        else if (data->stream)
+        {
+          if (!hb_stream_seek(data->stream, (float) ( i + 1 ) / 11.0 ) )
+          {
             goto error;
+          }
         }
-
+        
         hb_log( "scan: preview %d", i + 1 );
 
         mpeg2 = hb_libmpeg2_init();
 
         for( j = 0; j < 10240 ; j++ )
         {
-            if( !hb_dvd_read( data->dvd, buf_ps ) )
+            if (data->dvd)
             {
+              if( !hb_dvd_read( data->dvd, buf_ps ) )
+              {
+                  goto error;
+              }
+            }
+            else if (data->stream)
+            {
+              if ( !hb_stream_read(data->stream,buf_ps) )
+              {
                 goto error;
+              }
             }
             hb_demux_ps( buf_ps, list_es );
 
@@ -329,8 +381,13 @@ static int DecodePreviews( hb_scan_t * data, hb_title_t * title )
         {
             /* Get size and rate infos */
             title->rate = 27000000;
+            int ar;
             hb_libmpeg2_info( mpeg2, &title->width, &title->height,
-                              &title->rate_base );
+                              &title->rate_base, &ar );
+            // The aspect ratio may have already been set by parsing the VOB/IFO details on a DVD, however
+            // if we're working with program/transport streams that data needs to come from within the stream.
+            if (title->aspect <= 0)
+              title->aspect = ar;
             title->crop[0] = title->crop[1] = title->height / 2;
             title->crop[2] = title->crop[3] = title->width / 2;
         }
@@ -435,7 +492,9 @@ cleanup:
         hb_buffer_close( &buf_raw );
     }
     hb_list_close( &list_raw );
-    hb_dvd_stop( data->dvd );
+    if (data->dvd)
+      hb_dvd_stop( data->dvd );
+
     return ret;
 }
 
diff --git a/libhb/stream.c b/libhb/stream.c
new file mode 100755 (executable)
index 0000000..2cfc0e9
--- /dev/null
@@ -0,0 +1,1985 @@
+/* $Id$
+
+   This file is part of the HandBrake source code.
+   Homepage: <http://handbrake.m0k.org/>.
+   It may be used under the terms of the GNU General Public License. */
+
+#include "hb.h"
+#include "lang.h"
+#include "a52dec/a52.h"
+
+#include <string.h>
+
+#define min(a, b) a < b ? a : b
+
+typedef enum { hb_stream_type_unknown = 0, hb_stream_type_transport, hb_stream_type_program } hb_stream_type_t;
+#define kMaxNumberDecodeStreams 8
+#define kMaxNumberVideoPIDS 16
+#define kMaxNumberAudioPIDS 32
+//#define kVideoStream 0
+//#define kAudioStream 1
+#define kNumDecodeBuffers 2
+
+#define CLOCKRATE              ((int64_t)27000000)                     // MPEG System clock rate
+#define STREAMRATE             ((int64_t)2401587)                      // Original HD stream rate 19.2 Mbps
+#define DEMUX                  (((int)STREAMRATE * 8) / 50)// Demux value for HD content STREAMRATE / 50
+
+struct hb_stream_s
+{
+    char         * path;
+       FILE             * file_handle;
+       hb_stream_type_t stream_type;
+       
+       int                              ps_current_write_buffer_index;
+       int                              ps_current_read_buffer_index;
+
+       struct {
+               int                              size;
+               int                              len;
+               int                              read_pos;
+               int                              write_pos;
+               unsigned char *  data;
+       } ps_decode_buffer[kNumDecodeBuffers];
+       
+       struct {
+               int lang_code;
+               int flags;
+               int rate;
+               int bitrate;
+       } a52_info[kMaxNumberAudioPIDS];
+       
+       int                              ts_video_pids[kMaxNumberVideoPIDS];
+       int                              ts_audio_pids[kMaxNumberAudioPIDS];
+       
+       int                              ts_number_video_pids;
+       int                              ts_number_audio_pids;
+       int                              ts_selected_audio_pid_index;
+       
+       unsigned char*   ts_packetbuf[kMaxNumberDecodeStreams];
+       int                              ts_packetpos[kMaxNumberDecodeStreams];
+//     int                              ts_bufpackets[kMaxNumberDecodeStreams];
+       int                              ts_foundfirst[kMaxNumberDecodeStreams];
+       int                              ts_skipbad[kMaxNumberDecodeStreams];
+       int                              ts_streamcont[kMaxNumberDecodeStreams];
+       int                              ts_streamid[kMaxNumberDecodeStreams];
+       int                              ts_audio_stream_type[kMaxNumberAudioPIDS];
+       
+       FILE                     *debug_output;
+};
+
+/***********************************************************************
+ * Local prototypes
+ **********************************************************************/
+static void hb_stream_duration(hb_stream_t *stream, hb_title_t *inTitle);
+static void hb_ts_stream_init(hb_stream_t *stream);
+static void hb_ts_stream_find_pids(hb_stream_t *stream);
+static void hb_ts_stream_decode(hb_stream_t *stream);
+static void hb_ts_stream_reset(hb_stream_t *stream);
+static void hb_stream_put_back(hb_stream_t *stream, int i);
+static void hb_stream_set_audio_id_and_codec(hb_stream_t *stream, hb_audio_t *audio);
+
+int hb_stream_is_stream_type( char * path )
+{
+  if ((strstr(path,".mpg") != NULL) ||
+      (strstr(path,".vob") != NULL) || 
+      (strstr(path,".ts") != NULL) || 
+      (strstr(path, ".m2t") != NULL))
+  {
+    return 1;
+  }
+  else
+    return 0;
+}
+
+/***********************************************************************
+ * hb_stream_open
+ ***********************************************************************
+ *
+ **********************************************************************/
+hb_stream_t * hb_stream_open( char * path )
+{
+    hb_stream_t * d;
+
+    d = calloc( sizeof( hb_stream_t ), 1 );
+
+    /* Open device */
+    if( !( d->file_handle = fopen( path, "rb" ) ) )
+    {
+        hb_log( "hb_stream_open: fopen failed (%s)", path );
+        goto fail;
+    }
+
+    d->path = strdup( path );
+
+       if ( ( strstr(d->path,".ts") != NULL) || ( strstr(d->path,".m2t") != NULL))
+       {
+               d->stream_type = hb_stream_type_transport;
+               hb_ts_stream_init(d);
+       }
+       else if (( strstr(d->path,".mpg") != NULL) || ( strstr(d->path,".vob") != NULL))
+       {
+               d->stream_type = hb_stream_type_program;
+       }
+       
+    return d;
+
+fail:
+    free( d );
+    return NULL;
+}
+
+/***********************************************************************
+ * hb_stream_close
+ ***********************************************************************
+ * Closes and frees everything
+ **********************************************************************/
+void hb_stream_close( hb_stream_t ** _d )
+{
+    hb_stream_t * d = *_d;
+
+    if( d->file_handle )
+    {
+        fclose( d->file_handle );
+               d->file_handle = NULL;
+    }
+
+       if (d->debug_output)
+       {
+               fclose(d->debug_output);
+               d->debug_output = NULL;
+       }
+       
+       int i=0;
+       for (i = 0; i < kNumDecodeBuffers; i++)
+       {
+               if (d->ps_decode_buffer[i].data)
+               {
+                       free(d->ps_decode_buffer[i].data);
+                       d->ps_decode_buffer[i].data = NULL;
+               }
+       }
+       
+       for (i = 0; i < kMaxNumberDecodeStreams; i++)
+       {
+               if (d->ts_packetbuf[i])
+               {
+                       free(d->ts_packetbuf[i]);
+                       d->ts_packetbuf[i] = NULL;
+               }
+       }
+       
+    free( d );
+    *_d = NULL;
+}
+
+/***********************************************************************
+ * hb_ps_stream_title_scan
+ ***********************************************************************
+ *
+ **********************************************************************/
+hb_title_t * hb_stream_title_scan(hb_stream_t *stream)
+{
+    // 'Barebones Title'
+    hb_title_t *aTitle = hb_title_init( stream->path, 0 );
+
+       // Copy part of the stream path to the title name
+       char *sep = strrchr(stream->path, '/');
+       if (sep)
+               strcpy(aTitle->name, sep+1);
+       char *dot_term = strrchr(aTitle->name, '.');
+       if (dot_term)
+               *dot_term = '\0';
+       
+    // Height, width,  rate and aspect ratio information is filled in when the previews are built
+
+    hb_stream_duration(stream, aTitle);
+    
+    // One Chapter
+    hb_chapter_t * chapter;
+    chapter = calloc( sizeof( hb_chapter_t ), 1 );
+    chapter->index = 1;
+    chapter->duration = aTitle->duration;
+    chapter->hours = aTitle->hours;
+    chapter->minutes = aTitle->minutes;
+    chapter->seconds = aTitle->seconds;
+    hb_list_add( aTitle->list_chapter, chapter );
+    
+       int i=0, num_audio_tracks = 1;
+       
+       if (stream->stream_type == hb_stream_type_transport)
+       {
+               num_audio_tracks = stream->ts_number_audio_pids;
+       }
+       
+       for (i=0; i < num_audio_tracks ; i++)
+       {
+               // Basic AC-3 Audio track
+               hb_audio_t * audio;
+               audio = calloc( sizeof( hb_audio_t ), 1 );
+
+               audio->source_pid = stream->ts_audio_pids[i];
+               
+               hb_stream_set_audio_id_and_codec(stream, audio);
+               
+               hb_list_add( aTitle->list_audio, audio );
+       }
+       
+  return aTitle;
+}
+
+/***********************************************************************
+ * hb_stream_duration
+ ***********************************************************************
+ *
+ **********************************************************************/
+void hb_stream_duration(hb_stream_t *stream, hb_title_t *inTitle)
+{
+       // VOB Files often have exceedingly unusual PTS values in them - they will progress for a while
+       // and then reset without warning ! 
+       if  (strstr(stream->path,".vob") != NULL) 
+       {
+               // So we'll use a 'fake duration' that should give enough time !
+               int64_t duration = 4 * 3600 * 90000;
+               inTitle->duration = duration; //90LL * dvdtime2msec( &d->pgc->playback_time );
+               inTitle->hours    = inTitle->duration / 90000 / 3600;
+               inTitle->minutes  = ( ( inTitle->duration / 90000 ) % 3600 ) / 60;
+               inTitle->seconds  = ( inTitle->duration / 90000 ) % 60;
+               return;
+       }
+
+    unsigned char *buf = (unsigned char *) malloc(4096);
+    int done = 0;
+    off_t cur_pos;
+    int64_t first_pts = 0, last_pts = 0;
+    
+    // To calculate the duration we look for the first and last presentation time stamps in the stream for video data
+    // and then use the delta
+    while (!done)
+    {
+      cur_pos = ftello(stream->file_handle);
+      if (fread(buf, 4096, 1, stream->file_handle) == 1)
+      {
+        int i=0;
+        for (i=0; (i <= 4092) && !done; i++)
+        {
+          if ((buf[i] == 0x00) && (buf[i+1] == 0x00) && (buf[i+2] == 0x01) && (buf[i+3]  == 0xe0))    // Found a Video Stream
+          {
+              // Now look for a PTS field - we need to make sure we have enough space so we back up a little and read
+              // some more data
+              fseeko(stream->file_handle, cur_pos + i, SEEK_SET);
+              if (fread(buf, 4096, 1, stream->file_handle) == 1)
+              {
+                  int has_pts             = ( ( buf[7] >> 6 ) & 0x2 ) ? 1 : 0;
+                  if (has_pts)
+                  {
+                    first_pts = ( ( ( (uint64_t) buf[9] >> 1 ) & 0x7 ) << 30 ) +
+                          ( buf[10] << 22 ) +
+                          ( ( buf[11] >> 1 ) << 15 ) +
+                          ( buf[12] << 7 ) +
+                          ( buf[13] >> 1 );
+                    done = 1;
+                  }
+                  else
+                  {
+                    fseeko(stream->file_handle, cur_pos, SEEK_SET);
+                    fread(buf, 4096, 1, stream->file_handle);
+                  }
+              }
+          }
+        }
+      }
+      else
+        done = 1;    // End of data;
+    }
+
+    // Now work back from the end of the stream
+    fseeko(stream->file_handle,0 ,SEEK_END);
+    
+    done = 0;
+    while (!done)
+    {
+      // Back up a little
+      if (fseeko(stream->file_handle, -4096, SEEK_CUR) < 0)
+      {
+        done = 1;
+        break;
+      }
+      
+      cur_pos = ftello(stream->file_handle);
+      if (fread(buf, 4096, 1, stream->file_handle) == 1)
+      {
+        int i=0;
+        for (i=4092; (i >= 0) && !done; i--)
+        {
+          if ((buf[i] == 0x00) && (buf[i+1] == 0x00) && (buf[i+2] == 0x01) && (buf[i+3] == 0xe0))    // Found a Video Stream
+          {
+              // Now look for a PTS field - we need to make sure we have enough space so we back up a little and read
+              // some more data
+              fseeko(stream->file_handle, cur_pos + i, SEEK_SET);
+              fread(buf, 1, 4096, stream->file_handle);
+
+              unsigned char pts_dts_flag = buf[7];
+              
+              int has_pts             = ( ( buf[7] >> 6 ) & 0x2 ) ? 1 : 0;
+              if (has_pts)
+              {
+                last_pts = ( ( ( (uint64_t) buf[9] >> 1 ) & 0x7 ) << 30 ) +
+                      ( buf[10] << 22 ) +
+                      ( ( buf[11] >> 1 ) << 15 ) +
+                      ( buf[12] << 7 ) +
+                      ( buf[13] >> 1 );
+                
+                done = 1;
+              }
+              else
+              {
+                // Re Read the original data and carry on (i is still valid in the old buffer)
+                fseeko(stream->file_handle, cur_pos, SEEK_SET);
+                fread(buf, 4096, 1, stream->file_handle);
+              }
+          }
+        }
+        fseeko(stream->file_handle, -4096, SEEK_CUR);   // 'Undo' the last read
+      }
+      else
+        done = 1;    // End of data;
+    }
+    free(buf);
+    
+    int64_t duration = last_pts - first_pts;
+    inTitle->duration = duration; //90LL * dvdtime2msec( &d->pgc->playback_time );
+    inTitle->hours    = inTitle->duration / 90000 / 3600;
+    inTitle->minutes  = ( ( inTitle->duration / 90000 ) % 3600 ) / 60;
+    inTitle->seconds  = ( inTitle->duration / 90000 ) % 60;
+    
+}
+
+/***********************************************************************
+ * hb_stream_read
+ ***********************************************************************
+ *
+ **********************************************************************/
+int hb_stream_read( hb_stream_t * src_stream, hb_buffer_t * b )
+{
+  if (src_stream->stream_type == hb_stream_type_program)
+  {
+         size_t amt_read;
+         amt_read = fread(b->data, HB_DVD_READ_BUFFER_SIZE, 1, src_stream->file_handle);
+         if (amt_read > 0)
+               return 1;
+         else
+               return 0;
+  }
+  else if  (src_stream->stream_type == hb_stream_type_transport)
+  {
+       int read_buffer_index = src_stream->ps_current_read_buffer_index;
+
+       // Transport streams are a little more complex  - we might be able to just
+       // read from the transport stream conversion buffer (if there's enough data)
+       // or we may need to transfer what's left and fill it again.
+       if (src_stream->ps_decode_buffer[read_buffer_index].len - src_stream->ps_decode_buffer[read_buffer_index].read_pos > HB_DVD_READ_BUFFER_SIZE)
+       {
+               memcpy(b->data, src_stream->ps_decode_buffer[read_buffer_index].data + src_stream->ps_decode_buffer[read_buffer_index].read_pos,HB_DVD_READ_BUFFER_SIZE);
+               src_stream->ps_decode_buffer[read_buffer_index].read_pos += HB_DVD_READ_BUFFER_SIZE;
+               return 1;
+       }
+       else
+       {
+               // Not quite enough data in the buffer - transfer what is present, fill the buffer and then 
+               // transfer what's still needed.
+               int transfer_size = HB_DVD_READ_BUFFER_SIZE;
+               int amt_avail_to_transfer = src_stream->ps_decode_buffer[read_buffer_index].len - src_stream->ps_decode_buffer[read_buffer_index].read_pos;
+               memcpy(b->data, src_stream->ps_decode_buffer[read_buffer_index].data + src_stream->ps_decode_buffer[read_buffer_index].read_pos, amt_avail_to_transfer);
+               transfer_size -= amt_avail_to_transfer;
+               src_stream->ps_decode_buffer[read_buffer_index].read_pos += amt_avail_to_transfer;
+               
+               // Give up this buffer - decoding may well need it, and we're done
+               src_stream->ps_decode_buffer[read_buffer_index].write_pos = 0;
+               src_stream->ps_decode_buffer[read_buffer_index].len = 0;
+               
+               // Fill the buffer
+               hb_ts_stream_decode(src_stream);
+               
+               // Decoding will almost certainly have changed the current read buffer index
+               read_buffer_index = src_stream->ps_current_read_buffer_index;
+               
+               if (src_stream->ps_decode_buffer[read_buffer_index].len == 0)
+               {
+                       hb_log("hb_stream_read - buffer after decode has zero length data");
+                       return 0;
+               }
+               
+               // Read the bit we still need
+               memcpy(b->data+amt_avail_to_transfer, src_stream->ps_decode_buffer[read_buffer_index].data + src_stream->ps_decode_buffer[read_buffer_index].read_pos,transfer_size);
+               src_stream->ps_decode_buffer[read_buffer_index].read_pos += transfer_size;
+               
+               return 1;
+       }       
+  }
+  else
+       return 0;
+}
+
+/***********************************************************************
+ * hb_stream_seek
+ ***********************************************************************
+ *
+ **********************************************************************/
+int hb_stream_seek( hb_stream_t * src_stream, float f )
+{
+  off_t stream_size, cur_pos, new_pos;
+  double pos_ratio = f;
+  cur_pos = ftello(src_stream->file_handle);
+  fseeko(src_stream->file_handle,0 ,SEEK_END);
+  stream_size = ftello(src_stream->file_handle);
+  new_pos = (off_t) ((double) (stream_size) * pos_ratio);
+  int r = fseeko(src_stream->file_handle, new_pos, SEEK_SET);
+  
+  if (r == -1)
+  {
+    fseeko(src_stream->file_handle, cur_pos, SEEK_SET);
+    return 0;
+  }
+  
+  if (src_stream->stream_type == hb_stream_type_transport)
+  {
+       // We need to drop the current decoder output and move
+       // forwards to the next transport stream packet.
+       hb_ts_stream_reset(src_stream);
+  }
+  
+  // Now we must scan forwards for a valid start code (0x000001BA)
+  int done = 0;
+  hb_buffer_t *buf = hb_buffer_init(HB_DVD_READ_BUFFER_SIZE);
+  while (!done)
+  {
+    if (hb_stream_read(src_stream,buf) == 1)
+    {
+      int i=0;
+      for (i=0; (i <= HB_DVD_READ_BUFFER_SIZE-4) && (!done); i++)
+      {
+        if ((buf->data[i] == 0x00) && (buf->data[i+1] == 0x00) && (buf->data[i+2] == 0x01) && (buf->data[i+3] == 0xba))
+        {
+          done = 1;
+                 // 'Put Back' the data we've just read (up to this point)
+                 hb_stream_put_back(src_stream, i);
+        }
+      }
+    }
+    else
+      done = 1;    // End of data;
+  }
+  hb_buffer_close(&buf);
+  return 1;
+}
+
+/***********************************************************************
+ * hb_stream_set_audio_id_and_codec
+ ***********************************************************************
+ *
+ **********************************************************************/
+void hb_stream_set_audio_id_and_codec(hb_stream_t *stream, hb_audio_t *audio)
+{
+       off_t cur_pos;
+       cur_pos = ftello(stream->file_handle);
+       int done = 0;
+       hb_buffer_t *buf  = NULL;
+
+        int cur_audio_pid_index = stream->ts_selected_audio_pid_index;
+
+       if (stream->stream_type == hb_stream_type_transport)
+       {
+               int i=0;
+               for (i=0; i < stream->ts_number_audio_pids; i++)
+               {
+                       if (stream->ts_audio_pids[i] == audio->source_pid)
+                       {
+                               stream->ts_selected_audio_pid_index = i;
+                               break;
+                       }
+               }
+       }
+
+      //Start at the beginning of the stream
+      hb_stream_seek(stream, 0.0f);
+//             fseeko(stream->file_handle,0 ,SEEK_SET);
+               
+      // Now we must scan forwards for a valid audio start code (0x000001xx)
+      buf = hb_buffer_init(HB_DVD_READ_BUFFER_SIZE);
+      while (!done)
+      {
+//                     if (fread(buf->data,4096,1,stream->file_handle) == 1)
+              if (hb_stream_read(stream, buf) == 1)
+              {
+                int i=0;
+                for (i=0; (i <= HB_DVD_READ_BUFFER_SIZE-4) && (!done); i++)
+                {
+                      if ((buf->data[i] == 0x00) && (buf->data[i+1] == 0x00) && (buf->data[i+2] == 0x01))
+                      {
+                        if (buf->data[i+3] == 0xbd)
+                        {
+                              audio->id = 0x80bd;
+                              audio->codec = HB_ACODEC_AC3;
+                              done = 1;
+                        }
+                        else if ((buf->data[i+3] & 0xe0) == 0xc0)
+                        {
+                              audio->id = buf->data[i+3];
+                              audio->codec = HB_ACODEC_MPGA;
+                              done = 1;
+                        } 
+                      }
+                }
+              }
+              else
+                done = 1;    // End of data;
+      }
+      hb_buffer_close(&buf);
+
+      fseeko(stream->file_handle, cur_pos, SEEK_SET);
+      
+      stream->ts_selected_audio_pid_index = cur_audio_pid_index;
+}
+
+/***********************************************************************
+ * hb_stream_update_audio
+ ***********************************************************************
+ *
+ **********************************************************************/
+void hb_stream_update_audio(hb_stream_t *stream, hb_audio_t *audio)
+{
+       iso639_lang_t *lang;
+       
+       if (stream->stream_type == hb_stream_type_program)
+       {
+               lang = lang_for_code('e' << 8 | 'n');
+       }
+       else if (stream->stream_type == hb_stream_type_transport)
+       {
+               // Find the audio stream info for this PID
+               int i=0;
+               for (i=0; i < stream->ts_number_audio_pids; i++)
+               {
+                       if (stream->ts_audio_pids[i] == audio->source_pid)
+                               break;
+               }
+               if (i == stream->ts_number_audio_pids)
+               {
+                       hb_log("hb_stream_update_audio - cannot find PID 0x%x (%d) in ts_audio_pids list !", audio->source_pid, audio->source_pid);
+                       return;
+               }
+               
+               lang = lang_for_code(stream->a52_info[i].lang_code);
+               audio->rate = stream->a52_info[i].rate;
+               audio->bitrate = stream->a52_info[i].bitrate;
+               audio->config.a52.ac3flags = audio->ac3flags = stream->a52_info[i].flags;
+
+       }
+       
+       switch( audio->ac3flags & A52_CHANNEL_MASK )
+       {
+               /* mono sources */
+               case A52_MONO:
+               case A52_CHANNEL1:
+               case A52_CHANNEL2:
+                       audio->input_channel_layout = HB_INPUT_CH_LAYOUT_MONO;
+                       break;
+               /* stereo input */
+               case A52_CHANNEL:
+               case A52_STEREO:
+                       audio->input_channel_layout = HB_INPUT_CH_LAYOUT_STEREO;
+                       break;
+               /* dolby (DPL1 aka Dolby Surround = 4.0 matrix-encoded) input */
+               case A52_DOLBY:
+                       audio->input_channel_layout = HB_INPUT_CH_LAYOUT_DOLBY;
+                       break;
+               /* 3F/2R input */
+               case A52_3F2R:
+                       audio->input_channel_layout = HB_INPUT_CH_LAYOUT_3F2R;
+                       break;
+               /* 3F/1R input */
+               case A52_3F1R:
+                       audio->input_channel_layout = HB_INPUT_CH_LAYOUT_3F1R;
+                       break;
+               /* other inputs */
+               case A52_3F:
+                       audio->input_channel_layout = HB_INPUT_CH_LAYOUT_3F;
+                       break;
+               case A52_2F1R:
+                       audio->input_channel_layout = HB_INPUT_CH_LAYOUT_2F1R;
+                       break;
+               case A52_2F2R:
+                       audio->input_channel_layout = HB_INPUT_CH_LAYOUT_2F2R;
+                       break;
+               /* unknown */
+               default:
+                       audio->input_channel_layout = HB_INPUT_CH_LAYOUT_STEREO;
+       }
+
+       /* add in our own LFE flag if the source has LFE */
+       if (audio->ac3flags & A52_LFE)
+       {
+               audio->input_channel_layout = audio->input_channel_layout | HB_INPUT_CH_LAYOUT_HAS_LFE;
+       }
+
+       snprintf( audio->lang, sizeof( audio->lang ), "%s (%s)", strlen(lang->native_name) ? lang->native_name : lang->eng_name,
+         audio->codec == HB_ACODEC_AC3 ? "AC3" : ( audio->codec == HB_ACODEC_MPGA ? "MPEG" : "LPCM" ) );
+       snprintf( audio->lang_simple, sizeof( audio->lang_simple ), "%s", strlen(lang->native_name) ? lang->native_name : lang->eng_name );
+       snprintf( audio->iso639_2, sizeof( audio->iso639_2 ), "%s", lang->iso639_2);
+
+       if ( (audio->ac3flags & A52_CHANNEL_MASK) == A52_DOLBY ) {
+               sprintf( audio->lang + strlen( audio->lang ),
+                        " (Dolby Surround)" );
+       } else {
+               sprintf( audio->lang + strlen( audio->lang ),
+                        " (%d.%d ch)",
+                       HB_INPUT_CH_LAYOUT_GET_DISCRETE_FRONT_COUNT(audio->input_channel_layout) +
+                       HB_INPUT_CH_LAYOUT_GET_DISCRETE_REAR_COUNT(audio->input_channel_layout),
+                       HB_INPUT_CH_LAYOUT_GET_DISCRETE_LFE_COUNT(audio->input_channel_layout));
+       }
+
+       hb_log( "hb_stream_update_audio: id=%x, lang=%s, 3cc=%s, rate = %d, bitrate = %d, flags = 0x%x (%d)", audio->id, audio->lang, audio->iso639_2, audio->rate, audio->bitrate, audio->ac3flags, audio->ac3flags );
+
+}
+
+void            hb_stream_set_selected_audio_pid_index(hb_stream_t *stream, int i)
+{
+       stream->ts_selected_audio_pid_index = i;
+}
+
+/***********************************************************************
+ * hb_stream_put_back
+ ***********************************************************************
+ *
+ **********************************************************************/
+static void hb_stream_put_back(hb_stream_t *stream, int i)
+{
+       if (stream->stream_type == hb_stream_type_program)
+       {
+               // Program streams are pretty easy - we just reposition the source file
+               // pointer
+               fseeko(stream->file_handle, -(HB_DVD_READ_BUFFER_SIZE-i), SEEK_CUR);
+       }
+       else if (stream->stream_type == hb_stream_type_transport)
+       {
+               int read_buffer_index = stream->ps_current_read_buffer_index;
+               
+               // Transport streams are a little more tricky - so long as the 
+               // amount to back up is still within the current decode buffer
+               // we can just adjust the read pos.
+               if (stream->ps_decode_buffer[read_buffer_index].read_pos - i > 0)
+               {
+                       stream->ps_decode_buffer[read_buffer_index].read_pos -= i;
+               }
+               else
+                 fprintf(stderr, "hb_stream_put_back - trying to step beyond the start of the buffer, read_pos = %d amt to put back = %d\n", stream->ps_decode_buffer[read_buffer_index].read_pos, i);
+       }
+}
+
+
+/***********************************************************************
+ * hb_ts_stream_init
+ ***********************************************************************
+ *
+ **********************************************************************/
+ #define PS_DECODE_BUFFER_SIZE ( 1024 * 1024 * 4)
+static void hb_ts_stream_init(hb_stream_t *stream)
+{
+       // Output Program Stream
+       int i=0;
+       for (i=0; i < kNumDecodeBuffers; i++)
+       {
+               stream->ps_decode_buffer[i].data = (unsigned char *) malloc(PS_DECODE_BUFFER_SIZE);
+               stream->ps_decode_buffer[i].read_pos = 0;
+               stream->ps_decode_buffer[i].size = PS_DECODE_BUFFER_SIZE;
+               stream->ps_decode_buffer[i].len = 0;
+               stream->ps_decode_buffer[i].write_pos = 0;
+       }
+       
+       for (i=0; i < kMaxNumberDecodeStreams; i++)
+       {
+               stream->ts_streamcont[i] = -1;
+       }
+       
+       stream->ps_current_write_buffer_index = 0;
+       stream->ps_current_read_buffer_index = 1;
+       
+       // This is the index (in ts_audio_pids) of the selected
+       // output stream. It should not be set until after all the 
+       // pids in the stream have been discovered.
+       stream->ts_selected_audio_pid_index = -1;
+       
+       stream->debug_output = fopen("/Users/awk/Desktop/hb_debug.mpg", "wb");
+       
+       // Find the audio and video pids in the stream
+       hb_ts_stream_find_pids(stream);
+       
+       for (i=0; i < stream->ts_number_video_pids; i++)
+       {
+               // In progress audio/video data during the transport stream -> program stream processing
+               stream->ts_packetbuf[i] = (unsigned char *) malloc(1024 * 1024);
+               stream->ts_streamid[i] = 0xE0;          // Stream is Video
+       }
+       
+       for (i = stream->ts_number_video_pids; i < stream->ts_number_video_pids + stream->ts_number_audio_pids; i++)
+       {
+               stream->ts_packetbuf[i] = (unsigned char *) malloc(1024 * 1024);
+               stream->ts_streamid[i] = 0xBD;          // Stream 1 is AC-3 Audio
+       }
+}
+
+// ------------------------------------------------------------------------------------
+
+off_t align_to_next_packet(FILE* f)
+{
+       unsigned char buf[188*20];
+
+       off_t start = ftello(f);
+       off_t pos = 0;
+
+       if (fread(buf, 188*20, 1, f) == 1)
+       {
+               int found = 0;
+               while (!found && (pos < 188))
+               {
+                       found = 1;
+                       int i = 0;
+                       for (i = 0; i < 188*20; i += 188)
+                       {
+                               unsigned char c = buf[pos+i];
+                               // Check sync byte
+                               if ((c != 0x47) && (c != 0x72) && (c != 0x29))
+                               {
+                                       // this offset failed, try next
+                                       found = 0;
+                                       pos++;
+                                       break;
+                               }
+                       }
+               }
+       }
+
+       if (pos == 188)
+               pos = 0;                // failed to find anything!!!!!?
+
+       fseek(f, start+pos, SEEK_SET);
+
+       return pos;
+}
+
+// ------------------------------------------------------------------------------------
+
+int bitpos = 0;
+unsigned int bitval = 0;
+unsigned char* bitbuf = NULL;
+unsigned int bitmask[] = {
+       0x0,0x1,0x3,0x7,0xf,0x1f,0x3f,0x7f,0xff,
+       0x1ff,0x3ff,0x7ff,0xfff,0x1fff,0x3fff,0x7fff,0xffff,
+       0x1ffff,0x3ffff,0x7ffff,0xfffff,0x1fffff,0x3fffff,0x7fffff,0xffffff,
+       0x1ffffff,0x3ffffff,0x7ffffff,0xfffffff,0x1fffffff,0x3fffffff,0x7fffffff,0xffffffff};
+
+static inline void set_buf(unsigned char* buf, int bufsize, int clear)
+{
+       bitpos = 0;
+       bitbuf = buf;
+       bitval = (bitbuf[0] << 24) | (bitbuf[1] << 16) | (bitbuf[2] << 8) | bitbuf[3];
+       if (clear)
+               memset(bitbuf, 0, bufsize);
+}
+
+static inline int buf_size()
+{
+       return bitpos >> 3;
+}
+
+static inline void set_bits(unsigned int val, int bits)
+{
+       val &= bitmask[bits];
+
+       while (bits > 0)
+       {
+               int bitsleft = (8 - (bitpos & 7));
+               if (bits >= bitsleft)
+               {
+                       bitbuf[bitpos >> 3] |= val >> (bits - bitsleft);
+                       bitpos += bitsleft;
+                       bits -= bitsleft;
+                       val &= bitmask[bits];
+               }
+               else
+               {
+                       bitbuf[bitpos >> 3] |= val << (bitsleft - bits);
+                       bitpos += bits;
+                       bits = 0;
+               }
+       }
+}
+
+static inline unsigned int get_bits(int bits)
+{
+       unsigned int val;
+       int left = 32 - (bitpos & 31);
+
+       if (bits < left)
+       {
+               val = (bitval >> (left - bits)) & bitmask[bits];
+               bitpos += bits;
+       }
+       else
+       {
+               val = (bitval & bitmask[left]) << (bits - left);
+               bitpos += left;
+               bits -= left;
+
+               int pos = bitpos >> 3;
+               bitval = (bitbuf[pos] << 24) | (bitbuf[pos + 1] << 16) | (bitbuf[pos + 2] << 8) | bitbuf[pos + 3];
+               
+               if (bits > 0)
+               {
+                       val |= (bitval >> (32 - bits)) & bitmask[bits];
+                       bitpos += bits;
+               }
+       }
+
+       return val;
+}
+
+// ------------------------------------------------------------------------------------
+
+int decode_program_map(unsigned char *buf, hb_stream_t *stream)
+{
+    unsigned char tablebuf[1024];
+    unsigned int tablepos = 0;
+    
+    int reading = 0;
+
+
+    // Get adaption header info
+    int adapt_len = 0;
+    int adaption = (buf[3] & 0x30) >> 4;
+    if (adaption == 0)
+            return 0;
+    else if (adaption == 0x2)
+            adapt_len = 184;
+    else if (adaption == 0x3)
+            adapt_len = buf[4] + 1;
+    if (adapt_len > 184)
+            return 0;
+
+    // Get pointer length
+    int pointer_len = buf[4 + adapt_len] + 1;
+
+    // Get payload start indicator
+    int start;
+    start = (buf[1] & 0x40) != 0;
+
+    if (start)
+            reading = 1;
+
+    // Add the payload for this packet to the current buffer
+    if (reading && (184 - adapt_len) > 0)
+    {
+            if (tablepos + 184 - adapt_len - pointer_len > 1024)
+            {
+                    hb_log("decode_program_map - Bad program section length (> 1024)");
+                    return 0;
+            }
+            memcpy(tablebuf + tablepos, buf + 4 + adapt_len + pointer_len, 184 - adapt_len - pointer_len);
+            tablepos += 184 - adapt_len - pointer_len;
+    }
+
+    if (start && reading)
+    {
+            int done_reading_stream_types = 0;
+            
+            memcpy(tablebuf + tablepos, buf + 4 + adapt_len + 1, pointer_len - 1);
+
+            unsigned int pos = 0;
+            set_buf(tablebuf + pos, tablepos - pos, 0);
+
+            unsigned char section_id   = get_bits(8);
+                                                                      get_bits(4);
+            unsigned int section_length = get_bits(12);
+            unsigned int program_number = get_bits(16);
+                                                                        get_bits(2);
+            unsigned char version_number = get_bits(5);
+                                                                      get_bits(1);
+            unsigned char section_number = get_bits(8);
+            unsigned char last_section_number = get_bits(8);
+                                                                      get_bits(3);
+            unsigned int PCR_PID = get_bits(13);
+                                                                      get_bits(4);
+            unsigned int program_info_length = get_bits(12);
+            int i=0;
+            unsigned char *descriptor_buf = (unsigned char *) malloc(program_info_length);
+            for (i = 0; i < program_info_length; i++)
+            {
+              descriptor_buf[i] = get_bits(8);
+            }                                 
+            
+            int cur_pos =  9 /* data so far */ + program_info_length;
+            done_reading_stream_types = 0;
+            while (!done_reading_stream_types)
+            {
+              unsigned char stream_type = get_bits(8);
+                  get_bits(3);
+              unsigned int elementary_PID = get_bits(13);
+                  get_bits(4);
+              unsigned int ES_info_length = get_bits(12);
+              
+              int i=0;
+              unsigned char *ES_info_buf = (unsigned char *) malloc(ES_info_length);
+              for (i=0; i < ES_info_length; i++)
+              {
+                ES_info_buf[i] = get_bits(8);
+              }
+            
+              if (stream_type == 0x02)
+              {
+                if (stream->ts_number_video_pids <= kMaxNumberVideoPIDS)
+                  stream->ts_number_video_pids++;
+                stream->ts_video_pids[stream->ts_number_video_pids-1] = elementary_PID;
+              }
+              if ((stream_type == 0x04) || (stream_type == 0x81) || (stream_type == 0x03) || (stream_type == 0x06))    // ATSC Defines stream type 0x81 for AC-3/A52 audio, there's also some evidence of streams using type 6 for AC-3 audio too
+              {
+                if (stream->ts_number_audio_pids <= kMaxNumberAudioPIDS)
+                  stream->ts_number_audio_pids++;
+                stream->ts_audio_pids[stream->ts_number_audio_pids-1] =  elementary_PID;
+
+                stream->a52_info[stream->ts_number_audio_pids-1].lang_code = 'e' << 8 | 'n';
+                               stream->ts_audio_stream_type[stream->ts_number_audio_pids-1] = stream_type;
+                               
+                if (ES_info_length > 0)
+                {
+                  hb_log("decode_program_map - Elementary Stream Info Present, decode language codes ?");
+                }
+
+              }
+
+              cur_pos += 5 /* stream header */ + ES_info_length;
+              
+              free(ES_info_buf);
+              
+              if (cur_pos >= section_length - 4 /* stop before the CRC */)
+                done_reading_stream_types = 1;
+            }
+                                     
+            free(descriptor_buf);
+    }
+    
+    return 1;
+}
+
+int decode_PAT(unsigned char *buf, unsigned int *program_num, unsigned int *network_PID, unsigned int *program_map_PID)
+{
+//    int maxchannels = 8;
+//    static ATSC_CHANNEL_INFO* channels;
+//
+//    if (channels == NULL)
+//      channels = (ATSC_CHANNEL_INFO*) malloc(maxchannels * sizeof(ATSC_CHANNEL_INFO));
+//      
+//    int numchannels;
+
+    unsigned char tablebuf[1024];
+    unsigned int tablepos = 0;
+    
+    int reading = 0;
+
+
+    // Get adaption header info
+    int adapt_len = 0;
+    int adaption = (buf[3] & 0x30) >> 4;
+    if (adaption == 0)
+            return 0;
+    else if (adaption == 0x2)
+            adapt_len = 184;
+    else if (adaption == 0x3)
+            adapt_len = buf[4] + 1;
+    if (adapt_len > 184)
+            return 0;
+
+    // Get pointer length
+    int pointer_len = buf[4 + adapt_len] + 1;
+
+    // Get payload start indicator
+    int start;
+    start = (buf[1] & 0x40) != 0;
+
+    if (start)
+            reading = 1;
+
+    // Add the payload for this packet to the current buffer
+    if (reading && (184 - adapt_len) > 0)
+    {
+            if (tablepos + 184 - adapt_len - pointer_len > 1024)
+            {
+                    hb_log("decode_PAT - Bad program section length (> 1024)");
+                    return 0;
+            }
+            memcpy(tablebuf + tablepos, buf + 4 + adapt_len + pointer_len, 184 - adapt_len - pointer_len);
+            tablepos += 184 - adapt_len - pointer_len;
+    }
+
+    if (start && reading)
+    {
+            memcpy(tablebuf + tablepos, buf + 4 + adapt_len + 1, pointer_len - 1);
+
+            unsigned int pos = 0;
+            //while (pos < tablepos)
+            {
+                    set_buf(tablebuf + pos, tablepos - pos, 0);
+
+                    unsigned char section_id   = get_bits(8);
+                                                                              get_bits(4);
+                    unsigned int section_len   = get_bits(12);
+                    unsigned int transport_id  = get_bits(16);
+                                                                              get_bits(2);
+                    unsigned int version_num   = get_bits(5);
+                    unsigned int current_next  = get_bits(1);
+                    unsigned int section_num   = get_bits(8);
+                    unsigned int last_section  = get_bits(8);
+//                    unsigned int protocol_ver        = get_bits(8);
+
+                    switch (section_id)
+                    {
+                      case 0x00:
+                        {
+                          // Program Association Section
+                          section_len -= 5;    // Already read transport stream ID, version num, section num, and last section num
+                          section_len -= 4;   // Ignore the CRC
+                          int curr_pos = 0;
+                          while (curr_pos < section_len)
+                          {
+                            unsigned int pkt_program_num = get_bits(16);
+                            if (program_num)
+                              *program_num = pkt_program_num;
+                              
+                            get_bits(3);  // Reserved
+                            if (pkt_program_num == 0)
+                            {
+                              unsigned int pkt_network_PID = get_bits(13);
+//                              printf("PAT - Transport ID = 0x%x (%d) program_num 0x%x (%d) network_PID = 0x%x (%d)\n", transport_id, transport_id, pkt_program_num, pkt_program_num, pkt_network_PID, pkt_network_PID);
+                              if (network_PID)
+                                *network_PID = pkt_network_PID;
+                                
+                            }
+                            else
+                            {
+                              unsigned int pkt_program_map_PID = get_bits(13);
+//                              printf("PAT - Transport ID = 0x%x (%d) program_num 0x%x (%d) program_map_PID = 0x%x (%d)\n", transport_id, transport_id, pkt_program_num, pkt_program_num, pkt_program_map_PID, pkt_program_map_PID);
+                              if (program_map_PID)
+                                *program_map_PID = pkt_program_map_PID;
+                            }
+                            curr_pos += 4;
+                          }
+                        }
+                        break;
+                      case 0xC7:
+                            {
+                                    break;
+                            }
+                      case 0xC8:
+                            {
+                                    break;
+                            }
+                    }
+
+                    pos += 3 + section_len;
+            }
+
+            tablepos = 0;
+    }
+    return 1;
+}
+
+static int flushbuf(hb_stream_t *stream)
+{
+       int old_write_index = stream->ps_current_write_buffer_index;
+
+       if (stream->debug_output)
+       {
+               fwrite(stream->ps_decode_buffer[stream->ps_current_write_buffer_index].data, stream->ps_decode_buffer[stream->ps_current_write_buffer_index].len, 1, stream->debug_output);
+       }
+       
+       // Flip the buffers and start moving on to the next
+       stream->ps_current_write_buffer_index++;
+       if (stream->ps_current_write_buffer_index > kNumDecodeBuffers-1)
+               stream->ps_current_write_buffer_index = 0;
+       
+       if ( (stream->ps_decode_buffer[stream->ps_current_write_buffer_index].len != 0) || (stream->ps_decode_buffer[stream->ps_current_write_buffer_index].write_pos != 0) )
+       {
+               hb_log("flushbuf - new buffer (index %d) has non zero length and write position !", stream->ps_current_write_buffer_index);
+               return 0;
+       }
+       
+       stream->ps_current_read_buffer_index = old_write_index;
+       stream->ps_decode_buffer[stream->ps_current_read_buffer_index].read_pos = 0;
+       
+       return 1;
+}
+
+static int fwrite64(void* buf, int elsize, int elnum, hb_stream_t* stream)
+{
+       int size = elsize;
+       if (elnum > 1)
+               size *= elnum;
+       
+       int written = 0;
+       int current_write_index = stream->ps_current_write_buffer_index;
+       
+       if (size <= stream->ps_decode_buffer[current_write_index].size - stream->ps_decode_buffer[current_write_index].write_pos)
+       {
+               memcpy(stream->ps_decode_buffer[current_write_index].data + stream->ps_decode_buffer[current_write_index].write_pos, buf, size);
+               stream->ps_decode_buffer[current_write_index].write_pos += size;
+               stream->ps_decode_buffer[current_write_index].len = stream->ps_decode_buffer[current_write_index].write_pos;
+               written = size;
+       }
+       else
+       {
+               memcpy(stream->ps_decode_buffer[current_write_index].data + stream->ps_decode_buffer[current_write_index].write_pos, buf, stream->ps_decode_buffer[current_write_index].size - stream->ps_decode_buffer[current_write_index].write_pos);
+               written += stream->ps_decode_buffer[current_write_index].size - stream->ps_decode_buffer[current_write_index].write_pos;
+               stream->ps_decode_buffer[current_write_index].write_pos += stream->ps_decode_buffer[current_write_index].size - stream->ps_decode_buffer[current_write_index].write_pos;
+               stream->ps_decode_buffer[current_write_index].len = stream->ps_decode_buffer[current_write_index].write_pos;
+
+               if (flushbuf(stream))
+               {
+                       // FLushing the buffer will have change the current write buffer
+                       current_write_index = stream->ps_current_write_buffer_index;
+                       
+                       memcpy(stream->ps_decode_buffer[current_write_index].data, (unsigned char*)buf + written, size - written);
+                       stream->ps_decode_buffer[current_write_index].write_pos += size - written;
+                       stream->ps_decode_buffer[current_write_index].len = stream->ps_decode_buffer[current_write_index].write_pos;
+                       written += size - written;
+               }
+       }
+
+
+       if (elnum == 1 && written == size)
+               return 1;
+       else
+               return written / elsize;
+}
+
+static int write_pack(hb_stream_t* stream, int64_t time)
+{
+       unsigned char buf[64];
+       set_buf(buf, 64, 1);                                            // clear buffer
+
+       int64_t ext_time = time % 300;
+       time = time / 300;
+
+       set_bits(0x000001ba, 32);                                       // pack id                                                              32
+       set_bits(1, 2);                                                         // 0x01                                                                 2
+       set_bits((unsigned int)(time >> 30), 3);        // system_clock_reference_base                  3
+       set_bits(1, 1);                                                         // marker_bit                                                   1
+       set_bits((unsigned int)(time >> 15), 15);       // system_clock_reference_base                  15
+       set_bits(1, 1);                                                         // marker_bit                                                   1
+       set_bits((unsigned int)time, 15);                       // system_clock_reference_base1                 15
+       set_bits(1, 1);                                                         // marker_bit                                                   1
+       set_bits((unsigned int)ext_time, 9);            // system_clock_reference_extension             9
+       set_bits(1, 1);                                                         // marker_bit                                                   1
+       set_bits(DEMUX, 22);                                            // program_mux_rate                                             22
+       set_bits(1, 1);                                                         // marker_bit                                                   1
+       set_bits(1, 1);                                                         // marker_bit                                                   1
+       set_bits(31, 5);                                                        // reserved                                                             5
+       set_bits(0, 3);                                                         // pack_stuffing_length                                 3
+
+       return fwrite64(buf, buf_size(), 1, stream) == 1;
+}
+
+static int pad_buffer(hb_stream_t *stream, int pad)
+{
+       pad -= 6;
+
+       char buf[6];
+       buf[0] = '\x0'; buf[1] = '\x0'; buf[2] = '\x1'; buf[3] = '\xbe';
+       buf[4] = pad >> 8; buf[5] = pad & 0xff;
+
+       if (fwrite64(buf, 6, 1, stream) != 1)
+               return 0;
+
+       unsigned char padbyte = 0xff;
+       int i=0;
+       for (i = 0; i < pad; i++)
+       {
+               if (fwrite64(&padbyte, 1, 1, stream) != 1)
+                       return 0;
+       }
+
+       return 1;
+}
+
+int make_pes_header(unsigned char* buf, int streamid, int len, int64_t PTS, int64_t DTS)
+{
+       int hdrlen = 0;
+       int PTS_DTS_flags = 0;
+       if (PTS != -1)
+       {
+               if (DTS != -1)
+               {
+                       PTS_DTS_flags = 3;
+                       hdrlen += 10;
+               }
+               else
+               {
+                       PTS_DTS_flags = 2;
+                       hdrlen += 5;
+               }
+       }
+
+       set_buf(buf, 9 + hdrlen, 1);                            // clear the buffer
+
+       set_bits(0x000001, 24);                                         // packet_start_code_prefix                             24
+       set_bits((unsigned int)streamid, 8);            // directory_stream_id                                  8
+       set_bits(len, 16);                                                      // PES_packet_length                                    16
+       set_bits(0x2, 2);                                                       // '10'                                                                 2
+       set_bits(0, 2);                                                         // PES_scrambling_control                               2
+       set_bits(1, 1);                                                         // PES_priority                                                 1
+       set_bits(0, 1);                                                         // data_alignment_indicator                             1
+       set_bits(0, 1);                                                         // copyright                                                    1
+       set_bits(0, 1);                                                         // original_or_copy                                             1
+       set_bits(PTS_DTS_flags, 2);                                     // PTS_DTS_flags                                                2
+       set_bits(0, 1);                                                         // ESCR_flag                                                    1
+       set_bits(0, 1);                                                         // ES_rate_flag                                                 1
+       set_bits(0, 1);                                                         // DSM_trick_mode_flag                                  1
+       set_bits(0, 1);                                                         // additional_copy_info_flag                    1
+       set_bits(0, 1);                                                         // PES_CRC_flag                                                 1
+       set_bits(0, 1);                                                         // PES_extension_flag                                   1
+       set_bits(hdrlen, 8);                                            // PES_header_data_length                               8
+       
+       if (PTS_DTS_flags == 2)
+       {
+               set_bits(2, 4);                                                         // '0010'                                                       4
+               set_bits((unsigned int)(PTS >> 30), 3);         // PTS [32..30]                                         3
+               set_bits(1, 1);                                                         // marker bit                                           1
+               set_bits((unsigned int)(PTS >> 15), 15);        // PTS [29..15]                                         15
+               set_bits(1, 1);                                                         // marker bit                                           1
+               set_bits((unsigned int)PTS, 15);                        // PTS [14..0]                                          15
+               set_bits(1, 1);                                                         // marker bit                                           1
+       }
+       else if (PTS_DTS_flags == 3)
+       {
+               set_bits(3, 4);                                                         // '0011'                                                       4
+               set_bits((unsigned int)(PTS >> 30), 3);         // PTS [32..30]                                         3
+               set_bits(1, 1);                                                         // marker bit                                           1
+               set_bits((unsigned int)(PTS >> 15), 15);        // PTS [29..15]                                         15
+               set_bits(1, 1);                                                         // marker bit                                           1
+               set_bits((unsigned int)PTS, 15);                        // PTS [14..0]                                          15
+               set_bits(1, 1);                                                         // marker bit                                           1
+               set_bits(1, 4);                                                         // '0001'                                                       4
+               set_bits((unsigned int)(DTS >> 30), 3);         // DTS [32..30]                                         3
+               set_bits(1, 1);                                                         // marker bit                                           1
+               set_bits((unsigned int)(DTS >> 15), 15);        // DTS [29..15]                                         15
+               set_bits(1, 1);                                                         // marker bit                                           1
+               set_bits((unsigned int)DTS, 15);                        // DTS [14..0]                                          15
+               set_bits(1, 1);                                                         // marker bit                                           1
+       }
+
+       return buf_size();
+}
+
+int generate_output_data(hb_stream_t *stream, int write_ac3, int curstream, int pid)
+{
+                       unsigned char ac3_substream_id[4];
+                       int ac3len = 0;
+                       
+                       if (write_ac3)
+                       {
+                               // Make a four byte ac3 streamid
+                               ac3_substream_id[0] = 0x80;     // Four byte AC3 CODE??
+                               ac3_substream_id[1] = 0x01;
+                               ac3_substream_id[2] = 0x00;     // WHY???  OH WHY??
+                               ac3_substream_id[3] = 0x02;
+                               ac3len = 4;
+                       }
+                       
+                       int written = 0;        // Bytes we've written to output file
+                       int pos = 0;            // Position in PES packet buffer
+                       
+                       for (;;)
+                       {
+//                             int64_t fpos = ftell64(fout);
+                               if ((stream->ps_decode_buffer[stream->ps_current_write_buffer_index].len % HB_DVD_READ_BUFFER_SIZE) != 0)
+                               {
+                                       hb_log("write_output_stream - Packet's not falling on read buffer size boundries!");
+                                       return 1;
+                               }
+
+                               // Get total length of this pack
+                               int len = min(14 + ac3len + stream->ts_packetpos[curstream] - pos, HB_DVD_READ_BUFFER_SIZE);
+
+                               // Figure out stuffing (if we have less than 16 bytes left)
+                               int stuffing = 0;
+                               if (len < HB_DVD_READ_BUFFER_SIZE && HB_DVD_READ_BUFFER_SIZE - len < 16)
+                               {
+                                       stuffing = HB_DVD_READ_BUFFER_SIZE - len;
+                                       len += stuffing;
+                               }
+
+                               // Write out pack header
+                               off_t file_offset = ftello(stream->file_handle);
+                               int64_t packet_time = (file_offset * CLOCKRATE / STREAMRATE) + 0 /*file_time*/;
+                               if (!write_pack(stream, packet_time))
+                               {
+                                       hb_log("write_output_stream - Couldn't write pack header!");
+                                       return 1;
+                               }
+
+//                             if (pid == stream->ts_audio_pids[0])
+//                                     stream->ts_packetbuf[curstream][pos + 3] = stream->ts_streamid[kAudioStream];
+//                             else
+//                                     stream->ts_packetbuf[curstream][pos + 3] = stream->ts_streamid[kVideoStream];
+                               int index_of_selected_pid = -1;
+                               if ((index_of_selected_pid = index_of_video_pid(pid,stream)) < 0)
+                               {
+                                       if ((index_of_selected_pid = index_of_audio_pid(pid,stream)) < 0)
+                                       {
+                                               hb_log("generate_output_data - cannot find pid 0x%x (%d) in selected audio or video pids", pid, pid);
+                                               return 0;
+                                       }
+                                       else
+                                       {
+                                               stream->ts_packetbuf[curstream][pos + 3] = stream->ts_streamid[stream->ts_number_video_pids + index_of_selected_pid];
+                                       }
+                               }
+                               else
+                                       stream->ts_packetbuf[curstream][pos + 3] = stream->ts_streamid[index_of_selected_pid];
+
+                               // Packet length..
+                               // Subtract pack size (14) and pes id and len (6) from lenth
+                               stream->ts_packetbuf[curstream][pos + 4] = (len - 6 - 14) >> 8; stream->ts_packetbuf[curstream][pos + 5] = (len - 6 - 14) & 0xFF;
+
+                               // Add any stuffing bytes to header extra len
+                               int hdrsize = 9 + stream->ts_packetbuf[curstream][pos + 8];
+                               stream->ts_packetbuf[curstream][pos + 8] += stuffing;                                   // Add stuffing to header bytes
+
+                               // Write out id, streamid, len
+                               if (fwrite64(stream->ts_packetbuf[curstream] + pos, hdrsize, 1, stream) != 1)   // Write pes id, streamid, and len
+                               {
+                                       hb_log("write_output_stream - Failed to write output file!");
+                                       return 1;
+                               }
+                               
+                               // Write stuffing
+                               int i=0;
+                               for (i = 0; i < stuffing; i++)                          // Write any stuffing bytes
+                               {
+                                       unsigned char stuff = 0xff;
+                                       if (fwrite64(&stuff, 1, 1, stream) != 1)
+                                       {
+                                               hb_log("write_output_stream - Failed to write output file!");
+                                               return 1;
+                                       }
+                               }
+
+                               // Write ac3 streamid
+                               if (ac3len != 0)
+                               {
+                                       if (fwrite64(ac3_substream_id, ac3len, 1, stream) != 1)
+                                       {
+                                               hb_log("write_output_stream - Failed to write output file!");
+                                               return 1;
+                                       }
+                               }
+
+                               // Write rest of data len minus headersize (9) stuffing, and pack size (14)
+                               if (fwrite64(stream->ts_packetbuf[curstream] + pos + hdrsize, len - hdrsize - 14 - stuffing - ac3len, 1, stream) != 1)  // Write data bytes
+                               {
+                                       hb_log("write_output_stream - Failed to write output file!");
+                                       return 1;
+                               }
+                               written += len;
+
+                               // Add len minus stuff we added like the pack (14) and the stuffing.
+                               pos += len - 14 - stuffing - ac3len;
+                               if (pos == stream->ts_packetpos[curstream])
+                                       break;
+
+                               // Add pes header for next packet
+                               pos -= 9;
+//                             make_pes_header(stream->ts_packetbuf[curstream] + pos, (pid == stream->ts_video_pids[0] ? stream->ts_streamid[kVideoStream] : stream->ts_streamid[kAudioStream]), 0, -1, -1);
+                               make_pes_header(stream->ts_packetbuf[curstream] + pos, stream->ts_streamid[curstream], 0, -1, -1);
+                       }
+
+                       // Write padding
+                       if ((written % HB_DVD_READ_BUFFER_SIZE) != 0)
+                       {
+                               int left = HB_DVD_READ_BUFFER_SIZE - (written % HB_DVD_READ_BUFFER_SIZE);
+
+                               // Pad out to HB_DVD_READ_BUFFER_SIZE bytes
+                               if (!pad_buffer(stream, left))
+                               {
+                                       hb_log("write_output_stream - Couldn't write pad buffer!");
+                                       return 1;
+                               }
+                       }
+
+                       stream->ts_packetpos[curstream] = 0;
+                       stream->ts_streamcont[curstream] = -1;
+
+       return 0;
+}
+
+static void hb_ts_handle_mpeg_audio(hb_stream_t *stream, int curstream, unsigned char* buf, int adapt_len )
+{
+       // Although we don't have AC3/A52 audio here we can still use the same structure to record this useful information.
+       
+       stream->a52_info[curstream - stream->ts_number_video_pids].flags = A52_STEREO;
+       stream->a52_info[curstream - stream->ts_number_video_pids].rate = 48000 /*Hz*/;
+       stream->a52_info[curstream - stream->ts_number_video_pids].bitrate = 384000 /*Bps*/;
+}
+
+static int hb_ts_handle_ac3_audio(hb_stream_t *stream, int curstream, unsigned char* buf, int adapt_len )
+{
+       int spos, dpos;
+
+       // Make sure we start with 0x0b77
+       if (stream->ts_packetbuf[curstream][9 + stream->ts_packetbuf[curstream][8]] != 0x0b || stream->ts_packetbuf[curstream][9 + stream->ts_packetbuf[curstream][8] + 1] != 0x77)
+       {
+               spos = 9 + stream->ts_packetbuf[curstream][8];
+               dpos = 9 + stream->ts_packetbuf[curstream][8];
+               while (spos <= stream->ts_packetpos[curstream] - 2 && !(stream->ts_packetbuf[curstream][spos] == 0x0b && stream->ts_packetbuf[curstream][spos + 1] == 0x77))
+                       spos++;
+
+               if (!(stream->ts_packetbuf[curstream][spos] == 0x0b && stream->ts_packetbuf[curstream][spos + 1] == 0x77))
+               {
+                       hb_log("hb_ts_stream_decode - Couldn't sync AC3 packet!");
+                       stream->ts_skipbad[curstream] = 1;
+                       return 0;
+               }
+
+               while (spos < stream->ts_packetpos[curstream])
+               {
+                       stream->ts_packetbuf[curstream][dpos] = stream->ts_packetbuf[curstream][spos];
+                       spos++;
+                       dpos++;
+               }
+               stream->ts_packetpos[curstream] = dpos;
+       }
+
+       // Check the next packet to make sure IT starts with a 0x0b77
+       int plen = 0;
+//                                     if (buf[4 + adapt_len] == 0 && buf[4 + adapt_len + 1] == 0 &&           // Starting with an mpeg header?
+//                                             buf[4 + adapt_len + 2] == 1 && buf[4 + adapt_len + 3] == 0xBD)
+                       plen = 9 + buf[4 + adapt_len + 8];
+       int pstart = 4 + adapt_len + plen;
+       if (buf[pstart] != 0x0b || buf[pstart + 1] != 0x77)
+       {
+               spos = pstart;
+               while (spos < 188 - 2 && !(buf[spos] == 0x0b && buf[spos + 1] == 0x77))
+               {
+                       stream->ts_packetbuf[curstream][stream->ts_packetpos[curstream]] = buf[spos];
+                       stream->ts_packetpos[curstream]++;
+                       spos++;
+               }
+
+               if (!(buf[spos] == 0x0b && buf[spos + 1] == 0x77))
+               {
+                       hb_log("hb_ts_stream_decode - Couldn't sync AC3 packet!");
+                       stream->ts_skipbad[curstream] = 1;
+                       return 0;
+               }
+
+               adapt_len = spos - 4 - plen;
+
+               dpos = spos - 1;
+               spos = pstart - 1;
+               while (spos >= pstart - plen)
+               {
+                       buf[dpos] = buf[spos];
+                       spos--;
+                       dpos--;
+               }
+       }
+
+       int flags, rate, bitrate;
+       if( a52_syncinfo( &buf[pstart], &flags, &rate, &bitrate ) )
+       {
+               stream->a52_info[curstream - stream->ts_number_video_pids].flags = flags;
+               stream->a52_info[curstream - stream->ts_number_video_pids].rate = rate;
+               stream->a52_info[curstream - stream->ts_number_video_pids].bitrate = bitrate;
+       }
+       return 1;
+}
+
+static void hb_ts_stream_find_pids(hb_stream_t *stream)
+{
+       unsigned char buf[188];
+       int curstream = 0;
+
+       // Stream ID info
+       unsigned int program_num = 0;
+       unsigned int network_PID = 0;
+       unsigned int program_map_PID = 0;
+
+       // align to first packet
+       align_to_next_packet(stream->file_handle);
+
+       // Read the Transport Stream Packets (188 bytes each) looking at first for PID 0 (the PAT PID), then decode that
+       // to find the program map PID and then decode that to get the list of audio and video PIDs
+       
+       int bytesReadInPacket = 0;
+       for (;;)
+       {
+               // Try to read packet..
+               int bytesRead;
+               if ((bytesRead = fread(buf+bytesReadInPacket, 1, 188-bytesReadInPacket, stream->file_handle)) != 188-bytesReadInPacket)
+               {
+                       if (bytesRead < 0)
+                               bytesRead = 0;
+                       bytesReadInPacket += bytesRead;
+
+                       hb_log("hb_ts_stream_find_pids - end of file");
+                       break;  
+               }
+               else
+               {
+//                     curfilepos += bytesRead;
+                       bytesReadInPacket = 0;
+               }
+
+               // Check sync byte
+               if ((buf[0] != 0x47) && (buf[0] != 0x72) && (buf[0] != 0x29))
+               {
+//                     __int64 pos = ftell64(fin);
+                       hb_log("hb_ts_stream_find_pids - Bad transport packet (no sync byte 0x47)!");
+                       int i = 0;
+                       for (i=0; i < stream->ts_number_video_pids + stream->ts_number_audio_pids; i++)
+                               stream->ts_skipbad[i] = 1;
+//                     stream->ts_skipbad[kAudioStream] = stream->ts_skipbad[kVideoStream] = 1;
+                       continue;
+               }
+
+               // Get pid
+               int pid = (((buf[1] & 0x1F) << 8) | buf[2]) & 0x1FFF;
+                
+                if ((pid == 0x0000) && (program_num == 0))
+                {
+                  decode_PAT(buf, &program_num, &network_PID, &program_map_PID);
+                  continue;
+                }
+                
+                if (pid == 0x1ffb)
+                {
+                  printf("Need to decode PSIP data !\n");
+                  continue;
+                }
+                
+                if ((network_PID > 0) && (pid == network_PID))
+                {
+                  printf("Need to Decode network PID section !\n");
+                  continue;
+                }
+                
+                if ((program_map_PID > 0) && (pid == program_map_PID))
+                {
+                  decode_program_map(buf, stream);
+                  break;;
+                }
+                
+                // Skip until we have a complete set of PIDs
+                if ((stream->ts_number_video_pids == 0) || (stream->ts_number_audio_pids == 0))
+                  continue;
+               }
+               
+               hb_log("hb_ts_stream_find_pids - found the following PIDS");
+               hb_log("    Video PIDS : ");
+               int i=0;
+               for (i=0; i < stream->ts_number_video_pids; i++)
+               {
+                       hb_log("      0x%x (%d)", stream->ts_video_pids[i], stream->ts_video_pids[i]);
+               }
+               hb_log("    Audio PIDS : ");
+               for (i = 0; i < stream->ts_number_audio_pids; i++)
+               {
+                       hb_log("      0x%x (%d)", stream->ts_audio_pids[i], stream->ts_audio_pids[i]);
+               }
+ }
+
+int index_of_video_pid(int pid, hb_stream_t *stream)
+{
+       int found_pid = -1, i = 0;
+       
+       for (i = 0; (i < stream->ts_number_video_pids) && (found_pid < 0); i++)
+       {
+               if (pid == stream->ts_video_pids[i])
+                       found_pid = i;
+       }
+       return found_pid;
+}
+
+int index_of_audio_pid(int pid, hb_stream_t *stream)
+{
+       int i = 0, found_pid = -1;
+
+       // If a selected audio pid index has been set it indicates
+       // which of the potential pids we need to output so only return
+       // that index for the appropriate pid. Other pids should just
+       // be ignored.
+       if (stream->ts_selected_audio_pid_index >= 0) 
+       {
+               if (pid == stream->ts_audio_pids[stream->ts_selected_audio_pid_index])
+                       return stream->ts_selected_audio_pid_index;
+               else
+                       return -1;
+       }
+       
+       // If no specific pid index is set then we're probably just gathering
+       // pid and/or stream information (during DecodePreviews for example)
+       // so return the appropriate index
+       for (i = 0; (i < stream->ts_number_audio_pids) && (found_pid < 0); i++)
+       {
+               if (pid == stream->ts_audio_pids[i])
+                       found_pid = i;
+       }
+       return found_pid;
+}
+
+int index_of_pid(int pid, hb_stream_t *stream)
+{
+       int found_pid = -1;
+       
+       if ((found_pid = index_of_video_pid(pid, stream)) >= 0)
+               return found_pid;
+       
+       if ((found_pid = index_of_audio_pid(pid, stream)) >= 0)
+               return found_pid;
+               
+       return found_pid;
+}
+
+/***********************************************************************
+ * hb_ts_stream_decode
+ ***********************************************************************
+ *
+ **********************************************************************/
+static void hb_ts_stream_decode(hb_stream_t *stream)
+{
+       unsigned char buf[188];
+       int curstream;
+       int doing_iframe;
+       
+       int i = 0;
+       for (i=0; i < stream->ts_number_video_pids + stream->ts_number_audio_pids; i++)
+       {
+//     stream->ts_skipbad[kAudioStream] = stream->ts_skipbad[kVideoStream] = 0;
+               stream->ts_skipbad[i] = 0;
+       }
+       
+       doing_iframe = 0;
+       
+       if ((stream->ts_number_video_pids == 0) || (stream->ts_number_audio_pids == 0))
+       {
+               hb_log("hb_ts_stream_decode  - no Video or Audio PID selected, cannot decode transport stream");
+               return;
+       }
+       
+       int bytesReadInPacket = 0;
+       int curr_write_buffer_index = stream->ps_current_write_buffer_index;
+       
+       // Write output data until a buffer switch occurs.
+       while (curr_write_buffer_index == stream->ps_current_write_buffer_index)
+       {
+               // Try to read packet..
+               int bytesRead;
+               if ((bytesRead = fread(buf+bytesReadInPacket, 1, 188-bytesReadInPacket, stream->file_handle)) != 188-bytesReadInPacket)
+               {
+                       if (bytesRead < 0)
+                               bytesRead = 0;
+                       bytesReadInPacket += bytesRead;
+
+                       // Flush any outstanding output data - we're done here.
+                       flushbuf(stream);
+                       break;
+               }
+               else
+               {
+//                     curfilepos += bytesRead;
+                       bytesReadInPacket = 0;
+               }
+
+               // Check sync byte
+               if ((buf[0] != 0x47) && (buf[0] != 0x72) && (buf[0] != 0x29))
+               {
+//                     __int64 pos = ftell64(fin);
+                       hb_log("hb_ts_stream_decode - Bad transport packet (no sync byte 0x47)!");
+                       for (i=0; i < stream->ts_number_video_pids + stream->ts_number_audio_pids; i++)
+                       {
+               //      stream->ts_skipbad[kAudioStream] = stream->ts_skipbad[kVideoStream] = 1;
+                               stream->ts_skipbad[i] = 1;
+                       }
+                       continue;
+               }
+
+               // Get pid
+               int pid = (((buf[1] & 0x1F) << 8) | buf[2]) & 0x1FFF;
+
+               // Skip this block
+               if (index_of_pid(pid, stream) < 0)
+                       continue;
+//             if (pid != stream->ts_audio_pids[0] && pid != stream->ts_video_pids[0])
+//                     continue;
+
+               // Get the pos and buf - we organize our streams as 'n' video streams then 'm' audio streams
+               int index_of_selected_pid = -1;
+               if ((index_of_selected_pid = index_of_video_pid(pid,stream)) < 0)
+               {
+                       // Not a video PID perhaps audio ?
+                       if ((index_of_selected_pid = index_of_audio_pid(pid,stream)) < 0)
+                       {
+                               hb_log("hb_ts_stream_decode - Unknown pid 0x%x (%d)", pid, pid);
+                               continue;
+                       }
+                       else
+                       {
+                               curstream = stream->ts_number_video_pids + index_of_selected_pid;
+                               if (curstream > kMaxNumberDecodeStreams)
+                               {
+                                       hb_log("hb_ts_stream_decode - Too many streams %d", curstream);
+                                       continue;
+                               }
+                       }
+               }
+               else
+                       curstream = index_of_selected_pid;
+               
+//             if (pid == stream->ts_video_pids[0])
+//                     curstream = 0;
+//             else
+//                     curstream = 1;
+
+               // Get start code
+               int start;
+               start = (buf[1] & 0x40) != 0;
+                  
+               if (!start && stream->ts_skipbad[curstream])
+                       continue;
+
+               // Get error
+               int errorbit = (buf[1] & 0x80) != 0;
+               if (errorbit)
+               {
+                       hb_log("hb_ts_stream_decode - Error bit set in packet");
+                       stream->ts_skipbad[curstream] = 1;
+                       continue;
+               }
+
+               // Get adaption header info
+               int adaption = (buf[3] & 0x30) >> 4;
+               int adapt_len = 0;
+
+               // Get continuity
+               int continuity = (buf[3] & 0xF);
+               if ((stream->ts_streamcont[curstream] != -1) && (adaption & 0x01 == 0x01))              // Continuity only increments for adaption values of 0x3 or 0x01
+               {
+                       if (continuity != ((stream->ts_streamcont[curstream] + 1) & 0xF))
+                       {
+                               hb_log("hb_ts_stream_decode - Bad continuity code in packet");
+                               stream->ts_skipbad[curstream] = 1;
+                               continue;
+                       }
+                       stream->ts_streamcont[curstream] = continuity;
+               }
+                       
+               // Get adaption header size
+               if (adaption == 0)
+               {
+                       hb_log("hb_ts_stream_decode - Bad adaption code (code was 0)!");
+                       for (i=0; i < stream->ts_number_video_pids + stream->ts_number_audio_pids; i++)
+                       {
+                               stream->ts_skipbad[i] = 1;
+                       }
+               //      stream->ts_skipbad[kAudioStream] = stream->ts_skipbad[kVideoStream] = 1;
+                       continue;
+               }
+               else if (adaption == 0x2)
+                       adapt_len = 184;
+               else if (adaption == 0x3)
+               {
+                       adapt_len = buf[4] + 1;
+                       if (adapt_len > 184)
+                       {
+                               hb_log("hb_ts_stream_decode - Invalid adapt len (was > 183)!");
+                               for (i=0; i < stream->ts_number_video_pids + stream->ts_number_audio_pids; i++)
+                               {
+                                       stream->ts_skipbad[i] = 1;
+                               }
+//                             stream->ts_skipbad[kAudioStream] = stream->ts_skipbad[kVideoStream] = 1;
+                       }
+               }
+
+               // HBO is slick, it doesn't bother to sync AC3 packets with PES elementary stream packets.. so
+               // we have to swizzle them together!  (ARGHH!)
+//             if (pid == stream->ts_audio_pids[0] && start)
+               if ((index_of_audio_pid(pid, stream) >= 0) && start)
+               {
+                       // Is there an AC3 packet start 0b77 code in this packet??
+                       int sync_found = 0;
+                       unsigned char *p = buf + 4 + adapt_len;
+                       while (p <= buf + 186)
+                       {
+                               if (p[0] == 0x0b && p[1] == 0x77)
+                               {
+                                       sync_found = 1;
+                                       break;
+                               }
+                               p++;
+                       }
+
+                       // Couldn't find an AC3 sync start in this packet.. don't make a PES packet!
+                       if (!sync_found)
+                       {
+//                                     int pos = ftell(fin);
+//                                     error("AC3 packet sync not found in start frame");
+//                                     return 1;
+                               adapt_len += 9 + buf[4 + adapt_len + 8];        
+                               start = 0;
+                       }
+               }
+
+               // Get PCR
+               if (start && (adaption & 0x2) && (buf[5] & 0x10))
+               {
+                       int64_t PCR_base = ((int64_t)buf[6] << 25) | ((int64_t)buf[7] << 17) | 
+                                 ((int64_t)buf[8] << 9) | ((int64_t)buf[9] << 1) | ((int64_t)buf[10] >> 7);
+                       int64_t PCR_ext = ((int64_t)(buf[10] & 0x1) << 8) | ((int64_t)buf[11]);
+                       int64_t PCR = PCR_base * 300 + PCR_ext;
+               }
+
+               // Get random
+//             bool random = false;
+//             if (start && (adaption & 0x2))
+//                     random = (buf[5] & 0x40) != 0;          // BUG: SOME TS STREAMS DON'T HAVE THE RANDOM BIT (ABC!! ALIAS)
+
+               // Found a random access point (now we can start a frame/audio packet..)
+               if (start)
+               {
+                       // Check to see if this is an i_frame (group of picture start)
+                       if (pid == stream->ts_video_pids[0])
+                       {
+//                                printf("Found Video Start for pid 0x%x\n", pid);
+                               // Look for the Group of Pictures packet.. indicates this is an I-Frame packet..
+                               doing_iframe = 0;
+                               unsigned int strid = 0;
+                               int i = 4;
+                               for (i = 4 + adapt_len; i < 188; i++)
+                               {
+                                       strid = (strid << 8) | buf[i];
+                                       if (strid == 0x000001B8) // group_start_code
+                                       {
+                                               // found a Group of Pictures header, subsequent picture must be an I-frame
+                                               doing_iframe = 1;
+                                       }
+                                       else if (strid == 0x000001B3) // sequence_header code
+                                       {
+                                               doing_iframe = 1;
+                                       }
+                                       else if (strid == 0x00000100) // picture_start_code
+                                       {
+                                               // picture_header, let's see if it's an I-frame
+                                               if (i<187)
+                                               {
+//                                                     int pic_start_code = (buf[i+2] >> 3) & 0x07;
+//                                                     hb_log("hb_ts_stream_decode - picture_start_code header value = 0x%x (%d)", pic_start_code, pic_start_code);
+                                                       // check if picture_coding_type == 1
+                                                       if ((buf[i+2] & (0x7 << 3)) == (1 << 3))
+                                                       {
+                                                               // found an I-frame picture
+                                                               doing_iframe = 1;
+                                                       }
+                                               }
+                                       }
+
+                                       if (doing_iframe)
+                                       {
+                                               if (!stream->ts_foundfirst[curstream])
+                                               {
+                                                       stream->ts_foundfirst[curstream] = 1;
+//                                                     first_video_PCR = PCR;
+                                               }
+                                               break;
+                                       }
+                               }
+                       }
+                       else if (index_of_audio_pid(pid, stream) >= 0)
+                       {
+                           if (stream->ts_foundfirst[0])  // Set audio found first ONLY after first video frame found. There's an assumption here that stream '0' is a video stream
+                               {
+                                       stream->ts_foundfirst[curstream] |= 1;
+                               }
+                       }
+                       
+                       // If we were skipping a bad packet, start fresh on this new PES packet..
+                       if (stream->ts_skipbad[curstream] == 1)
+                       {
+                               stream->ts_skipbad[curstream] = 0;
+                               stream->ts_packetpos[curstream] = 0;
+                       }
+
+                       // Get the continuity code of this packet
+                       stream->ts_streamcont[curstream] = continuity;
+               }
+
+               // Write a 2048 byte program stream packet..
+               if (start && stream->ts_packetpos[curstream] > 0 && stream->ts_foundfirst[curstream] && !stream->ts_skipbad[curstream])
+               {
+                       // Save the substream id block so we can added it to subsequent blocks
+                       int write_ac3 = 0;
+//                     if (pid == stream->ts_audio_pids[0] /*&& audstreamid == 0xBD*/)
+                       if (index_of_audio_pid(pid, stream) >= 0)
+                       {
+                               if ((stream->ts_audio_stream_type[curstream] == 0x04) || (stream->ts_audio_stream_type[curstream] == 0x81))
+                               {
+                                       write_ac3 = hb_ts_handle_ac3_audio(stream, curstream, buf, adapt_len);
+                               }
+                               else if (stream->ts_audio_stream_type[curstream] == 0x03)
+                               {
+                                       hb_ts_handle_mpeg_audio(stream, curstream, buf, adapt_len);
+                               }
+                               else
+                               {
+                                       hb_log("hb_ts_stream_decode - Unknown Audio Stream type ! 0x%x (%d)", stream->ts_audio_stream_type[curstream], stream->ts_audio_stream_type[curstream]);
+                               }
+                       }
+               
+               if (generate_output_data(stream, write_ac3, curstream, pid) != 0)
+                       return ;
+               }
+
+               // Add the payload for this packet to the current buffer
+               if (stream->ts_foundfirst[curstream] && (184 - adapt_len) > 0)
+               {
+                       memcpy(stream->ts_packetbuf[curstream] + stream->ts_packetpos[curstream], buf + 4 + adapt_len, 184 - adapt_len);
+                       stream->ts_packetpos[curstream] += 184 - adapt_len;
+               }
+       }
+}
+
+/***********************************************************************
+ * hb_ts_stream_reset
+ ***********************************************************************
+ *
+ **********************************************************************/
+static void hb_ts_stream_reset(hb_stream_t *stream)
+{
+       int i=0;
+       for (i=0; i < kNumDecodeBuffers; i++)
+       {
+               stream->ps_decode_buffer[i].read_pos = 0;
+               stream->ps_decode_buffer[i].write_pos = 0;
+               stream->ps_decode_buffer[i].len = 0;
+       }
+
+       for (i=0; i < kMaxNumberDecodeStreams; i++)
+       {
+               stream->ts_streamcont[i] = -1;
+       }
+
+       stream->ps_current_write_buffer_index = 0;
+       stream->ps_current_read_buffer_index = 1;
+
+       align_to_next_packet(stream->file_handle);
+}
+
index afcc631..d45dac3 100644 (file)
                59CBD2650BBB4D1B004A3BE3 /* ChapterTitles.m in Sources */ = {isa = PBXBuildFile; fileRef = 593034EA0BBA39A100172349 /* ChapterTitles.m */; };
                A29E05800BE1283E000533F5 /* Growl.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A29E057F0BE1283E000533F5 /* Growl.framework */; };
                A29E058B0BE12889000533F5 /* Growl.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = A29E057F0BE1283E000533F5 /* Growl.framework */; };
+               EAA526930C3B25D200944FF2 /* stream.c in Sources */ = {isa = PBXBuildFile; fileRef = EAA526920C3B25D200944FF2 /* stream.c */; };
+               EAA526940C3B25D200944FF2 /* stream.c in Sources */ = {isa = PBXBuildFile; fileRef = EAA526920C3B25D200944FF2 /* stream.c */; };
 /* End PBXBuildFile section */
 
 /* Begin PBXContainerItemProxy section */
                593034EA0BBA39A100172349 /* ChapterTitles.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ChapterTitles.m; sourceTree = "<group>"; };
                59CBD2360BBB44DA004A3BE3 /* parsecsv.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = parsecsv.c; path = ../test/parsecsv.c; sourceTree = SOURCE_ROOT; };
                A29E057F0BE1283E000533F5 /* Growl.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Growl.framework; sourceTree = "<group>"; };
+               EAA526920C3B25D200944FF2 /* stream.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = stream.c; path = ../libhb/stream.c; sourceTree = SOURCE_ROOT; };
 /* End PBXFileReference section */
 
 /* Begin PBXFrameworksBuildPhase section */
                526FBC8D0B4CA9F90064E04C /* libhb Sources */ = {
                        isa = PBXGroup;
                        children = (
+                               EAA526920C3B25D200944FF2 /* stream.c */,
                                0DFA5C7E0B8DD3B60020BC09 /* declpcm.c */,
                                0DFA5C7F0B8DD3B60020BC09 /* decmpeg2.c */,
                                0DFA5C800B8DD3B60020BC09 /* decsub.c */,
                                0D096E020B707D3400A845D4 /* deca52.c in Sources */,
                                0D096E030B707D3400A845D4 /* decavcodec.c in Sources */,
                                5558B56A0BE3BADF00E15E27 /* decdca.c in Sources */,
+                               EAA526940C3B25D200944FF2 /* stream.c in Sources */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                                0DFF0B7A0B6BC72A00549488 /* decavcodec.c in Sources */,
                                593034EC0BBA39A100172349 /* ChapterTitles.m in Sources */,
                                5558B5690BE3BADF00E15E27 /* decdca.c in Sources */,
+                               EAA526930C3B25D200944FF2 /* stream.c in Sources */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };