]> granicus.if.org Git - handbrake/commitdiff
- Add mpeg2 "Standard Target Decoder" clock recovery to the low level mpeg stream...
authorvan <vanj.hb@gmail.com>
Sat, 15 Mar 2008 08:02:08 +0000 (08:02 +0000)
committervan <vanj.hb@gmail.com>
Sat, 15 Mar 2008 08:02:08 +0000 (08:02 +0000)
- Since sync now has a fairly reliable clock, make it just trim excess audio or video and fill holes so that we maintain cross media sync.
- Redo the TS-to-PS transmuxing code to work on smaller units so that we can reliably convert the TS clock (PCR) to a PS clock (SCR).

git-svn-id: svn://svn.handbrake.fr/HandBrake/trunk@1341 b64f7644-9d1e-0410-96f1-a4d463321fa5

libhb/demuxmpeg.c
libhb/reader.c
libhb/stream.c
libhb/sync.c

index 93b3709c35869b4a4d32e96f3eb37396041443f5..40264fd2e1930a47fde697b42bfc873054ec64ac 100644 (file)
@@ -11,9 +11,8 @@
 int hb_demux_ps( hb_buffer_t * buf_ps, hb_list_t * list_es )
 {
     hb_buffer_t * buf_es;
-    int           pos;
-
-    pos = 0;
+    int           pos = 0;
+    int64_t       scr;
 
 #define d (buf_ps->data)
 
@@ -26,6 +25,14 @@ int hb_demux_ps( hb_buffer_t * buf_ps, hb_list_t * list_es )
         return 0;
     }
     pos += 4;                    /* pack_start_code */
+    /* extract the system clock reference (scr) */
+    scr = ((uint64_t)(d[pos] & 0x38) << 27) |
+          ((uint64_t)(d[pos] & 0x03) << 28) |
+          ((uint64_t)(d[pos+1]) << 20) |
+          ((uint64_t)(d[pos+2] >> 3) << 15) |
+          ((uint64_t)(d[pos+2] & 3) << 13) |
+          ((uint64_t)(d[pos+3]) << 5) |
+          (d[pos+4] >> 3);
     pos += 9;                    /* pack_header */
     pos += 1 + ( d[pos] & 0x7 ); /* stuffing bytes */
 
@@ -112,6 +119,7 @@ int hb_demux_ps( hb_buffer_t * buf_ps, hb_list_t * list_es )
 
         buf_es->id       = id;
         buf_es->start    = pts;
+        buf_es->stop     = scr;
         if (id == 0xE0) {
             // Consume a chapter break, and apply it to the ES.
             buf_es->new_chap = buf_ps->new_chap;
index a77cf38e9562681ebfc01a07cf189f74c2962111..b2bff03ef3cb45c94a5366c62109ab1b97c241a1 100644 (file)
@@ -17,6 +17,10 @@ typedef struct
     hb_stream_t  * stream;
 
     uint           sequence;
+    int            saw_video;
+    int64_t        scr_offset;
+    int64_t        last_scr;
+    int            scr_changes;
 
 } hb_reader_t;
 
@@ -163,8 +167,73 @@ static void ReaderFunc( void * _r )
         {
             hb_list_rem( list, buf );
             fifos = GetFifoForId( r->job, buf->id );
+
+            if ( ! r->saw_video )
+            {
+                /* The first video packet defines 'time zero' so discard
+                   data until we get a video packet with a PTS */
+                if ( buf->id == 0xE0 && buf->start != -1 )
+                {
+                    r->saw_video = 1;
+                    r->scr_offset = buf->start;
+                    r->last_scr = buf->stop;
+                    hb_log( "reader: first SCR %llu scr_offset %llu",
+                            r->last_scr, r->scr_offset );
+                }
+                else
+                {
+                    fifos = NULL;
+                }
+            }
             if( fifos )
             {
+                /*
+                 * This section of code implements the timing model of
+                 * the "Standard Target Decoder" (STD) of the MPEG2 standard
+                 * (specified in ISO 13818-1 sections 2.4.2, 2.5.2 & Annex D).
+                 * The STD removes and corrects for clock discontinuities so
+                 * that the time stamps on the video, audio & other media
+                 * streams can be used for cross-media synchronization. To do
+                 * this the STD has its own timestamp value, the System Clock
+                 * Reference or SCR, in the PACK header. Clock discontinuities
+                 * are detected using the SCR & and the adjustment needed
+                 * to correct post-discontinuity timestamps to be contiguous
+                 * with pre-discontinuity timestamps is computed from pre- and
+                 * post-discontinuity values of the SCR. Then this adjustment
+                 * is applied to every media timestamp (PTS).
+                 *
+                 * hb_demux_ps left the SCR for this pack in buf->stop.
+                 * ISO 13818-1 says there must be an SCR at least every 700ms
+                 * (100ms for Transport Streams) so if the difference between
+                 * this SCR & the previous is >700ms it's a discontinuity.
+                 * If the difference is negative it's non-physical (time doesn't
+                 * go backward) and must also be a discontinuity. When we find a
+                 * discontinuity we adjust the scr_offset so that the SCR of the
+                 * new packet lines up with that of the previous packet.
+                 */
+                int64_t scr_delta = buf->stop - r->last_scr;
+                if ( scr_delta > 67500 || scr_delta < -900 )
+                {
+                    ++r->scr_changes;
+                    r->scr_offset += scr_delta - 1;
+                }
+                r->last_scr = buf->stop;
+                buf->stop = -1;
+
+                /*
+                 * The last section detected discontinuites and computed the
+                 * appropriate correction to remove them. The next couple of
+                 * lines apply the correction to the media timestamps so the
+                 * code downstream of us sees only timestamps relative to the
+                 * same, continuous clock with time zero on that clock being
+                 * the time of the first video packet.
+                 */
+                if ( buf->start != -1 )
+                {
+                    /* this packet has a PTS - correct it for the initial
+                       video time offset & any timing discontinuities. */
+                    buf->start -= r->scr_offset;
+                }
                 buf->sequence = r->sequence++;
                 for( n = 0; fifos[n] != NULL; n++)
                 {
@@ -212,10 +281,10 @@ static void ReaderFunc( void * _r )
       hb_stream_close(&r->stream);
     }
 
+    hb_log( "reader: done. %d scr changes", r->scr_changes );
+
     free( r );
     _r = NULL;
-
-    hb_log( "reader: done" );
 }
 
 /***********************************************************************
index 5cc1be190fd02c1b7e4109a6c439d73040f5060a..9b6fd432c3806ce6b0ad60f0a970f5b591d490b3 100755 (executable)
@@ -17,12 +17,8 @@ typedef enum { hb_stream_type_unknown = 0, hb_stream_type_transport, hb_stream_t
 #define kMaxNumberVideoPIDS 1
 #define kMaxNumberAudioPIDS 16
 #define kMaxNumberDecodeStreams (kMaxNumberVideoPIDS+kMaxNumberAudioPIDS)
-#define kNumDecodeBuffers 2
 #define kMaxNumberPMTStreams 32
 
-#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
 {
@@ -30,23 +26,13 @@ struct hb_stream_s
        FILE             * file_handle;
        hb_stream_type_t stream_type;
 
-       int                              ps_current_write_buffer_index;
-       int                              ps_current_read_buffer_index;
+    int              frames;            /* video frames so far */
+    int              errors;            /* total errors so far */
+    int              last_error_frame;  /* frame # at last error message */
+    int              last_error_count;  /* # errors at last error message */
 
-       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];
+    int64_t          ts_lastpcr;    /* the last pcr we found in the TS stream */
+    int64_t          ts_nextpcr;    /* the next pcr to put in a PS packet */
 
        int                              ts_video_pids[kMaxNumberVideoPIDS];
        int                              ts_audio_pids[kMaxNumberAudioPIDS];
@@ -54,14 +40,21 @@ struct hb_stream_s
        int                              ts_number_video_pids;
        int                              ts_number_audio_pids;
 
-       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];
+       uint8_t         *ts_buf[kMaxNumberDecodeStreams];
+       int                              ts_pos[kMaxNumberDecodeStreams];
        int                              ts_streamid[kMaxNumberDecodeStreams];
-       int                              ts_audio_stream_type[kMaxNumberAudioPIDS];
+       int                              ts_audio_stream_type[kMaxNumberDecodeStreams];
+       int8_t           ts_foundfirst[kMaxNumberDecodeStreams];
+       int8_t           ts_skipbad[kMaxNumberDecodeStreams];
+       int8_t           ts_streamcont[kMaxNumberDecodeStreams];
+       int8_t           ts_start[kMaxNumberDecodeStreams];
+
+       struct {
+               int lang_code;
+               int flags;
+               int rate;
+               int bitrate;
+       } a52_info[kMaxNumberAudioPIDS];
 
        struct
        {
@@ -98,9 +91,8 @@ struct hb_stream_s
 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 int hb_ts_stream_decode(hb_stream_t *stream, uint8_t *obuf);
 static void hb_ts_stream_reset(hb_stream_t *stream);
-static void hb_stream_put_back(hb_stream_t *stream, int i);
 static hb_audio_t *hb_ts_stream_set_audio_id_and_codec(hb_stream_t *stream,
                                                        int aud_pid_index);
 static void hb_ps_stream_find_audio_ids(hb_stream_t *stream, hb_title_t *title);
@@ -195,21 +187,13 @@ static void hb_stream_delete( hb_stream_t ** _d )
     }
 
        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])
