+
+// mpeg transport stream demuxer. the elementary stream headers have been
+// stripped off and buf_ps has all the info gleaned from them: id is set,
+// start contains the pts (if any), renderOffset contains the dts (if any)
+// and stop contains the pcr (if it changed).
+int hb_demux_ts( hb_buffer_t *buf_ps, hb_list_t *list_es, hb_psdemux_t *state )
+{
+ if ( state )
+ {
+ // we're keeping track of timing (i.e., not in scan)
+ // check if there's a new pcr in this packet
+ if ( buf_ps->stop >= 0 )
+ {
+ // we have a new pcr
+ check_mpeg_scr( state, buf_ps->stop, 300 );
+ buf_ps->stop = -1;
+ }
+ if ( buf_ps->start >= 0 )
+ {
+ // Program streams have an SCR in every PACK header so they
+ // can't lose their clock reference. But the PCR in Transport
+ // streams is typically on <.1% of the packets. If a PCR
+ // packet gets lost and it marks a clock discontinuity then
+ // the data following it will be referenced to the wrong
+ // clock & introduce huge gaps or throw our A/V sync off.
+ // We try to protect against that here by sanity checking
+ // timestamps against the current reference clock and discarding
+ // packets where the DTS is "too far" from its clock.
+ int64_t fdelta = buf_ps->start - state->last_scr;
+ if ( fdelta < -300 * 90000LL || fdelta > 300 * 90000LL )
+ {
+ // packet too far behind or ahead of its clock reference
+ ++state->dts_drops;
+ return 1;
+ }
+ if ( state->last_pts >= 0 )
+ {
+ fdelta = buf_ps->start - state->last_pts;
+ if ( fdelta < -5 * 90000LL || fdelta > 5 * 90000LL )
+ {
+ // Packet too far from last. This may be a NZ TV broadcast
+ // as they like to change the PCR without sending a PCR
+ // update. Since it may be a while until they actually tell
+ // us the new PCR use the PTS as the PCR.
+ ++state->scr_changes;
+ state->last_scr = buf_ps->start;
+ }
+ }
+ state->last_pts = buf_ps->start;
+ }
+ }
+
+ hb_buffer_t *buf = hb_buffer_init( buf_ps->alloc );
+ hb_buffer_swap_copy( buf_ps, buf );
+ hb_list_add( list_es, buf );
+
+ return 1;
+}
+
+// "null" demuxer (makes a copy of input buf & returns it in list)
+// used when the reader for some format includes its own demuxer.
+// for example, ffmpeg.
+int hb_demux_null( hb_buffer_t * buf_ps, hb_list_t * list_es, hb_psdemux_t* state )
+{
+ // if we don't have a time offset yet, use this timestamp as the offset.
+ if ( state && state->scr_changes == 0 &&
+ ( buf_ps->start >= 0 || buf_ps->renderOffset >= 0 ) )
+ {
+ ++state->scr_changes;
+ state->last_scr = buf_ps->start >= 0 ? buf_ps->start : buf_ps->renderOffset;
+ }
+
+ hb_buffer_t *buf = hb_buffer_init( buf_ps->alloc );
+ hb_buffer_swap_copy( buf_ps, buf );
+ hb_list_add( list_es, buf );
+
+ return 1;
+}
+
+const hb_muxer_t hb_demux[] = { hb_demux_ps, hb_demux_ts, hb_demux_null };