X-Git-Url: http://git.osdn.jp/view?a=blobdiff_plain;f=libhb%2Fdemuxmpeg.c;h=afeda30c732ddd1b6ed0654fa7bfd9bc2a82c8f8;hb=fe0c6626d091b954d153d4b3993609a59768d7d1;hp=40264fd2e1930a47fde697b42bfc873054ec64ac;hpb=4e710ccc1cbdab66cf92f137cffa7cd450401f36;p=handbrake-jp%2Fhandbrake-jp-git.git diff --git a/libhb/demuxmpeg.c b/libhb/demuxmpeg.c index 40264fd2..afeda30c 100644 --- a/libhb/demuxmpeg.c +++ b/libhb/demuxmpeg.c @@ -1,18 +1,55 @@ /* $Id: demuxmpeg.c,v 1.4 2004/10/19 23:11:36 titer Exp $ This file is part of the HandBrake source code. - Homepage: . + Homepage: . It may be used under the terms of the GNU General Public License. */ #include "hb.h" -/* Basic MPEG demuxer, only works with DVDs (2048 bytes packets) */ +static inline void check_mpeg_scr( hb_psdemux_t *state, int64_t scr, int tol ) +{ + /* + * 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). + * + * 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. + */ + + // we declare a discontinuity if there's a gap of more than + // 'tol'ms between the last scr & this or if this scr goes back + // by more than half a frame time. + int64_t scr_delta = scr - state->last_scr; + if ( scr_delta > 90*tol || scr_delta < -90*10 ) + { + ++state->scr_changes; + state->last_pts = -1; + } + state->last_scr = scr; +} -int hb_demux_ps( hb_buffer_t * buf_ps, hb_list_t * list_es ) +/* Basic MPEG demuxer */ + +int hb_demux_ps( hb_buffer_t * buf_ps, hb_list_t * list_es, hb_psdemux_t* state ) { hb_buffer_t * buf_es; int pos = 0; - int64_t scr; #define d (buf_ps->data) @@ -25,14 +62,20 @@ 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); + + if ( state ) + { + /* extract the system clock reference (scr) */ + int64_t 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); + check_mpeg_scr( state, scr, 300 ); + } + pos += 9; /* pack_header */ pos += 1 + ( d[pos] & 0x7 ); /* stuffing bytes */ @@ -57,7 +100,7 @@ int hb_demux_ps( hb_buffer_t * buf_ps, hb_list_t * list_es ) int pes_header_d_length; int pes_header_end; int has_pts; - int64_t pts = -1; + int64_t pts = -1, dts = -1; pos += 3; /* packet_start_code_prefix */ id = d[pos]; @@ -75,7 +118,7 @@ int hb_demux_ps( hb_buffer_t * buf_ps, hb_list_t * list_es ) continue; } - has_pts = ( ( d[pos+1] >> 6 ) & 0x2 ) ? 1 : 0; + has_pts = d[pos+1] >> 6; pos += 2; /* Required headers */ pes_header_d_length = d[pos]; @@ -84,11 +127,23 @@ int hb_demux_ps( hb_buffer_t * buf_ps, hb_list_t * list_es ) if( has_pts ) { - pts = ( ( ( (uint64_t) d[pos] >> 1 ) & 0x7 ) << 30 ) + + pts = ( (uint64_t)(d[pos] & 0xe ) << 29 ) + ( d[pos+1] << 22 ) + ( ( d[pos+2] >> 1 ) << 15 ) + ( d[pos+3] << 7 ) + ( d[pos+4] >> 1 ); + if ( has_pts & 1 ) + { + dts = ( (uint64_t)(d[pos+5] & 0xe ) << 29 ) + + ( d[pos+6] << 22 ) + + ( ( d[pos+7] >> 1 ) << 15 ) + + ( d[pos+8] << 7 ) + + ( d[pos+9] >> 1 ); + } + else + { + dts = pts; + } } pos = pes_header_end; @@ -119,7 +174,8 @@ 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; + buf_es->renderOffset = dts; + buf_es->stop = -1; if (id == 0xE0) { // Consume a chapter break, and apply it to the ES. buf_es->new_chap = buf_ps->new_chap; @@ -136,3 +192,83 @@ int hb_demux_ps( hb_buffer_t * buf_ps, hb_list_t * list_es ) return 1; } + +// 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 };