+               if (d->ts_buf[i])
                {
-                       free(d->ts_packetbuf[i]);
-                       d->ts_packetbuf[i] = NULL;
+                       free(d->ts_buf[i]);
+                       d->ts_buf[i] = NULL;
                }
        }
     free( d->path );
@@ -261,6 +245,13 @@ hb_stream_t * hb_stream_open( char * path )
  **********************************************************************/
 void hb_stream_close( hb_stream_t ** _d )
 {
+    hb_stream_t *stream = * _d;
+    if ( stream->frames )
+    {
+        hb_log( "stream: %d good frames, %d errors (%.0f%%)", stream->frames,
+                stream->errors, (double)stream->errors * 100. / 
+                (double)stream->frames );
+    }
 }
 
 /* when the file was first opened we made entries for all the audio elementary
@@ -276,14 +267,27 @@ static void hb_stream_delete_audio_entry(hb_stream_t *stream, int indx)
     for (i = indx+1; i < stream->ts_number_audio_pids; ++i)
     {
         stream->ts_audio_pids[indx] = stream->ts_audio_pids[i];
-        stream->ts_audio_stream_type[indx] = stream->ts_audio_stream_type[i];
-        stream->ts_streamid[stream->ts_number_video_pids + indx] =
-            stream->ts_streamid[stream->ts_number_video_pids + i];
+        stream->ts_audio_stream_type[1 + indx] = stream->ts_audio_stream_type[1+i];
+        stream->ts_streamid[1 + indx] = stream->ts_streamid[1 + i];
         ++indx;
     }
     --stream->ts_number_audio_pids;
 }
 
+static int index_of_pid(int pid, hb_stream_t *stream)
+{
+    int i;
+
+    if ( pid == stream->ts_video_pids[0] )
+        return 0;
+
+    for ( i = 0; i < stream->ts_number_audio_pids; ++i )
+        if ( pid == stream->ts_audio_pids[i] )
+            return i + 1;
+
+    return -1;
+}
+
 /***********************************************************************
  * hb_ps_stream_title_scan
  ***********************************************************************
@@ -340,6 +344,13 @@ hb_title_t * hb_stream_title_scan(hb_stream_t *stream)
                 --i;
             }
         }
+
+        // add the PCR PID if we don't already have it
+        if ( index_of_pid( stream->pmt_info.PCR_PID, stream ) < 0 )
+        {
+            stream->ts_audio_pids[stream->ts_number_audio_pids++] =
+                stream->pmt_info.PCR_PID;
+        }
        }
     else
     {
@@ -537,7 +548,7 @@ static double compute_stream_rate( struct pts_pos *pp, int n )
     for ( i = 0; i < n-1; ++i )
     {
         // Bias the median filter by not including pairs that are "far"
-        // frome one another. This is to handle cases where the file is
+        // from one another. This is to handle cases where the file is
         // made of roughly equal size pieces where a symmetric choice of
         // pairs results in having the same number of intra-piece &
         // inter-piece rate estimates. This would mean that the median
@@ -595,68 +606,13 @@ static void hb_stream_duration(hb_stream_t *stream, hb_title_t *inTitle)
  **********************************************************************/
 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;
