typedef struct
{
- int64_t last; // last timestamp seen on this stream
double average; // average time between packets
+ int64_t last; // last timestamp seen on this stream
int id; // stream id
} stream_timing_t;
r->stream_timing[0].id = r->title->video_id;
r->stream_timing[0].average = 90000. * (double)job->vrate_base /
(double)job->vrate;
+ r->stream_timing[0].last = -r->stream_timing[0].average;
r->stream_timing[1].id = -1;
return hb_thread_init( "reader", ReaderFunc, r,
hb_fifo_push( fifo, buf );
}
-// The MPEG STD (Standard Timing Decoder) essentially requires that we keep
+// The MPEG STD (Standard Target Decoder) essentially requires that we keep
// per-stream timing so that when there's a timing discontinuity we can
// seemlessly join packets on either side of the discontinuity. This join
// requires that we know the timestamp of the previous packet and the
// average inter-packet time (since we position the new packet at the end
-// of the previous packet). The next three routines keep track of this
+// of the previous packet). The next four routines keep track of this
// per-stream timing.
-// find the per-stream timing state associated with 'buf'
+// find the per-stream timing state for 'buf'
+
+static stream_timing_t *find_st( hb_reader_t *r, const hb_buffer_t *buf )
+{
+ stream_timing_t *st = r->stream_timing;
+ for ( ; st->id != -1; ++st )
+ {
+ if ( st->id == buf->id )
+ return st;
+ }
+ return NULL;
+}
+
+// find or create the per-stream timing state for 'buf'
static stream_timing_t *id_to_st( hb_reader_t *r, const hb_buffer_t *buf )
{
st = r->stream_timing + slot;
}
st->id = buf->id;
- st->last = buf->renderOffset;
st->average = 30.*90.;
+ st->last = buf->renderOffset - st->average;
st[1].id = -1;
}
{
stream_timing_t *st = id_to_st( r, buf );
double dt = buf->renderOffset - st->last;
- st->average += ( dt - st->average ) * (1./16.);
+ st->average += ( dt - st->average ) * (1./32.);
st->last = buf->renderOffset;
}
// such that 'buf' will follow the previous packet of this stream separated
// by the average packet time of the stream.
-static void new_scr_offset( hb_reader_t *r, const hb_buffer_t *buf )
+static void new_scr_offset( hb_reader_t *r, hb_buffer_t *buf )
{
stream_timing_t *st = id_to_st( r, buf );
- int64_t nxt = st->last + st->average - r->scr_offset;
+ int64_t nxt = st->last + st->average;
r->scr_offset = buf->renderOffset - nxt;
+ buf->renderOffset = nxt;
r->scr_changes = r->demux.scr_changes;
st->last = buf->renderOffset;
}
list = hb_list_init();
hb_buffer_t *ps = hb_buffer_init( HB_DVD_READ_BUFFER_SIZE );
+ r->demux.flaky_clock = r->title->flaky_clock;
while( !*r->die && !r->job->done )
{
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 == r->title->video_id && buf->start != -1 )
+ data until we get a video packet with a PTS & DTS */
+ if ( buf->id == r->title->video_id && buf->start != -1 &&
+ buf->renderOffset != -1 )
{
+ // force a new scr offset computation
+ r->scr_changes = r->demux.scr_changes - 1;
r->saw_video = 1;
- r->scr_changes = r->demux.scr_changes;
- new_scr_offset( r, buf );
- hb_log( "reader: first SCR %llu scr_offset %llu",
- r->demux.last_scr, r->scr_offset );
+ hb_log( "reader: first SCR %lld", r->demux.last_scr );
}
else
{
}
if( fifos )
{
- if ( buf->start != -1 )
+ if ( buf->renderOffset != -1 )
{
if ( r->scr_changes == r->demux.scr_changes )
{
// This packet is referenced to the same SCR as the last.
- // Update the average inter-packet time for this stream.
+ // Adjust timestamp to remove the System Clock Reference
+ // offset then update the average inter-packet time
+ // for this stream.
+ buf->renderOffset -= r->scr_offset;
update_ipt( r, buf );
}
else
// change. Compute a new scr offset that would make this
// packet follow the last of this stream with the correct
// average spacing.
- new_scr_offset( r, buf );
+ stream_timing_t *st = find_st( r, buf );
+
+ if ( st )
+ {
+ // if this isn't the video stream or we don't
+ // have audio yet then generate a new scr
+ if ( st != r->stream_timing ||
+ r->stream_timing[1].id == -1 )
+ {
+ new_scr_offset( r, buf );
+ }
+ else
+ {
+ // defer the scr change until we get some
+ // audio since audio has a timestamp per
+ // frame but video doesn't. Clear the timestamps
+ // so the decoder will regenerate them from
+ // the frame durations.
+ buf->start = -1;
+ buf->renderOffset = -1;
+ }
+ }
+ else
+ {
+ // we got a new scr at the same time as the first
+ // packet of a stream we've never seen before. We
+ // have no idea what the timing should be so toss
+ // this buffer & wait for a stream we've already seen.
+ hb_buffer_close( &buf );
+ continue;
+ }
}
- // adjust timestamps to remove System Clock Reference offsets.
- buf->start -= r->scr_offset;
- buf->renderOffset -= r->scr_offset;
}
+ if ( buf->start != -1 )
+ buf->start -= r->scr_offset;
buf->sequence = r->sequence++;
/* if there are mutiple output fifos, send a copy of the
}
}
- /* send empty buffers upstream to video & audio decoders to signal we're done */
+ // send empty buffers downstream to video & audio decoders to signal we're done.
push_buf( r, r->job->fifo_mpeg2, hb_buffer_init(0) );
hb_audio_t *audio;
- for( n = 0;
- ( audio = hb_list_item( r->job->title->list_audio, n ) ) != NULL;
- ++n )
+ for( n = 0; ( audio = hb_list_item( r->job->title->list_audio, n ) ); ++n )
{
- push_buf( r, audio->priv.fifo_in, hb_buffer_init(0) );
+ if ( audio->priv.fifo_in )
+ push_buf( r, audio->priv.fifo_in, hb_buffer_init(0) );
}
hb_list_empty( &list );
hb_buffer_close( &ps );
if (r->dvd)
{
- hb_dvd_stop( r->dvd );
- hb_dvd_close( &r->dvd );
+ hb_dvd_stop( r->dvd );
+ hb_dvd_close( &r->dvd );
}
else if (r->stream)
{
- hb_stream_close(&r->stream);
+ hb_stream_close(&r->stream);
}
if ( r->stream_timing )
}
hb_log( "reader: done. %d scr changes", r->demux.scr_changes );
+ if ( r->demux.dts_drops )
+ {
+ hb_log( "reader: %d drops because DTS out of range", r->demux.dts_drops );
+ }
free( r );
_r = NULL;