Index: libhb/reader.c===================================================================--- libhb/reader.c (revision 1336)+++ libhb/reader.c (working copy)@@ -17,6 +17,10 @@ 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 @@ { 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 @@ hb_stream_close(&r->stream); }+ hb_log( "reader: done. %d scr changes", r->scr_changes );+ free( r ); _r = NULL;-- hb_log( "reader: done" ); } /***********************************************************************Index: libhb/deca52.c===================================================================--- libhb/deca52.c (revision 1336)+++ libhb/deca52.c (working copy)@@ -213,6 +213,10 @@ /* Get the whole frame */ hb_list_getbytes( pv->list, pv->frame, pv->size, &pts, &pos );+ if (pts == -1)+ {+ pts = pv->next_expected_pts;+ } /* AC3 passthrough: don't decode the AC3 frame */ if( pv->job->acodec & HB_ACODEC_AC3 ||@@ -222,6 +226,7 @@ memcpy( buf->data, pv->frame, pv->size ); buf->start = pts + ( pos / pv->size ) * 6 * 256 * 90000 / pv->rate; buf->stop = buf->start + 6 * 256 * 90000 / pv->rate;+ pv->next_expected_pts = buf->stop; pv->sync = 0; return buf; }@@ -236,10 +241,6 @@ /* 6 blocks per frame, 256 samples per block, channelsused channels */ buf = hb_buffer_init( 6 * 256 * pv->out_discrete_channels * sizeof( float ) );- if (pts == -1)- {- pts = pv->next_expected_pts;- } buf->start = pts + ( pos / pv->size ) * 6 * 256 * 90000 / pv->rate; buf->stop = buf->start + 6 * 256 * 90000 / pv->rate;Index: libhb/stream.c===================================================================--- libhb/stream.c (revision 1336)+++ libhb/stream.c (working copy)@@ -17,12 +17,8 @@ #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,39 +26,36 @@ 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];+ 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 */- struct {- int lang_code;- int flags;- int rate;- int bitrate;- } a52_info[kMaxNumberAudioPIDS];- int ts_video_pids[kMaxNumberVideoPIDS]; int ts_audio_pids[kMaxNumberAudioPIDS]; int ts_number_video_pids; int ts_number_audio_pids;- 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 { unsigned short program_number;@@ -98,9 +91,8 @@ 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 @@ } 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 @@ **********************************************************************/ 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 @@ 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 @@ --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 @@ 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 @@ **********************************************************************/ 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 @@ 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 @@ 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 @@ 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 @@ { 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 @@ 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->ts_video_pids[0] = -1;+ for ( i = 0; i < stream->ts_number_audio_pids; i++ )+ {+ stream-> ts_audio_pids[i] = -1;+ }- stream->ps_current_write_buffer_index = 0;- stream->ps_current_read_buffer_index = 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 @@ 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 @@ 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 @@ // 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,82 +1413,325 @@ } }-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- for (i = 0; (i < stream->ts_number_audio_pids) && (found_pid < 0); i++)- {- if (pid == stream->ts_audio_pids[i])- found_pid = i;+ 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());+}++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) {- int found_pid = -1;+ uint8_t buf[9];+ set_buf(buf, 9, 1); // clear the buffer- if ((found_pid = index_of_video_pid(pid, stream)) >= 0)- return found_pid;+ 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- if ((found_pid = index_of_audio_pid(pid, stream)) >= 0)- return found_pid;+ fwrite64(buf, 9);+}- return found_pid;+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 )+{+ // 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];++ vsnprintf( msg, sizeof(msg), log, args );++ 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;++ 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;+}+ /*********************************************************************** * hb_ts_stream_decode *********************************************************************** * **********************************************************************/-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;+ uint8_t buf[188];- int i = 0;- for (i=0; i < stream->ts_number_video_pids + stream->ts_number_audio_pids; i++)- {- stream->ts_skipbad[i] = 0;- }+ set_fwrite_buf( obuf );- doing_iframe = 0;-- 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 @@ { 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 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 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 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 @@ 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++;- }+ // don't accumulate data until we get a pcr+ continue;+ }- // Couldn't find an AC3 sync start in this packet.. don't make a PES packet!- if (!sync_found)+ // 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 )+ {+ 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 ) ) ) {- adapt_len = 184;- start = 0;+ 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; }- // Found a random access point (now we can start a frame/audio packet..)- if (start)- {- // Check to see if this is an i_frame (group of picture start)- if (pid == stream->ts_video_pids[0])- {- // 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 we get here the packet is valid - process its data */- if (doing_iframe)- {- if (!stream->ts_foundfirst[curstream])- {- stream->ts_foundfirst[curstream] = 1;-// first_video_PCR = PCR;- }- break;- }- }- }- else if (index_of_audio_pid(pid, stream) >= 0)- {- if (stream->ts_foundfirst[0]) // Set audio found first ONLY after first video frame found. There's an assumption here that stream '0' is a video stream- {- stream->ts_foundfirst[curstream] |= 1;- }- }+ if ( 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,22 +1919,25 @@ **********************************************************************/ 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: libhb/demuxmpeg.c===================================================================--- libhb/demuxmpeg.c (revision 1336)+++ libhb/demuxmpeg.c (working copy)@@ -11,10 +11,9 @@ int hb_demux_ps( hb_buffer_t * buf_ps, hb_list_t * list_es ) { hb_buffer_t * buf_es;- int pos;+ int pos = 0;+ int64_t scr;- pos = 0;- #define d (buf_ps->data) /* pack_header */@@ -26,6 +25,14 @@ 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 @@ 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: libhb/sync.c===================================================================--- libhb/sync.c (revision 1336)+++ libhb/sync.c (working copy)@@ -19,8 +19,14 @@ 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; SRC_DATA data;@@ -39,11 +45,13 @@ /* 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 */@@ -56,11 +64,6 @@ 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 +72,8 @@ 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 +94,10 @@ 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 +109,7 @@ /* 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 +142,13 @@ 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 ) {@@ -268,8 +273,6 @@ 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 +285,7 @@ { /* 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 +309,7 @@ /* 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,74 +318,60 @@ 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;- }- }+ pv->first_drop = next->start; }-- /* 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->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 +407,6 @@ */ 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,73 +505,27 @@ } }- 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;+ /*+ * 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;- //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;- }- /* If we have a subtitle for this picture, copy it */ /* FIXME: we should avoid this memcpy */ if( sub )@@ -621,7 +547,7 @@ /* 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 +562,68 @@ 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 +632,13 @@ 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 +651,90 @@ 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;+ sync->first_drop = buf->start; }- }-- /* 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->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 )+ if ( buf->start - sync->next_pts >= (90 * 100) ) {- 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- {- 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 +751,11 @@ { /* 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 +765,47 @@ 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 ); } }