-
-               // Give up this buffer - decoding may well need it, and we're done
-               src_stream->ps_decode_buffer[read_buffer_index].read_pos = 0;
-               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;
+    if ( src_stream->stream_type == hb_stream_type_program )
+    {
+        size_t amt_read = fread(b->data, HB_DVD_READ_BUFFER_SIZE, 1,
+                                src_stream->file_handle);
+        return (amt_read > 0);
+    }
+    return hb_ts_stream_decode( src_stream, b->data );
 }
 
 /***********************************************************************
@@ -688,28 +644,6 @@ int hb_stream_seek( hb_stream_t * src_stream, float f )
        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;
 }
 
@@ -733,10 +667,10 @@ static hb_audio_t *hb_ts_stream_set_audio_id_and_codec(hb_stream_t *stream,
             audio->codec = HB_ACODEC_AC3;
             hb_log("transport stream pid 0x%x (type 0x%x) is AC-3 audio id 0x%x",
                    stream->ts_audio_pids[aud_pid_index],
-                   stream->ts_audio_stream_type[aud_pid_index],
+                   stream->ts_audio_stream_type[1 + aud_pid_index],
                    audio->id);
-            stream->ts_audio_stream_type[aud_pid_index] = 0x81;
-            stream->ts_streamid[stream->ts_number_video_pids + aud_pid_index] = buf[3];
+            stream->ts_audio_stream_type[1 + aud_pid_index] = 0x81;
+            stream->ts_streamid[1 + aud_pid_index] = buf[3];
         }
         else if ((buf[3] & 0xe0) == 0xc0)
         {
@@ -744,10 +678,10 @@ static hb_audio_t *hb_ts_stream_set_audio_id_and_codec(hb_stream_t *stream,
             audio->codec = HB_ACODEC_MPGA;
             hb_log("transport stream pid 0x%x (type 0x%x) is MPEG audio id 0x%x",
                    stream->ts_audio_pids[aud_pid_index],
-                   stream->ts_audio_stream_type[aud_pid_index],
+                   stream->ts_audio_stream_type[1 + aud_pid_index],
                    audio->id);
-            stream->ts_audio_stream_type[aud_pid_index] = 0x03;
-            stream->ts_streamid[stream->ts_number_video_pids + aud_pid_index] = buf[3];
+            stream->ts_audio_stream_type[1 + aud_pid_index] = 0x03;
+            stream->ts_streamid[1 + aud_pid_index] = buf[3];
         }
     }
     fseeko(stream->file_handle, cur_pos, SEEK_SET);
@@ -755,7 +689,7 @@ static hb_audio_t *hb_ts_stream_set_audio_id_and_codec(hb_stream_t *stream,
     {
         hb_log("transport stream pid 0x%x (type 0x%x) isn't audio",
                 stream->ts_audio_pids[aud_pid_index],
-                stream->ts_audio_stream_type[aud_pid_index]);
+                stream->ts_audio_stream_type[1 + aud_pid_index]);
        }
     return audio;
 }
@@ -949,81 +883,41 @@ void hb_stream_update_audio(hb_stream_t *stream, hb_audio_t *audio)
                        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 );
+       hb_log( "stream: audio %x: lang %s, rate %d, bitrate %d, "
+            "flags = 0x%x", audio->id, audio->lang, audio->rate,
+            audio->bitrate, audio->ac3flags );
 
 }
 
-/***********************************************************************
- * 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
-                 hb_error("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;
-       }
+       int i;
 
        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;
+       stream->ts_video_pids[0] = -1;
+    for ( i = 0; i < stream->ts_number_audio_pids; i++ )
+    {
+        stream-> ts_audio_pids[i] = -1;
+    }
 
        // 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
-       }
+    stream->ts_streamid[0] = 0xE0;             // stream 0 must be video
 
-       for (i = stream->ts_number_video_pids; i < stream->ts_number_video_pids + stream->ts_number_audio_pids; i++)
+       for (i = 0; i < stream->ts_number_video_pids + stream->ts_number_audio_pids; i++)
        {
-               stream->ts_packetbuf[i] = (unsigned char *) malloc(1024 * 1024);
+        // demuxing buffer for TS to PS conversion
+               stream->ts_buf[i] = malloc( HB_DVD_READ_BUFFER_SIZE );
        }
 }
 
@@ -1233,7 +1127,7 @@ int decode_program_map(hb_stream_t* stream)
         if (i < kMaxNumberAudioPIDS)
             stream->ts_number_audio_pids++;
         stream->ts_audio_pids[i] = elementary_PID;
-        stream->ts_audio_stream_type[i] = stream_type;
+        stream->ts_audio_stream_type[1 + i] = stream_type;
 
                if (ES_info_length > 0)
                {
@@ -1426,391 +1320,6 @@ int decode_PAT(unsigned char *buf, hb_stream_t *stream)
     return 1;
 }
 
-static int flushbuf(hb_stream_t *stream)
-{
-       int old_write_index = stream->ps_current_write_buffer_index;
-
-       // 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 DVD ac3 stream header
-                int ssid = (curstream - stream->ts_number_video_pids) & 0xf;
-                ac3_substream_id[0] = 0x80 | ssid;  // substream id
-                ac3_substream_id[1] = 0x01;         // number of sync words
-                ac3_substream_id[2] = 0x00;         // first offset (16 bits)
-                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 (;;)
-                       {
-                               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;
-                               }
-
-                stream->ts_packetbuf[curstream][pos + 3] =
-                    stream->ts_streamid[curstream];
-
-                               // 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, stream->ts_streamid[curstream], 0, -1, -1);
-                       }
-
-                       stream->ts_packetpos[curstream] = 0;
-                       stream->ts_streamcont[curstream] = -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;
-                               }
-                       }
-
-       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;
-    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];
@@ -1843,10 +1352,16 @@ static void hb_ts_stream_find_pids(hb_stream_t *stream)
                // Check sync byte
                if ((buf[0] != 0x47) && (buf[0] != 0x72) && (buf[0] != 0x29))
                {
-                       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;
+            off_t pos = ftello(stream->file_handle) - 188;
+            off_t pos2 = align_to_next_packet(stream->file_handle);
+            if ( pos2 == 0 )
+            {
+                hb_log( "hb_ts_stream_find_pids: eof while re-establishing sync @ %lld",
+                        pos );
+                break;
+            }
+            hb_log("hb_ts_stream_decode: sync lost @%lld, regained after %lld bytes",
+                    pos, pos2 );
                        continue;
                }
 
@@ -1898,41 +1413,297 @@ static void hb_ts_stream_find_pids(hb_stream_t *stream)
        }
  }
 
-int index_of_video_pid(int pid, hb_stream_t *stream)
+
+static uint8_t *fwrite_buf;
+static uint8_t *fwrite_buf_orig;
+
+static void set_fwrite_buf( uint8_t *obuf )
 {
-       int found_pid = -1, i = 0;
+    fwrite_buf = obuf;
+    fwrite_buf_orig = obuf;
+}
 
-       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;
+static void fwrite64( void* buf, int size )
+{
+    if ( (fwrite_buf - fwrite_buf_orig) + size > 2048 )
+    {
+        hb_log( "steam fwrite64 buffer overflow - writing %d with %d already",
+                size, fwrite_buf - fwrite_buf_orig );
+        return;
+    }
+    memcpy( fwrite_buf, buf, size );
+    fwrite_buf += size;
 }
 
-int index_of_audio_pid(int pid, hb_stream_t *stream)
+static void write_pack(hb_stream_t* stream, uint64_t time, int stuffing)
 {
-       int i = 0, found_pid = -1;
+       uint8_t buf[64];
+       set_buf(buf, 64, 1);                    // clear buffer
+
+       set_bits(0x000001ba, 32);               // pack id                                                              32
+       set_bits(1, 2);                                 // 0x01                                                                 2
+       set_bits(time >> 30, 3);            // system_clock_reference_base                      3
+       set_bits(1, 1);                                 // marker_bit                                                   1
+       set_bits(time >> 15, 15);       // system_clock_reference_base                  15
+       set_bits(1, 1);                                 // marker_bit                                                   1
+       set_bits(time, 15);                     // system_clock_reference_base1                 15
+       set_bits(1, 1);                                 // marker_bit                                                   1
+       set_bits(0, 9);                     // system_clock_reference_extension         9
+       set_bits(1, 1);                                 // marker_bit                                                   1
+       set_bits(384000, 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(stuffing, 3);                  // pack_stuffing_length                                 3
+    while ( --stuffing >= 0 )
+    {
+        set_bits(0xff, 8 );
+    }
+       fwrite64(buf, buf_size());
+}
 
-       for (i = 0; (i < stream->ts_number_audio_pids) && (found_pid < 0); i++)
-       {
-               if (pid == stream->ts_audio_pids[i])
-                       found_pid = i;
+static void pad_buffer(int pad)
+{
+       pad -= 6;
+
+       uint8_t buf[6];
+       buf[0] = 0;
+    buf[1] = 0;
+    buf[2] = 0;
+    buf[3] = 0xbe;
+       buf[4] = pad >> 8;
+    buf[5] = pad;
+
+       fwrite64(buf, 6);
+
+       buf[0] = 0xff;
+    while ( --pad >= 0 )
+    {
+               fwrite64(buf, 1);
        }
-       return found_pid;
 }
 
-int index_of_pid(int pid, hb_stream_t *stream)
+static void make_pes_header(int len, uint8_t streamid)
+{
+       uint8_t buf[9];
+       set_buf(buf, 9, 1);                         // clear the buffer
+
+       set_bits(0x000001, 24);                         // packet_start_code_prefix                             24
+       set_bits(streamid, 8);                  // directory_stream_id                                  8
+       set_bits(len + 3, 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(0, 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(0, 8);                                         // PES_header_data_length                               8
+
+    fwrite64(buf, 9);
+}
+
+static void generate_output_data(hb_stream_t *stream, int curstream)
+{
+    uint8_t *tdat = stream->ts_buf[curstream];
+    int len;
+
+    // we always ship a PACK header plus all the data in our demux buf.
+    // AC3 audio also always needs it substream header.
+    len = 14 + stream->ts_pos[curstream];
+    if ( stream->ts_audio_stream_type[curstream] == 0x81)
+    {
+        len += 4;
+    }
+
+    if ( ! stream->ts_start[curstream] )
+    {
+        // we're in the middle of a chunk of PES data - we need to add
+        // a 'continuation' PES header after the PACK header.
+        len += 9;
+    }
+
+    // Write out pack header
+    // If we don't have 2048 bytes we need to pad to 2048. We can
+    // add a padding frame after our data but we need at least 7
+    // bytes of space to do it (6 bytes of header & 1 of pad). If
+    // we have fewer than 7 bytes left we need to fill the excess
+    // space with stuffing bytes added to the pack header.
+    int stuffing = 0;
+    if ( len > HB_DVD_READ_BUFFER_SIZE )
+    {
+        hb_log( "stream ts length botch %d", len );
+    }
+    if ( HB_DVD_READ_BUFFER_SIZE - len < 8)
+    {
+        stuffing = HB_DVD_READ_BUFFER_SIZE - len;
+    }
+    write_pack(stream, stream->ts_nextpcr, stuffing );
+    stream->ts_nextpcr += 10;
+
+    if ( stream->ts_start[curstream] )
+    {
+        // Start frames already have a PES header but we have modify it
+        // to map from TS PID to PS stream id. Also, if the stream is AC3
+        // audio we have to insert an AC3 stream header between the end of
+        // the PES header and the start of the stream data.
+
+        stream->ts_start[curstream] = 0;
+        tdat[3] = stream->ts_streamid[curstream];
+
+        uint16_t plen = stream->ts_pos[curstream] - 6;
+        if ( stream->ts_audio_stream_type[curstream] == 0x81)
+        {
+            // We have to add an AC3 header in front of the data. Add its
+            // size to the PES packet length.
+            plen += 4;
+            tdat[4] = plen >> 8;
+            tdat[5] = plen;
+
+            // Write out the PES header
+            int hdrsize = 9 + tdat[8];
+            fwrite64(tdat, hdrsize);
+
+            // add a four byte DVD ac3 stream header
+            uint8_t ac3_substream_id[4];
+            int ssid = (curstream - stream->ts_number_video_pids) & 0xf;
+            ac3_substream_id[0] = 0x80 | ssid;  // substream id
+            ac3_substream_id[1] = 0x01;         // number of sync words
+            ac3_substream_id[2] = 0x00;         // first offset (16 bits)
+            ac3_substream_id[3] = 0x02;
+            fwrite64(ac3_substream_id, 4);
+
+            // add the rest of the data
+            fwrite64(tdat + hdrsize, stream->ts_pos[curstream] - hdrsize);
+        }
+        else
+        {
+            // not audio - don't need to modify the stream so write what we've got
+            tdat[4] = plen >> 8;
+            tdat[5] = plen;
+            fwrite64( tdat, stream->ts_pos[curstream] );
+        }
+    }
+    else
+    {
+        // data without a PES start header needs a simple 'continuation'
+        // PES header. AC3 audio also needs its substream header.
+        if ( stream->ts_audio_stream_type[curstream] != 0x81)
+        {
+            make_pes_header(stream->ts_pos[curstream],
+                            stream->ts_streamid[curstream]);
+        }
+        else
+        {
+            make_pes_header(stream->ts_pos[curstream] + 4,
+                            stream->ts_streamid[curstream]);
+
+            // add a four byte DVD ac3 stream header
+            uint8_t ac3_substream_id[4];
+            int ssid = (curstream - stream->ts_number_video_pids) & 0xf;
+            ac3_substream_id[0] = 0x80 | ssid;  // substream id
+            ac3_substream_id[1] = 0x01;         // number of sync words
+            ac3_substream_id[2] = 0x00;         // first offset (16 bits)
+            ac3_substream_id[3] = 0x02;
+            fwrite64(ac3_substream_id, 4);
+        }
+        fwrite64( tdat, stream->ts_pos[curstream] );
+    }
+
+    // Write padding
+    int left = HB_DVD_READ_BUFFER_SIZE - len;
+    if ( left >= 8 )
+    {
+        pad_buffer(left);
+    }
+
+    stream->ts_pos[curstream] = 0;
+}
+
+static void ts_warn_helper( hb_stream_t *stream, char *log, va_list args )
 {
-       int found_pid = -1;
+    // limit error printing to at most one per minute of video (at 30fps)
+    ++stream->errors;
+    if ( stream->frames - stream->last_error_frame >= 30*60 )
+    {
+        char msg[256];
 
-       if ((found_pid = index_of_video_pid(pid, stream)) >= 0)
-               return found_pid;
+        vsnprintf( msg, sizeof(msg), log, args );
 
-       if ((found_pid = index_of_audio_pid(pid, stream)) >= 0)
-               return found_pid;
+        if ( stream->errors - stream->last_error_count < 10 )
+        {
+            hb_log( "stream: error near frame %d: %s", stream->frames, msg );
+        }
+        else
+        {
+            int Edelta = stream->errors - stream->last_error_count;
+            double Epcnt = (double)Edelta * 100. /
+                            (stream->frames - stream->last_error_frame);
+            hb_log( "stream: %d new errors (%.0f%%) up to frame %d: %s",
+                    Edelta, Epcnt, stream->frames, msg );
+        }
+        stream->last_error_frame = stream->frames;
+        stream->last_error_count = stream->errors;
+    }
+}
+
+static void ts_warn( hb_stream_t *stream, char *log, ... )
+{
+    va_list     args;
+    va_start( args, log );
+    ts_warn_helper( stream, log, args );
+    va_end( args );
+}
+
+static void ts_err( hb_stream_t *stream, int curstream, char *log, ... )
+{
+    va_list     args;
+    va_start( args, log );
+    ts_warn_helper( stream, log, args );
+    va_end( args );
+
+    stream->ts_skipbad[curstream] = 1;
+    stream->ts_pos[curstream] = 0;
+    stream->ts_streamcont[curstream] = -1;
+}
+
+static int isIframe( const uint8_t *buf, int adapt_len )
+{
+    // Look for the Group of Pictures packet
+    int i;
+    uint32_t strid = 0;
+
+    for (i = 4 + adapt_len; i < 188; i++)
+    {
+        strid = (strid << 8) | buf[i];
+        switch ( strid )
+        {
+            case 0x000001B8: // group_start_code (GOP header)
+            case 0x000001B3: // sequence_header code
+                return 1;
 
-       return found_pid;
+            case 0x00000100: // picture_start_code
+                // picture_header, let's see if it's an I-frame
+                if (i<185)
+                {
+                    // check if picture_coding_type == 1
+                    if ((buf[i+2] & (0x7 << 3)) == (1 << 3))
+                    {
+                        // found an I-frame picture
+                        return 1;
+                    }
+                }
+                break;
+        }
+    }
+    // didn't find an I frame
+    return 0;
 }
 
 /***********************************************************************
@@ -1940,40 +1711,27 @@ int index_of_pid(int pid, hb_stream_t *stream)
  ***********************************************************************
  *
  **********************************************************************/
-static void hb_ts_stream_decode(hb_stream_t *stream)
+static int hb_ts_stream_decode( hb_stream_t *stream, uint8_t *obuf )
 {
-       unsigned char buf[188];
+    int64_t pcr = stream->ts_lastpcr;
        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[i] = 0;
-       }
+       uint8_t buf[188];
 
-       doing_iframe = 0;
+    set_fwrite_buf( obuf );
 
-       if ((stream->ts_number_video_pids == 0) || (stream->ts_number_audio_pids == 0))
+       // spin until we get a packet of data from some stream or hit eof
+       while ( 1 )
        {
-               hb_log("hb_ts_stream_decode  - no Video or Audio PID selected, cannot decode transport stream");
-               return;
-       }
-
-       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)
-       {
-      if ((fread(buf, 188, 1, stream->file_handle)) != 1)
+        if ((fread(buf, 188, 1, stream->file_handle)) != 1)
                {
             // end of file - we didn't finish filling our ps write buffer
             // so just discard the remainder (the partial buffer is useless)
             hb_log("hb_ts_stream_decode - eof");
-            stream->ps_decode_buffer[stream->ps_current_write_buffer_index].len = 0;
-            return;
+            return 0;
                }
 
+        /* This next section validates the packet */
+
                // Check sync byte
                if ((buf[0] != 0x47) && (buf[0] != 0x72) && (buf[0] != 0x29))
                {
@@ -1985,81 +1743,32 @@ static void hb_ts_stream_decode(hb_stream_t *stream)
             {
                 hb_log( "hb_ts_stream_decode: eof while re-establishing sync @ %lld",
                         pos );
-                stream->ps_decode_buffer[stream->ps_current_write_buffer_index].len = 0;
-                return;
+                return 0;
             }
-            hb_log("hb_ts_stream_decode: sync lost @%lld, regained after %lld bytes",
-                    pos, pos2 );
-                       for (i=0; i < stream->ts_number_video_pids + stream->ts_number_audio_pids; i++)
-                       {
-                               stream->ts_skipbad[i] = 1;
-                       }
+            ts_warn( stream, "hb_ts_stream_decode: sync lost @%lld, "
+                     "regained after %lld bytes", pos, pos2 );
                        continue;
                }
 
-               // Get pid
-               int pid = (((buf[1] & 0x1F) << 8) | buf[2]) & 0x1FFF;
-
-               // Get the pos and buf - we organize our streams as 'n' video streams then 'm' audio streams
-      int index_of_selected_pid;
-               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)
-                       {
-            // not a pid we want
-                               continue;
-                       }
-                       else
-                       {
-                               curstream = stream->ts_number_video_pids + index_of_selected_pid;
-                       }
-               }
-               else
-                       curstream = index_of_selected_pid;
-
-               // Get start code
-               int start;
-               start = (buf[1] & 0x40) != 0;
-
-               if (!start && stream->ts_skipbad[curstream])
-                       continue;
+               // Get pid and use it to find stream state.
+               int pid = ((buf[1] & 0x1F) << 8) | buf[2];
+        if ( ( curstream = index_of_pid( pid, stream ) ) < 0 )
+            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;
+                       ts_err( stream, curstream,  "packet error bit set");
                        continue;
                }
 
                // Get adaption header info
                int adaption = (buf[3] & 0x30) >> 4;
                int adapt_len = 0;
-
-               // Get continuity
-        // Continuity only increments for adaption values of 0x3 or 0x01
-               int continuity = (buf[3] & 0xF);
-        if ((stream->ts_streamcont[curstream] != -1) && ((adaption & 0x01) != 0))
-               {
-                       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;
-                       }
+                       ts_err( stream, curstream,  "adaptation code 0");
                        continue;
                }
                else if (adaption == 0x2)
@@ -2069,161 +1778,136 @@ static void hb_ts_stream_decode(hb_stream_t *stream)
                        adapt_len = buf[4] + 1;
                        if (adapt_len > 184)
                        {
-                               hb_log("hb_ts_stream_decode - Invalid adapt len %d", adapt_len);
-                               for (i=0; i < stream->ts_number_video_pids + stream->ts_number_audio_pids; i++)
-                               {
-                                       stream->ts_skipbad[i] = 1;
-                               }
+                               ts_err( stream, curstream,  "invalid adapt len %d", adapt_len);
+                continue;
                        }
                }
 
-               // 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 (start && curstream >= stream->ts_number_video_pids &&
-            stream->ts_audio_stream_type[curstream - stream->ts_number_video_pids]
-              != 0x03)
+        // if there's an adaptation header & PCR_flag is set
+        // get the PCR (Program Clock Reference)
+        if ( adapt_len > 7 && ( buf[5] & 0x10 ) != 0 )
+        {
+            pcr = ( (uint64_t)buf[6] << (33 - 8) ) |
+                  ( (uint64_t)buf[7] << (33 - 16) ) |
+                  ( (uint64_t)buf[8] << (33 - 24) ) |
+                  ( (uint64_t)buf[9] << (33 - 32) ) |
+                  ( buf[10] >> 7 );
+            stream->ts_nextpcr = pcr;
+
+            // remember the pcr across calls to this routine
+            stream->ts_lastpcr = pcr;
+        }
+               
+               if ( pcr == -1 )
                {
-                       // 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)
-                       {
-                               adapt_len = 184;
-                               start = 0;
-                       }
+            // don't accumulate data until we get a pcr
+                   continue;
                }
 
-               // Found a random access point (now we can start a frame/audio packet..)
-               if (start)
+               // Get continuity
+        // Continuity only increments for adaption values of 0x3 or 0x01
+        // and is not checked for start packets.
+
+               int start = (buf[1] & 0x40) != 0;
+
+        if ( (adaption & 0x01) != 0 )
                {
-                       // Check to see if this is an i_frame (group of picture start)
-                       if (pid == stream->ts_video_pids[0])
-                       {
-                               // 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)
-                                               {
-                                                       // 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)
+            int continuity = (buf[3] & 0xF);
+            if ( continuity == stream->ts_streamcont[curstream] )
+            {
+                // we got a duplicate packet (usually used to introduce
+                // a PCR when one is needed). The only thing that can
+                // change in the dup is the PCR which we grabbed above
+                // so ignore the rest.
+                continue;
+            }
+            if ( !start && (stream->ts_streamcont[curstream] != -1) && 
+                 (continuity != ( (stream->ts_streamcont[curstream] + 1) & 0xf ) ) )
                        {
-                           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;
-                               }
+                               ts_err( stream, curstream,  "continuity error: got %d expected %d",
+                        (int)continuity,
+                        (stream->ts_streamcont[curstream] + 1) & 0xf );
+                stream->ts_streamcont[curstream] = continuity;
+                               continue;
                        }
+                       stream->ts_streamcont[curstream] = continuity;
+               }
+
+        /* If we get here the packet is valid - process its data */
+
+        if ( start )
+        {
+            // Found a random access point (now we can start a frame/audio packet..)
 
                        // If we were skipping a bad packet, start fresh on this new PES packet..
                        if (stream->ts_skipbad[curstream] == 1)
                        {
+                // video skips to an iframe after a bad packet to minimize
+                // screen corruption
+                if ( curstream == 0 && !isIframe( buf, adapt_len ) )
+                {
+                    continue;
+                }
                                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 (curstream >= stream->ts_number_video_pids)
-                       {
-                               // Curstream is a zero based index of streams and includes both video and audio streams, so we must subtract the numver of video streams
-                               // from the indes value used here since ts_audio_stream_type is indexed only by audio streams.
-                if (stream->ts_audio_stream_type[curstream - stream->ts_number_video_pids] == 0x03)
-                               {
-                                       hb_ts_handle_mpeg_audio(stream, curstream, buf, adapt_len);
-                               }
-                               else
-                               {
-                    write_ac3 = hb_ts_handle_ac3_audio(stream, curstream, buf, adapt_len);
-                               }
+                       // If we don't have video yet, check to see if this is an 
+            // i_frame (group of picture start)
+                       if ( curstream == 0 )
+            {
+                if ( !stream->ts_foundfirst[0] )
+                {
+                    if ( !isIframe( buf, adapt_len ) )
+                    {
+                        // didn't find an I frame
+                        continue;
+                    }
+                    stream->ts_foundfirst[0] = 1;
+                }
+                ++stream->frames;
+            }
+                       else if ( ! stream->ts_foundfirst[curstream] )
+            {
+                // start other streams only after first video frame found.
+                if ( ! stream->ts_foundfirst[0] )
+                {
+                    continue;
+                }
+                stream->ts_foundfirst[curstream] = 1;
                        }
 
-               if (generate_output_data(stream, write_ac3, curstream, pid) != 0)
-                       return ;
-               }
+            // If we have some data already on this stream, turn it into
+            // a program stream packet. Then add the payload for this
+            // packet to the current pid's buffer.
+            if ( stream->ts_pos[curstream] )
+            {
+                generate_output_data(stream, curstream);
+                stream->ts_start[curstream] = 1;
+                memcpy(stream->ts_buf[curstream],
+                       buf + 4 + adapt_len, 184 - adapt_len);
+                stream->ts_pos[curstream] = 184 - adapt_len;
+                return 1;
+            }
+            stream->ts_start[curstream] = 1;
+        }
 
                // Add the payload for this packet to the current buffer
                if (!stream->ts_skipbad[curstream] && stream->ts_foundfirst[curstream] &&
             (184 - adapt_len) > 0)
                {
-            if (stream->ts_packetpos[curstream] + 184 - adapt_len > 1024*1024)
+                       memcpy(stream->ts_buf[curstream] + stream->ts_pos[curstream],
+                   buf + 4 + adapt_len, 184 - adapt_len);
+                       stream->ts_pos[curstream] += 184 - adapt_len;
+
+            // if the next TS packet could possibly overflow our 2K output buffer
+            // we need to generate a packet now. Overflow would be 184 bytes of
+            // data + the 9 byte PES hdr + the 14 byte PACK hdr = 211 bytes.
+            if ( stream->ts_pos[curstream] >= (HB_DVD_READ_BUFFER_SIZE - 216) )
             {
-                int aindx = curstream - stream->ts_number_video_pids;
-                if ( aindx >= 0 && stream->ts_audio_stream_type[aindx] == 0x81)
-                {
-                    /* we've searched through a megabyte & didn't find an AC3
-                     * sync frame so this probably isn't AC3. (DVB standard
-                     * teletext uses the same code points as ATSC AC3 so we
-                     * could easily have guessed wrong.) Delete this pid from
-                     * the audio list so we don't waste any more time on it. */
-                    hb_log("hb_ts_stream_decode: removing pid 0x%x - "
-                           "it isn't an AC3 stream.", stream->ts_audio_pids[aindx]);
-                    hb_stream_delete_audio_entry( stream, aindx );
-                }
-                else
-                {
-                    hb_log("hb_ts_stream_decode: pid 0x%x ts_packetbuf overflow "
-                           "pos %d len = %d",
-                           aindx < 0 ? stream->ts_video_pids[curstream] :
-                                       stream->ts_audio_pids[aindx],
-                           stream->ts_packetpos[curstream], 184 - adapt_len );
-                }
-                stream->ts_packetpos[curstream] = 0;
-                continue;
+                // we have enough data to make a PS packet
+                generate_output_data(stream, curstream);
+                return 1;
             }
-                       memcpy(stream->ts_packetbuf[curstream] + stream->ts_packetpos[curstream], buf + 4 + adapt_len, 184 - adapt_len);
-                       stream->ts_packetpos[curstream] += 184 - adapt_len;
                }
        }
 }
@@ -2235,21 +1919,24 @@ static void hb_ts_stream_decode(hb_stream_t *stream)
  **********************************************************************/
 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;
-       }
+       int i;
 
        for (i=0; i < kMaxNumberDecodeStreams; i++)
        {
+               stream->ts_pos[i] = 0;
+               stream->ts_foundfirst[i] = 0;
+               stream->ts_skipbad[i] = 0;
                stream->ts_streamcont[i] = -1;
+               stream->ts_start[i] = 0;
        }
 
-       stream->ps_current_write_buffer_index = 0;
-       stream->ps_current_read_buffer_index = 1;
+    stream->ts_lastpcr = -1;
+    stream->ts_nextpcr = -1;
+
+    stream->frames = 0;
+    stream->errors = 0;
+    stream->last_error_frame = -10000;
+    stream->last_error_count = 0;
 
        align_to_next_packet(stream->file_handle);
 }
index 04bdf162f36dac69cb0365a451548a2ada917d3b..460d175ffbdc039d8a5b659b22406c3cd3faef23 100644 (file)
 typedef struct
 {
     hb_audio_t * audio;
-    int64_t      count_frames;
+
+    int64_t      next_start;    /* start time of next output frame */
+    int64_t      next_pts;      /* start time of next input frame */
+    int64_t      start_silence; /* if we're inserting silence, the time we started */
+    int64_t      first_drop;    /* PTS of first 'went backwards' frame dropped */
+    int          drop_count;    /* count of 'time went backwards' drops */
+    int          inserting_silence;
 
     /* Raw */
     SRC_STATE  * state;
@@ -39,28 +45,22 @@ struct hb_work_private_s
     /* Video */
     hb_subtitle_t * subtitle;
     int64_t pts_offset;
-    int64_t pts_offset_old;
-    int64_t next_start;
-    int64_t count_frames;
-    int64_t count_frames_max;
-    int64_t video_sequence;
+    int64_t next_start;         /* start time of next output frame */
+    int64_t next_pts;           /* start time of next input frame */
+    int64_t first_drop;         /* PTS of first 'went backwards' frame dropped */
+    int drop_count;             /* count of 'time went backwards' drops */
+    int video_sequence;
+    int count_frames;
+    int count_frames_max;
     hb_buffer_t * cur; /* The next picture to process */
 
     /* Audio */
     hb_sync_audio_t sync_audio[8];
 
-    /* Flags */
-    int discontinuity;
-
     /* Statistics */
     uint64_t st_counts[4];
     uint64_t st_dates[4];
     uint64_t st_first;
-
-    /* Throttle message flags */
-    int   trashing_audio;
-    int   inserting_silence;
-    int   way_out_of_sync;
 };
 
 /***********************************************************************
@@ -69,8 +69,8 @@ struct hb_work_private_s
 static void InitAudio( hb_work_object_t * w, int i );
 static int  SyncVideo( hb_work_object_t * w );
 static void SyncAudio( hb_work_object_t * w, int i );
-static int  NeedSilence( hb_work_object_t * w, hb_audio_t * );
-static void InsertSilence( hb_work_object_t * w, int i );
+static int  NeedSilence( hb_work_object_t * w, hb_audio_t *, int i );
+static void InsertSilence( hb_work_object_t * w, int i, int64_t d );
 static void UpdateState( hb_work_object_t * w );
 
 /***********************************************************************
@@ -91,15 +91,8 @@ int syncInit( hb_work_object_t * w, hb_job_t * job )
 
     pv->job            = job;
     pv->pts_offset     = INT64_MIN;
-    pv->pts_offset_old = INT64_MIN;
     pv->count_frames   = 0;
 
-    pv->discontinuity = 0;
-
-    pv->trashing_audio = 0;
-    pv->inserting_silence = 0;
-    pv->way_out_of_sync = 0;
-
     /* Calculate how many video frames we are expecting */
     duration = 0;
     for( i = job->chapter_start; i <= job->chapter_end; i++ )
@@ -111,7 +104,7 @@ int syncInit( hb_work_object_t * w, hb_job_t * job )
         /* 1 second safety so we're sure we won't miss anything */
     pv->count_frames_max = duration * job->vrate / job->vrate_base / 90000;
 
-    hb_log( "sync: expecting %lld video frames", pv->count_frames_max );
+    hb_log( "sync: expecting %d video frames", pv->count_frames_max );
 
     /* Initialize libsamplerate for every audio track we have */
     for( i = 0; i < hb_list_count( title->list_audio ); i++ )
@@ -144,6 +137,13 @@ void syncClose( hb_work_object_t * w )
 
     for( i = 0; i < hb_list_count( title->list_audio ); i++ )
     {
+        if ( pv->sync_audio[i].start_silence )
+        {
+            hb_log( "sync: added %d ms of silence to audio %d",
+                    (int)((pv->sync_audio[i].next_pts -
+                              pv->sync_audio[i].start_silence) / 90), i );
+        }
+
         if( job->acodec & HB_ACODEC_AC3 ||
             job->audio_mixdowns[i] == HB_AMIXDOWN_AC3 )
         {
@@ -254,10 +254,6 @@ static void InitAudio( hb_work_object_t * w, int i )
     }
 }
 
-
-
-#define PTS_DISCONTINUITY_TOLERANCE 90000
-
 /***********************************************************************
  * SyncVideo
  ***********************************************************************
@@ -268,8 +264,6 @@ static int SyncVideo( hb_work_object_t * w )
     hb_work_private_t * pv = w->private_data;
     hb_buffer_t * cur, * next, * sub = NULL;
     hb_job_t * job = pv->job;
-    int64_t pts_expected;
-    int chap_break;
 
     if( pv->done )
     {
@@ -282,7 +276,7 @@ static int SyncVideo( hb_work_object_t * w )
     {
         /* All video data has been processed already, we won't get
            more */
-        hb_log( "sync: got %lld frames, %lld expected",
+        hb_log( "sync: got %d frames, %d expected",
                 pv->count_frames, pv->count_frames_max );
         pv->done = 1;
 
@@ -306,7 +300,7 @@ static int SyncVideo( hb_work_object_t * w )
     /* At this point we have a frame to process. Let's check
         1) if we will be able to push into the fifo ahead
         2) if the next frame is there already, since we need it to
-           know whether we'll have to repeat the current frame or not */
+           compute the duration of the current frame*/
     while( !hb_fifo_is_full( job->fifo_sync ) &&
            ( next = hb_fifo_see( job->fifo_raw ) ) )
     {
@@ -315,73 +309,59 @@ static int SyncVideo( hb_work_object_t * w )
         if( pv->pts_offset == INT64_MIN )
         {
             /* This is our first frame */
-            hb_log( "sync: first pts is %lld", cur->start );
-            pv->pts_offset = cur->start;
+            pv->pts_offset = 0;
+            if ( cur->start != 0 )
+            {
+                /*
+                 * The first pts from a dvd should always be zero but
+                 * can be non-zero with a transport or program stream since
+                 * we're not guaranteed to start on an IDR frame. If we get
+                 * a non-zero initial PTS extend its duration so it behaves
+                 * as if it started at zero so that our audio timing will
+                 * be in sync.
+                 */
+                hb_log( "sync: first pts is %lld", cur->start );
+                cur->start = 0;
+            }
         }
 
         /*
-         * Track the video sequence number localy so that we can sync the audio
-         * to it using the sequence number as well as the PTS.
+         * since the first frame is always 0 and the upstream reader code
+         * is taking care of adjusting for pts discontinuities, we just have
+         * to deal with the next frame's start being in the past. This can
+         * happen when the PTS is adjusted after data loss but video frame
+         * reordering causes some frames with the old clock to appear after
+         * the clock change. This creates frames that overlap in time which
+         * looks to us like time going backward. The downstream muxing code
+         * can deal with overlaps of up to a frame time but anything larger
+         * we handle by dropping frames here.
          */
-        pv->video_sequence = cur->sequence;
-
-        /* Check for PTS jumps over 0.5 second */
-        if( next->start < cur->start - PTS_DISCONTINUITY_TOLERANCE ||
-            next->start > cur->start + PTS_DISCONTINUITY_TOLERANCE )
+        if ( pv->next_pts - next->start > 1000 )
         {
-           hb_log( "Sync: Video PTS discontinuity %s (current buffer start=%lld, next buffer start=%lld)",
-                    pv->discontinuity ? "second" : "first", cur->start, next->start );
-
-            /*
-             * Do we need to trash the subtitle, is it from the next->start period
-             * or is it from our old position. If the latter then trash it.
-             */
-            if( pv->subtitle )
+            if ( pv->first_drop == 0 )
             {
-                while( ( sub = hb_fifo_see( pv->subtitle->fifo_raw ) ) )
-                {
-                    if( ( sub->start > ( cur->start - PTS_DISCONTINUITY_TOLERANCE ) ) &&
-                        ( sub->start < ( cur->start + PTS_DISCONTINUITY_TOLERANCE ) ) )
-                    {
-                        /*
-                         * The subtitle is from our current time region which we are
-                         * jumping from. So trash it as we are about to jump backwards
-                         * or forwards and don't want it blocking the subtitle fifo.
-                         */
-                        hb_log("Trashing subtitle 0x%x due to PTS discontinuity", sub);
-                        sub = hb_fifo_get( pv->subtitle->fifo_raw );
-                        hb_buffer_close( &sub );
-                    } else {
-                        break;
-                    }
-                }
-            }
-
-            /* Trash current picture */
-            /* Also, make sure we don't trash a chapter break */
-            chap_break = cur->new_chap;
-            hb_buffer_close( &cur );
-            pv->cur = cur = hb_fifo_get( job->fifo_raw );
-            cur->new_chap |= chap_break; // Don't stomp existing chapter breaks
-
-            /* Calculate new offset */
-            pv->pts_offset_old = pv->pts_offset;
-            if ( job->vfr )
-            {
-                pv->pts_offset = cur->start - pv->next_start;
-            } else {
-                pv->pts_offset = cur->start -
-                    pv->count_frames * pv->job->vrate_base / 300;
-            }
-
-            if( !pv->discontinuity )
-            {
-                pv->discontinuity = 1;
+                pv->first_drop = next->start;
             }
-
-            pv->video_sequence = cur->sequence;
+            ++pv->drop_count;
+            buf_tmp = hb_fifo_get( job->fifo_raw );
+            hb_buffer_close( &buf_tmp );
             continue;
         }
+        if ( pv->first_drop )
+        {
+            hb_log( "sync: video time went backwards %d ms, dropped %d frames "
+                    "(frame %lld, expected %lld)",
+                    (int)( pv->next_pts - pv->first_drop ) / 90, pv->drop_count,
+                    pv->first_drop, pv->next_pts );
+            pv->first_drop = 0;
+            pv->drop_count = 0;
+        }
+
+        /*
+         * Track the video sequence number localy so that we can sync the audio
+         * to it using the sequence number as well as the PTS.
+         */
+        pv->video_sequence = cur->sequence;
 
         /* Look for a subtitle for this frame */
         if( pv->subtitle )
@@ -418,23 +398,6 @@ static int SyncVideo( hb_work_object_t * w )
                      */
                     break;
                 }
-                else
-                {
-                    /*
-                     * The stop time is in the past. But is it due to
-                     * it having been played already, or has the PTS
-                     * been reset to 0?
-                     */
-                    if( ( cur->start - sub->stop ) > PTS_DISCONTINUITY_TOLERANCE ) {
-                        /*
-                         * There is a lot of time between our current
-                         * video and where this subtitle is ending,
-                         * assume that we are about to reset the PTS
-                         * and do not throw away this subtitle.
-                         */
-                        break;
-                    }
-                }
 
                 /*
                  * The subtitle is older than this picture, trash it
@@ -533,72 +496,26 @@ static int SyncVideo( hb_work_object_t * w )
             }
         }
 
-        if ( job->vfr )
-        {
-            /*
-             * adjust the pts of the current frame so that it's contiguous
-             * with the previous frame. pts_offset tracks the time difference
-             * between the pts values in the input content (which start at some
-             * random time) and our timestamps (which start at zero). We don't
-             * make any adjustments to the source timestamps other than removing
-             * the clock offsets (which also removes pts discontinuities).
-             * This means we automatically encode at the source's frame rate.
-             * MP2 uses an implicit duration (frames end when the next frame
-             * starts) but more advanced containers like MP4 use an explicit
-             * duration. Since we're looking ahead one frame we set the
-             * explicit stop time from the start time of the next frame.
-             */
-            buf_tmp = cur;
-            pv->cur = cur = hb_fifo_get( job->fifo_raw );
-            buf_tmp->start = pv->next_start;
-            pv->next_start = next->start - pv->pts_offset;
-            buf_tmp->stop = pv->next_start;
-        }
-        else
-        {
-            /* The PTS of the frame we are expecting now */
-            pts_expected = pv->pts_offset +
-                pv->count_frames * pv->job->vrate_base / 300;
-
-            //hb_log("Video expecting PTS %lld, current frame: %lld, next frame: %lld, cf: %lld",
-            //       pts_expected, cur->start, next->start, pv->count_frames * pv->job->vrate_base / 300 );
-
-            if( cur->start < pts_expected - pv->job->vrate_base / 300 / 2 &&
-                next->start < pts_expected + pv->job->vrate_base / 300 / 2 )
-            {
-                /* The current frame is too old but the next one matches,
-                   let's trash */
-                /* Also, make sure we don't trash a chapter break */
-                chap_break = cur->new_chap;
-                hb_buffer_close( &cur );
-                pv->cur = cur = hb_fifo_get( job->fifo_raw );
-                cur->new_chap |= chap_break; // Make sure we don't stomp the existing one.
-
-                continue;
-            }
-
-            if( next->start > pts_expected + 3 * pv->job->vrate_base / 300 / 2 )
-            {
-                /* We'll need the current frame more than one time. Make a
-                   copy of it and keep it */
-                buf_tmp = hb_buffer_init( cur->size );
-                memcpy( buf_tmp->data, cur->data, cur->size );
-                buf_tmp->sequence = cur->sequence;
-            }
-            else
-            {
-                /* The frame has the expected date and won't have to be
-                   duplicated, just put it through */
-                buf_tmp = cur;
-                pv->cur = cur = hb_fifo_get( job->fifo_raw );
-            }
-
-            /* Replace those MPEG-2 dates with our dates */
-            buf_tmp->start = (uint64_t) pv->count_frames *
-                pv->job->vrate_base / 300;
-            buf_tmp->stop  = (uint64_t) ( pv->count_frames + 1 ) *
-                pv->job->vrate_base / 300;
-        }
+        /*
+         * Adjust the pts of the current frame so that it's contiguous
+         * with the previous frame. The start time of the current frame
+         * has to be the end time of the previous frame and the stop
+         * time has to be the start of the next frame.  We don't
+         * make any adjustments to the source timestamps other than removing
+         * the clock offsets (which also removes pts discontinuities).
+         * This means we automatically encode at the source's frame rate.
+         * MP2 uses an implicit duration (frames end when the next frame
+         * starts) but more advanced containers like MP4 use an explicit
+         * duration. Since we're looking ahead one frame we set the
+         * explicit stop time from the start time of the next frame.
+         */
+        buf_tmp = cur;
+        pv->cur = cur = hb_fifo_get( job->fifo_raw );
+        pv->next_pts = next->start;
+        int64_t duration = next->start - buf_tmp->start;
+        buf_tmp->start = pv->next_start;
+        pv->next_start += duration;
+        buf_tmp->stop = pv->next_start;
 
         /* If we have a subtitle for this picture, copy it */
         /* FIXME: we should avoid this memcpy */
@@ -621,7 +538,7 @@ static int SyncVideo( hb_work_object_t * w )
         /* Make sure we won't get more frames then expected */
         if( pv->count_frames >= pv->count_frames_max * 2)
         {
-            hb_log( "sync: got too many frames (%lld), exiting early", pv->count_frames );
+            hb_log( "sync: got too many frames (%d), exiting early", pv->count_frames );
             pv->done = 1;
 
            // Drop an empty buffer into our output to ensure that things
@@ -636,6 +553,68 @@ static int SyncVideo( hb_work_object_t * w )
     return HB_WORK_OK;
 }
 
+static void OutputAudioFrame( hb_job_t *job, hb_audio_t *audio, hb_buffer_t *buf,
+                              hb_sync_audio_t *sync, hb_fifo_t *fifo, int i )
+{
+    int64_t start = sync->next_start;
+    int64_t duration = buf->stop - buf->start;
+    if (duration <= 0 || 
+        duration > ( 90000 * AC3_SAMPLES_PER_FRAME ) / audio->rate )
+    {
+        hb_log("sync: audio %d weird duration %lld, start %lld, stop %lld, next %lld",
+               i, duration, buf->start, buf->stop, sync->next_pts);
+        if ( duration <= 0 )
+        {
+            duration = ( 90000 * AC3_SAMPLES_PER_FRAME ) / audio->rate;
+            buf->stop = buf->start + duration;
+        }
+    }
+    sync->next_pts += duration;
+
+    if( /* audio->rate == job->arate || This should work but doesn't */
+        job->acodec & HB_ACODEC_AC3 ||
+        job->audio_mixdowns[i] == HB_AMIXDOWN_AC3 )
+    {
+        /*
+         * If we don't have to do sample rate conversion or this audio is AC3
+         * pass-thru just send the input buffer downstream after adjusting
+         * its timestamps to make the output stream continuous.
+         */
+    }
+    else
+    {
+        /* Not pass-thru - do sample rate conversion */
+        int count_in, count_out;
+        hb_buffer_t * buf_raw = buf;
+        int channel_count = HB_AMIXDOWN_GET_DISCRETE_CHANNEL_COUNT(audio->amixdown) *
+                            sizeof( float );
+
+        count_in  = buf_raw->size / channel_count;
+        count_out = ( buf_raw->stop - buf_raw->start ) * job->arate / 90000;
+
+        sync->data.input_frames = count_in;
+        sync->data.output_frames = count_out;
+        sync->data.src_ratio = (double)count_out / (double)count_in;
+
+        buf = hb_buffer_init( count_out * channel_count );
+        sync->data.data_in  = (float *) buf_raw->data;
+        sync->data.data_out = (float *) buf->data;
+        if( src_process( sync->state, &sync->data ) )
+        {
+            /* XXX If this happens, we're screwed */
+            hb_log( "sync: audio %d src_process failed", i );
+        }
+        hb_buffer_close( &buf_raw );
+
+        buf->size = sync->data.output_frames_gen * channel_count;
+    }
+    buf->start = start;
+    buf->stop  = start + duration;
+    buf->frametype = HB_FRAME_AUDIO;
+    sync->next_start = start + duration;
+    hb_fifo_push( fifo, buf );
+}
+
 /***********************************************************************
  * SyncAudio
  ***********************************************************************
@@ -644,21 +623,13 @@ static int SyncVideo( hb_work_object_t * w )
 static void SyncAudio( hb_work_object_t * w, int i )
 {
     hb_work_private_t * pv = w->private_data;
-    hb_job_t        * job;
-    hb_audio_t      * audio;
+    hb_job_t        * job = pv->job;
+    hb_sync_audio_t * sync = &pv->sync_audio[i];
+    hb_audio_t      * audio = sync->audio;
     hb_buffer_t     * buf;
-    hb_sync_audio_t * sync;
-
     hb_fifo_t       * fifo;
     int               rate;
 
-    int64_t           pts_expected;
-    int64_t           start;
-
-    job    = pv->job;
-    sync   = &pv->sync_audio[i];
-    audio  = sync->audio;
-
     if( job->acodec & HB_ACODEC_AC3 ||
         job->audio_mixdowns[i] == HB_AMIXDOWN_AC3 )
     {
@@ -671,230 +642,90 @@ static void SyncAudio( hb_work_object_t * w, int i )
         rate = job->arate;
     }
 
-    while( !hb_fifo_is_full( fifo ) &&
-           ( buf = hb_fifo_see( audio->fifo_raw ) ) )
+    while( !hb_fifo_is_full( fifo ) && ( buf = hb_fifo_see( audio->fifo_raw ) ) )
     {
-        /* The PTS of the samples we are expecting now */
-        pts_expected = pv->pts_offset + sync->count_frames * 90000 / rate;
-
-        // hb_log("Video Sequence: %lld, Audio Sequence: %lld", pv->video_sequence, buf->sequence);
-
-        /*
-         * Using the same logic as the Video have we crossed a VOB
-         * boundary as detected by the expected PTS and the PTS of our
-         * audio being out by more than the tolerance value.
-         */
-        if( buf->start > pts_expected + PTS_DISCONTINUITY_TOLERANCE ||
-            buf->start < pts_expected - PTS_DISCONTINUITY_TOLERANCE )
+        if ( sync->next_pts - buf->start > 500 )
         {
-            /* There has been a PTS discontinuity, and this frame might
-               be from before the discontinuity*/
-
-            if( pv->discontinuity )
-            {
-                /*
-                 * There is an outstanding discontinuity, so use the offset from
-                 * that discontinuity.
-                 */
-                pts_expected = pv->pts_offset_old + sync->count_frames *
-                    90000 / rate;
-            }
-            else
-            {
-                /*
-                 * No outstanding discontinuity, so the audio must be leading the
-                 * video (or the PTS values are really stuffed). So lets mark this
-                 * as a discontinuity ourselves for the audio to use until
-                 * the video also crosses the discontinuity.
-                 *
-                 * pts_offset is used when we are in the same time space as the video
-                 * pts_offset_old when in a discontinuity.
-                 *
-                 * Therefore set the pts_offset_old given the new pts_offset for this
-                 * current buffer.
-                 */
-                pv->discontinuity = 1;
-                pv->pts_offset_old = buf->start - sync->count_frames *
-                    90000 / rate;
-                pts_expected = pv->pts_offset_old + sync->count_frames *
-                    90000 / rate;
-
-                hb_log("Sync: Audio discontinuity (sequence: vid %lld aud %lld) (pts %lld < %lld < %lld)",
-                       pv->video_sequence, buf->sequence,
-                       pts_expected - PTS_DISCONTINUITY_TOLERANCE, buf->start,
-                       pts_expected + PTS_DISCONTINUITY_TOLERANCE );
-            }
-
             /*
-             * Is the audio from a valid period given the previous
-             * Video PTS. I.e. has there just been a video PTS
-             * discontinuity and this audio belongs to the vdeo from
-             * before?
+             * audio time went backwards by more than a frame time (this can
+             * happen when we reset the PTS because of lost data).
+             * Discard data that's in the past.
              */
-            if( buf->start > pts_expected + PTS_DISCONTINUITY_TOLERANCE ||
-                buf->start < pts_expected - PTS_DISCONTINUITY_TOLERANCE )
+            if ( sync->first_drop == 0 )
             {
-                /*
-                 * It's outside of our tolerance for where the video
-                 * is now, and it's outside of the tolerance for
-                 * where we have been in the case of a VOB change.
-                 * Try and reconverge regardless. so continue on to
-                 * our convergence code below which will kick in as
-                 * it will be more than 100ms out.
-                 *
-                 * Note that trashing the Audio could make things
-                 * worse if the Audio is in front because we will end
-                 * up diverging even more. We need to hold on to the
-                 * audio until the video catches up.
-                 */
-                if( !pv->way_out_of_sync )
-                {
-                    hb_log("Sync: Audio is way out of sync, attempt to reconverge from current video PTS");
-                    pv->way_out_of_sync = 1;
-                }
-
-                /*
-                 * It wasn't from the old place, so we must be from
-                 * the new, but just too far out. So attempt to
-                 * reconverge by resetting the point we want to be to
-                 * where we are currently wanting to be.
-                 */
-               pts_expected = pv->pts_offset + sync->count_frames * 90000 / rate;
-                start = pts_expected - pv->pts_offset;
-           } else {
-                 /* Use the older offset */
-                start = pts_expected - pv->pts_offset_old;
-           }
-        }
-        else
-        {
-            start = pts_expected - pv->pts_offset;
-
-            if( pv->discontinuity )
-            {
-                /*
-                 * The Audio is tracking the Video again using the normal pts_offset, so the
-                 * discontinuity is over.
-                 */
-                hb_log( "Sync: Audio joined Video after discontinuity at PTS %lld", buf->start );
-                pv->discontinuity = 0;
-            }
-        }
-
-        /* Tolerance: 100 ms */
-        if( buf->start < pts_expected - 9000 )
-        {
-            if( !pv->trashing_audio )
-            {
-                /* Audio is behind the Video, trash it, can't use it now. */
-                hb_log( "Sync: Audio PTS (%lld) < Video PTS (%lld) by greater than 100ms, trashing audio to reconverge",
-                        buf->start, pts_expected);
-                pv->trashing_audio = 1;
+                sync->first_drop = buf->start;
             }
+            ++sync->drop_count;
             buf = hb_fifo_get( audio->fifo_raw );
             hb_buffer_close( &buf );
             continue;
         }
-        else if( buf->start > pts_expected + 9000 )
+        if ( sync->first_drop )
         {
-            /* Audio is ahead of the Video, insert silence until we catch up*/
-            if( !pv->inserting_silence )
-            {
-                hb_log("Sync: Audio PTS (%lld) >  Video PTS (%lld) by greater than 100ms insert silence until reconverged", buf->start, pts_expected);
-                pv->inserting_silence = 1;
-            }
-            InsertSilence( w, i );
-            continue;
+            hb_log( "sync: audio %d time went backwards %d ms, dropped %d frames "
+                    "(frame %lld, expected %lld)", i,
+                    (int)( sync->next_pts - sync->first_drop ) / 90,
+                    sync->drop_count, sync->first_drop, sync->next_pts );
+            sync->first_drop = 0;
+            sync->drop_count = 0;
         }
-        else
+
+        if ( sync->inserting_silence && buf->start - sync->next_pts > 0 )
         {
-            if( pv->trashing_audio || pv->inserting_silence )
+            /*
+             * if we're within one frame time of the amount of silence
+             * we need, insert just what we need otherwise insert a frame time.
+             */
+            int64_t framedur = buf->stop - buf->start;
+            if ( buf->start - sync->next_pts <= framedur )
             {
-                hb_log( "Sync: Audio back in Sync at PTS %lld", buf->start );
-                pv->trashing_audio = 0;
-                pv->inserting_silence = 0;
+                InsertSilence( w, i, buf->start - sync->next_pts );
+                sync->inserting_silence = 0;
             }
-            if( pv->way_out_of_sync )
+            else
             {
-                hb_log( "Sync: Audio no longer way out of sync at PTS %lld",
-                        buf->start );
-                pv->way_out_of_sync = 0;
+                InsertSilence( w, i, framedur );
             }
+            continue;
         }
-
-        if( job->acodec & HB_ACODEC_AC3 ||
-            job->audio_mixdowns[i] == HB_AMIXDOWN_AC3 )
-        {
-            buf        = hb_fifo_get( audio->fifo_raw );
-            buf->start = start;
-            buf->stop  = start + 90000 * AC3_SAMPLES_PER_FRAME / rate;
-
-            sync->count_frames += AC3_SAMPLES_PER_FRAME;
-        }
-        else
+        if ( buf->start - sync->next_pts >= (90 * 100) )
         {
-            hb_buffer_t * buf_raw = hb_fifo_get( audio->fifo_raw );
-
-            int count_in, count_out;
-
-            count_in  = buf_raw->size / HB_AMIXDOWN_GET_DISCRETE_CHANNEL_COUNT(audio->amixdown) / sizeof( float );
-            count_out = ( buf_raw->stop - buf_raw->start ) * job->arate / 90000;
-            if( buf->start < pts_expected - 1500 )
-                count_out--;
-            else if( buf->start > pts_expected + 1500 )
-                count_out++;
-
-            sync->data.data_in      = (float *) buf_raw->data;
-            sync->data.input_frames = count_in;
-            sync->data.output_frames = count_out;
-
-            sync->data.src_ratio = (double) sync->data.output_frames /
-                                   (double) sync->data.input_frames;
-
-            buf = hb_buffer_init( sync->data.output_frames * HB_AMIXDOWN_GET_DISCRETE_CHANNEL_COUNT(audio->amixdown) *
-                                  sizeof( float ) );
-            sync->data.data_out = (float *) buf->data;
-            if( src_process( sync->state, &sync->data ) )
+            /*
+             * there's a gap of at least 100ms between the last
+             * frame we processed & the next. Fill it with silence.
+             */
+            if ( ! sync->inserting_silence )
             {
-                /* XXX If this happens, we're screwed */
-                hb_log( "sync: src_process failed" );
+                hb_log( "sync: adding %d ms of silence to audio %d"
+                        "  start %lld, next %lld",
+                        (int)((buf->start - sync->next_pts) / 90),
+                        i, buf->start, sync->next_pts );
+                sync->inserting_silence = 1;
             }
-            hb_buffer_close( &buf_raw );
-
-            buf->size = sync->data.output_frames_gen * HB_AMIXDOWN_GET_DISCRETE_CHANNEL_COUNT(audio->amixdown) * sizeof( float );
-
-            /* Set dates for resampled data */
-            buf->start = start;
-            buf->stop  = start + sync->data.output_frames_gen *
-                            90000 / job->arate;
-
-            sync->count_frames += sync->data.output_frames_gen;
+            InsertSilence( w, i, buf->stop - buf->start );
+            continue;
         }
 
-        buf->frametype = HB_FRAME_AUDIO;
-        hb_fifo_push( fifo, buf );
-    }
-
-    if( hb_fifo_is_full( fifo ) &&
-        pv->way_out_of_sync )
-    {
         /*
-         * Trash the top audio packet to avoid dead lock as we reconverge.
+         * When we get here we've taken care of all the dups and gaps in the
+         * audio stream and are ready to inject the next input frame into
+         * the output stream.
          */
-        if ( (buf = hb_fifo_get( audio->fifo_raw ) ) != NULL)
-            hb_buffer_close( &buf );
+        buf = hb_fifo_get( audio->fifo_raw );
+        OutputAudioFrame( job, audio, buf, sync, fifo, i );
     }
 
-    if( NeedSilence( w, audio ) )
+    if( NeedSilence( w, audio, i ) )
     {
-        InsertSilence( w, i );
+        InsertSilence( w, i, (90000 * AC3_SAMPLES_PER_FRAME) / sync->audio->rate );
     }
 }
 
-static int NeedSilence( hb_work_object_t * w, hb_audio_t * audio )
+static int NeedSilence( hb_work_object_t * w, hb_audio_t * audio, int i )
 {
     hb_work_private_t * pv = w->private_data;
     hb_job_t * job = pv->job;
+    hb_sync_audio_t * sync = &pv->sync_audio[i];
 
     if( hb_fifo_size( audio->fifo_in ) ||
         hb_fifo_size( audio->fifo_raw ) ||
@@ -911,7 +742,11 @@ static int NeedSilence( hb_work_object_t * w, hb_audio_t * audio )
     {
         /* We might miss some audio to complete encoding and muxing
            the video track */
-       hb_log("Reader has exited early, inserting silence.");
+        if ( sync->start_silence == 0 )
+        {
+            hb_log("sync: reader has exited, adding silence to audio %d", i);
+            sync->start_silence = sync->next_pts;
+        }
         return 1;
     }
 
@@ -921,51 +756,47 @@ static int NeedSilence( hb_work_object_t * w, hb_audio_t * audio )
         hb_fifo_is_full( job->fifo_render ) &&
         hb_fifo_is_full( job->fifo_mpeg4 ) )
     {
-        /* Too much video and no audio, oh-oh */
-       hb_log("Still got some video - and nothing in the audio fifo, insert silence");
+        if ( sync->start_silence == 0 )
+        {
+            /* Too much video and no audio, oh-oh */
+            hb_log("sync: have video but no audio, adding silence to audio %d", i);
+            sync->start_silence = sync->next_pts;
+        }
         return 1;
     }
 
+    if ( sync->start_silence )
+    {
+        hb_log( "sync: added %d ms of silence to audio %d",
+                (int)((sync->next_pts - sync->start_silence) / 90), i );
+        sync->start_silence = 0;
+    }
     return 0;
 }
 
-static void InsertSilence( hb_work_object_t * w, int i )
+static void InsertSilence( hb_work_object_t * w, int i, int64_t duration )
 {
     hb_work_private_t * pv = w->private_data;
-    hb_job_t        * job;
-    hb_sync_audio_t * sync;
-    hb_buffer_t     * buf;
+    hb_job_t        *job = pv->job;
+    hb_sync_audio_t *sync = &pv->sync_audio[i];
+    hb_buffer_t     *buf;
 
-    job    = pv->job;
-    sync   = &pv->sync_audio[i];
-
-    if( job->acodec & HB_ACODEC_AC3 ||
-        job->audio_mixdowns[i] == HB_AMIXDOWN_AC3 )
+    if( job->acodec & HB_ACODEC_AC3 || job->audio_mixdowns[i] == HB_AMIXDOWN_AC3 )
     {
         buf        = hb_buffer_init( sync->ac3_size );
-        buf->start = sync->count_frames * 90000 / sync->audio->rate;
-        buf->stop  = buf->start + 90000 * AC3_SAMPLES_PER_FRAME /
-                     sync->audio->rate;
+        buf->start = sync->next_pts;
+        buf->stop  = buf->start + duration;
         memcpy( buf->data, sync->ac3_buf, buf->size );
-
-        hb_log( "sync: adding a silent AC-3 frame for track %x",
-                sync->audio->id );
-        hb_fifo_push( sync->audio->fifo_out, buf );
-
-        sync->count_frames += AC3_SAMPLES_PER_FRAME;
-
+        OutputAudioFrame( job, sync->audio, buf, sync, sync->audio->fifo_out, i );
     }
     else
     {
-        buf        = hb_buffer_init( HB_AMIXDOWN_GET_DISCRETE_CHANNEL_COUNT(sync->audio->amixdown) * job->arate / 20 *
-                                     sizeof( float ) );
-        buf->start = sync->count_frames * 90000 / job->arate;
-        buf->stop  = buf->start + 90000 / 20;
+        buf = hb_buffer_init( duration * sizeof( float ) *
+                    HB_AMIXDOWN_GET_DISCRETE_CHANNEL_COUNT(sync->audio->amixdown) );
+        buf->start = sync->next_pts;
+        buf->stop  = buf->start + duration;
         memset( buf->data, 0, buf->size );
-
-        hb_fifo_push( sync->audio->fifo_sync, buf );
-
-        sync->count_frames += job->arate / 20;
+        OutputAudioFrame( job, sync->audio, buf, sync, sync->audio->fifo_sync, i );
     }
 }