summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjstebbins <[email protected]>2015-04-22 21:12:21 +0000
committerjstebbins <[email protected]>2015-04-22 21:12:21 +0000
commitddcecd907b532f867a5c7a1aa89ea6909205caab (patch)
tree3c2deafb87344c300243f11609366998c3d246be
parentc48e55d5563dc05aed1652a34bc5359376aed2a7 (diff)
stream: Improve transport stream PCR handling
When a new PCR is seen, emit all currently cached data. This prevents data that was referenced to the previous PCR from being associated with the new PCR. git-svn-id: svn://svn.handbrake.fr/HandBrake/trunk@7120 b64f7644-9d1e-0410-96f1-a4d463321fa5
-rw-r--r--libhb/bd.c12
-rw-r--r--libhb/demuxmpeg.c106
-rw-r--r--libhb/internal.h4
-rw-r--r--libhb/stream.c502
4 files changed, 338 insertions, 286 deletions
diff --git a/libhb/bd.c b/libhb/bd.c
index abe13fa5d..86a956675 100644
--- a/libhb/bd.c
+++ b/libhb/bd.c
@@ -698,7 +698,7 @@ hb_buffer_t * hb_bd_read( hb_bd_t * d )
uint8_t buf[192];
BD_EVENT event;
uint64_t pos;
- hb_buffer_t * b;
+ hb_buffer_t * out = NULL;
uint8_t discontinuity;
int new_chap = 0;
@@ -753,13 +753,9 @@ hb_buffer_t * hb_bd_read( hb_bd_t * d )
}
}
// buf+4 to skip the BD timestamp at start of packet
- b = hb_ts_decode_pkt( d->stream, buf+4 );
- if ( b )
- {
- b->s.discontinuity = discontinuity;
- b->s.new_chap = new_chap;
- return b;
- }
+ out = hb_ts_decode_pkt( d->stream, buf+4, new_chap, discontinuity );
+ if (out != NULL)
+ return out;
}
return NULL;
}
diff --git a/libhb/demuxmpeg.c b/libhb/demuxmpeg.c
index 988b1ca5c..32e0d6387 100644
--- a/libhb/demuxmpeg.c
+++ b/libhb/demuxmpeg.c
@@ -9,7 +9,7 @@
#include "hb.h"
-static inline void check_mpeg_scr( hb_psdemux_t *state, int64_t scr, int tol )
+static inline int check_mpeg_scr( hb_psdemux_t *state, int64_t scr, int tol )
{
/*
* This section of code implements the timing model of
@@ -38,14 +38,17 @@ static inline void check_mpeg_scr( hb_psdemux_t *state, int64_t scr, int tol )
// 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.
+ int discontinuity = 0;
int64_t scr_delta = scr - state->last_scr;
if (state->last_scr == AV_NOPTS_VALUE ||
scr_delta > 90*tol || scr_delta < -90*10)
{
++state->scr_changes;
state->last_pts = AV_NOPTS_VALUE;
+ discontinuity = 1;
}
state->last_scr = scr;
+ return discontinuity;
}
static inline void save_chap( hb_psdemux_t *state, hb_buffer_t *buf )
@@ -246,64 +249,78 @@ void hb_demux_dvd_ps( hb_buffer_t * buf, hb_list_t * list_es, hb_psdemux_t* stat
// start contains the pts (if any), renderOffset contains the dts (if any)
// and stop contains the pcr (if it changed).
void hb_demux_mpeg(hb_buffer_t *buf, hb_list_t *list_es,
- hb_psdemux_t *state, int pcr_tolerance)
+ hb_psdemux_t *state, int tolerance)
{
while ( buf )
{
save_chap( state, buf );
if ( state )
{
- if ( buf->s.discontinuity )
- {
- // Buffer has been flagged as a discontinuity. This happens
- // when a blueray changes clips.
- ++state->scr_changes;
- state->last_scr = buf->s.start;
- state->scr_delta = 0;
- }
-
+ int discontinuity = 0;
// we're keeping track of timing (i.e., not in scan)
// check if there's a new pcr in this packet
if ( buf->s.pcr >= 0 )
{
// we have a new pcr
- check_mpeg_scr( state, buf->s.pcr, pcr_tolerance );
+ discontinuity = check_mpeg_scr( state, buf->s.pcr, tolerance );
buf->s.pcr = AV_NOPTS_VALUE;
// Some streams have consistantly bad PCRs or SCRs
// So filter out the offset
if ( buf->s.start >= 0 )
state->scr_delta = buf->s.start - state->last_scr;
+ else
+ state->scr_delta = 0;
}
+ if ( !discontinuity && buf->s.discontinuity )
+ {
+ // Buffer has been flagged as a discontinuity. This happens
+ // when a blueray changes clips.
+ ++state->scr_changes;
+ state->last_scr = buf->s.start;
+ state->scr_delta = 0;
+ }
+
if ( buf->s.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->s.start - state->last_scr - state->scr_delta;
- if ( fdelta < -300 * 90000LL || fdelta > 300 * 90000LL )
+ int64_t fdelta;
+ if (state->last_scr != AV_NOPTS_VALUE)
{
- // packet too far behind or ahead of its clock reference
- ++state->dts_drops;
- hb_buffer_t *tmp = buf->next;
- buf->next = NULL;
- hb_buffer_close( &buf );
- buf = tmp;
- continue;
- }
- else
- {
- // Some streams have no PCRs. In these cases, we
- // will only get an "PCR" update if a large change
- // in DTS or PTS is detected. So we need to update
- // our scr_delta with each valid timestamp so that
- // fdelta does not continually grow.
- state->scr_delta = buf->s.start - state->last_scr;
+ // 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.
+ fdelta = buf->s.start - state->last_scr - state->scr_delta;
+ if ( fdelta < -300 * 90000LL || fdelta > 300 * 90000LL )
+ {
+ // packet too far behind or ahead of its clock reference
+ ++state->dts_drops;
+ ++state->dts_drop_run;
+ hb_buffer_t *tmp = buf->next;
+ buf->next = NULL;
+ hb_buffer_close( &buf );
+ buf = tmp;
+ continue;
+ }
+ else
+ {
+ // Some streams have no PCRs. In these cases, we
+ // will only get an "PCR" update if a large change
+ // in DTS or PTS is detected. So we need to update
+ // our scr_delta with each valid timestamp so that
+ // fdelta does not continually grow.
+ state->scr_delta = buf->s.start - state->last_scr;
+ if (state->dts_drop_run > 0)
+ {
+ hb_error("Packet clock reference error. Dropped %d frames.", state->dts_drop_run);
+ state->dts_drop_run = 0;
+ }
+ }
}
if (buf->s.type == AUDIO_BUF || buf->s.type == VIDEO_BUF)
{
@@ -312,10 +329,11 @@ void hb_demux_mpeg(hb_buffer_t *buf, hb_list_t *list_es,
fdelta = buf->s.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.
+ // 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->s.start;
state->scr_delta = 0;
@@ -361,7 +379,7 @@ void hb_demux_null( hb_buffer_t * buf, hb_list_t * list_es, hb_psdemux_t* state
save_chap( state, buf );
if ( state )
{
- // if we don't have a time offset yet,
+ // if we don't have a time offset yet,
// use this timestamp as the offset.
if (state->scr_changes == 0 &&
(buf->s.start != AV_NOPTS_VALUE ||
diff --git a/libhb/internal.h b/libhb/internal.h
index 7337a1387..442816fa7 100644
--- a/libhb/internal.h
+++ b/libhb/internal.h
@@ -286,6 +286,7 @@ typedef struct {
int64_t last_pts; /* last pts we saw */
int scr_changes; /* number of SCR discontinuities */
int dts_drops; /* number of drops because DTS too far from SCR */
+ int dts_drop_run; /* number of consecutive drops */
int new_chap;
} hb_psdemux_t;
@@ -354,7 +355,8 @@ int hb_stream_seek_ts( hb_stream_t * stream, int64_t ts );
int hb_stream_seek_chapter( hb_stream_t *, int );
int hb_stream_chapter( hb_stream_t * );
-hb_buffer_t * hb_ts_decode_pkt( hb_stream_t *stream, const uint8_t * pkt );
+hb_buffer_t * hb_ts_decode_pkt( hb_stream_t *stream, const uint8_t * pkt,
+ int chapter, int discontinuity );
void hb_stream_set_need_keyframe( hb_stream_t *stream, int need_keyframe );
diff --git a/libhb/stream.c b/libhb/stream.c
index d8da41153..b1e15cea9 100644
--- a/libhb/stream.c
+++ b/libhb/stream.c
@@ -111,16 +111,31 @@ typedef enum {
#define MAX_PS_PROBE_SIZE (5*1024*1024)
#define kMaxNumberPMTStreams 32
-typedef struct {
- hb_buffer_t *buf;
- hb_buffer_t *extra_buf;
- int8_t skipbad;
- int8_t continuity;
- uint8_t pkt_summary[8];
- int pid;
- uint8_t is_pcr;
- int pes_list;
+typedef struct
+{
+ uint8_t has_stream_id_ext;
+ uint8_t stream_id;
+ uint8_t stream_id_ext;
+ uint8_t bd_substream_id;
+ int64_t pts;
+ int64_t dts;
+ int64_t scr;
+ int header_len;
+ int packet_len;
+} hb_pes_info_t;
+typedef struct {
+ hb_buffer_t * buf;
+ hb_pes_info_t pes_info;
+ int8_t pes_info_valid;
+ int packet_len;
+ int packet_offset;
+ int8_t skipbad;
+ int8_t continuity;
+ uint8_t pkt_summary[8];
+ int pid;
+ uint8_t is_pcr;
+ int pes_list;
} hb_ts_stream_t;
typedef struct {
@@ -161,11 +176,8 @@ struct hb_stream_s
struct
{
+ int discontinuity;
uint8_t found_pcr; // non-zero if we've found at least one pcr
- int pcr_out; // sequence number of most recent output pcr
- int pcr_in; // sequence number of most recent input pcr
- int pcr_discontinuity; // sequence number of last discontinuity
- int pcr_current; // last discontinuity sent to reader
int64_t pcr; // most recent input pcr
int64_t last_timestamp; // used for discontinuity detection when
// there are no PCRs
@@ -234,19 +246,6 @@ typedef struct {
int size;
} bitbuf_t;
-typedef struct
-{
- uint8_t has_stream_id_ext;
- uint8_t stream_id;
- uint8_t stream_id_ext;
- uint8_t bd_substream_id;
- int64_t pts;
- int64_t dts;
- int64_t scr;
- int header_len;
- int packet_len;
-} hb_pes_info_t;
-
/***********************************************************************
* Local prototypes
@@ -378,7 +377,7 @@ static int index_of_ps_stream(hb_stream_t *stream, int id, int sid)
return i;
}
}
- // If there is no match on the stream_id_ext, try matching
+ // If there is no match on the stream_id_ext, try matching
// on only the stream_id.
for ( i = 0; i < stream->pes.count; ++i )
{
@@ -658,9 +657,7 @@ static void hb_stream_delete_dynamic( hb_stream_t *d )
if (d->ts.list[i].buf)
{
hb_buffer_close(&(d->ts.list[i].buf));
- hb_buffer_close(&(d->ts.list[i].extra_buf));
d->ts.list[i].buf = NULL;
- d->ts.list[i].extra_buf = NULL;
}
}
}
@@ -775,7 +772,7 @@ static void prune_streams(hb_stream_t *d)
}
}
}
- // reset to beginning of file and reset some stream
+ // reset to beginning of file and reset some stream
// state information
hb_stream_seek( d, 0. );
}
@@ -800,7 +797,7 @@ static void prune_streams(hb_stream_t *d)
continue;
}
}
- // reset to beginning of file and reset some stream
+ // reset to beginning of file and reset some stream
// state information
hb_stream_seek( d, 0. );
}
@@ -851,7 +848,7 @@ hb_stream_open(hb_handle_t *h, char *path, hb_title_t *title, int scan)
{
prune_streams( d );
}
- // reset to beginning of file and reset some stream
+ // reset to beginning of file and reset some stream
// state information
hb_stream_seek( d, 0. );
return d;
@@ -992,9 +989,7 @@ hb_stream_t * hb_bd_stream_open( hb_handle_t *h, hb_title_t *title )
for ( ii = 0; ii < d->ts.count; ii++ )
{
d->ts.list[ii].buf = hb_buffer_init(d->packetsize);
- d->ts.list[ii].extra_buf = hb_buffer_init(d->packetsize);
d->ts.list[ii].buf->size = 0;
- d->ts.list[ii].extra_buf->size = 0;
}
return d;
@@ -1104,7 +1099,7 @@ hb_title_t * hb_stream_title_scan(hb_stream_t *stream, hb_title_t * title)
title->flags |= HBTF_NO_IDR;
}
- if ( stream->hb_stream_type == transport &&
+ if ( stream->hb_stream_type == transport &&
( stream->ts_flags & TS_HAS_PCR ) == 0 )
{
hb_log( "transport stream missing PCRs - using video DTS instead" );
@@ -2227,9 +2222,7 @@ static int hb_ts_stream_init(hb_stream_t *stream)
{
// demuxing buffer for TS to PS conversion
stream->ts.list[i].buf = hb_buffer_init(stream->packetsize);
- stream->ts.list[i].extra_buf = hb_buffer_init(stream->packetsize);
stream->ts.list[i].buf->size = 0;
- stream->ts.list[i].extra_buf->size = 0;
}
hb_ts_resolve_pid_types(stream);
@@ -2243,7 +2236,7 @@ static int hb_ts_stream_init(hb_stream_t *stream)
{
hb_log( " 0x%x type %s (0x%x)%s",
stream->ts.list[i].pid,
- stream_type_name2(stream,
+ stream_type_name2(stream,
&stream->pes.list[stream->ts.list[i].pes_list]),
ts_stream_type( stream, i ),
stream->ts.list[i].is_pcr ? " (PCR)" : "");
@@ -2487,9 +2480,9 @@ static inline unsigned int bits_peek(bitbuf_t *bb, int bits)
if (bits > 0)
{
int pos = bpos >> 3;
- int bval = (bb->buf[pos] << 24) |
- (bb->buf[pos + 1] << 16) |
- (bb->buf[pos + 2] << 8) |
+ int bval = (bb->buf[pos] << 24) |
+ (bb->buf[pos + 1] << 16) |
+ (bb->buf[pos + 2] << 8) |
bb->buf[pos + 3];
val |= (bval >> (32 - bits)) & bitmask[bits];
}
@@ -2972,8 +2965,8 @@ static int parse_pes_header(
return 0;
}
- int expect = (!!has_pts) * 5 + (has_pts & 0x01) * 5 + has_escr * 6 +
- has_esrate * 3 + has_dsm + has_copy_info + has_crc * 2 +
+ int expect = (!!has_pts) * 5 + (has_pts & 0x01) * 5 + has_escr * 6 +
+ has_esrate * 3 + has_dsm + has_copy_info + has_crc * 2 +
has_ext;
if ( bits_bytes_left(&bb_hdr) < expect )
@@ -3031,7 +3024,7 @@ static int parse_pes_header(
bits_skip(&bb_hdr, 3); // reserved bits
int has_ext2 = bits_get(&bb_hdr, 1);
- expect = (has_private) * 16 + has_pack + has_counter * 2 +
+ expect = (has_private) * 16 + has_pack + has_counter * 2 +
has_pstd * 2 + has_ext2 * 2;
if ( bits_bytes_left(&bb_hdr) < expect )
@@ -3538,7 +3531,7 @@ static int update_ps_streams( hb_stream_t * stream, int stream_id, int stream_id
{
// Assume primary video stream has the smallest stream id
// and only use the primary. move the current item
- // to the end of the list. we want to keep it for
+ // to the end of the list. we want to keep it for
// debug and informational purposes.
int jj = new_pes( stream );
memcpy( &stream->pes.list[jj], &stream->pes.list[ii],
@@ -3618,14 +3611,14 @@ static int update_ts_streams( hb_stream_t * stream, int pid, int stream_id_ext,
break;
}
// Resolve multiple videos
- if ( kind == V && ts_stream_kind( stream, ii ) == V &&
+ if ( kind == V && ts_stream_kind( stream, ii ) == V &&
pes_idx < stream->ts.list[ii].pes_list )
{
// We have a new candidate for the primary video. Move
// the current video to the end of the list. And put the
// new video in this slot
int jj = new_pid( stream );
- memcpy( &stream->ts.list[jj], &stream->ts.list[ii],
+ memcpy( &stream->ts.list[jj], &stream->ts.list[ii],
sizeof( hb_ts_stream_t ) );
break;
}
@@ -3711,7 +3704,7 @@ static int decode_ps_map( hb_stream_t * stream, uint8_t *buf, int len )
// This is how most PS streams specify this type of audio.
//
// TiVo sets the stream id to 0xbd and does not
- // give a substream id. This limits them to one audio
+ // give a substream id. This limits them to one audio
// stream and differs from how everyone else specifies
// this type of audio.
if ( stream_id < 0xb9 )
@@ -3780,7 +3773,7 @@ static void hb_ps_stream_find_streams(hb_stream_t *stream)
{
// program stream map
// Note that if there is a program map, any
- // extrapolation that is made below based on
+ // extrapolation that is made below based on
// stream id may be overridden by entry in the map.
if ( decode_ps_map( stream, buf->data, buf->size ) )
{
@@ -3813,7 +3806,7 @@ static void hb_ps_stream_find_streams(hb_stream_t *stream)
pes_info.bd_substream_id, 0, -1 );
stream->pes.list[idx].stream_kind = S;
stream->pes.list[idx].codec = WORK_DECVOBSUB;
- strncpy(stream->pes.list[idx].codec_name,
+ strncpy(stream->pes.list[idx].codec_name,
"DVD Subtitle", 80);
continue;
}
@@ -3856,7 +3849,7 @@ static void hb_ps_stream_find_streams(hb_stream_t *stream)
// HD-DVD uses this for both ac3 and eac3.
// Check ac3 bitstream_id to distinguish between them.
bitbuf_t bb;
- bits_init(&bb, buf->data + pes_info.header_len,
+ bits_init(&bb, buf->data + pes_info.header_len,
buf->size - pes_info.header_len, 0);
int sync = bits_get(&bb, 16);
if ( sync == 0x0b77 )
@@ -3891,7 +3884,7 @@ static void hb_ps_stream_find_streams(hb_stream_t *stream)
else if ( ( pes_info.stream_id & 0xf0 ) == 0xe0 )
{
// Normally this is MPEG video, but MPEG-1 PS streams
- // (which do not have a program stream map) may use
+ // (which do not have a program stream map) may use
// this for other types of video.
//
// Also, the hddvd tards decided to use 0xe2 and 0xe3 for
@@ -3912,7 +3905,7 @@ static void hb_ps_stream_find_streams(hb_stream_t *stream)
stream_type = 0xea;
}
else
- {
+ {
// mark as unknown and probe.
stream_type = 0x00;
}
@@ -4169,7 +4162,7 @@ static void hb_ts_resolve_pid_types(hb_stream_t *stream)
// 0xa2 is DTS-HD LBR used in HD-DVD and bluray for
// secondary audio streams. Libav can not decode yet.
// Having it in the audio list causes delays during scan
- // while we try to get stream parameters. So skip
+ // while we try to get stream parameters. So skip
// this type for now.
if ( stype == 0x85 &&
stream->reg_desc == STR4_TO_UINT32("HDMV") )
@@ -4449,53 +4442,81 @@ static int64_t pes_timestamp( const uint8_t *buf )
return ts;
}
+static int stream_kind_to_buf_type(int kind)
+{
+ switch (kind)
+ {
+ case A:
+ return AUDIO_BUF;
+ case V:
+ return VIDEO_BUF;
+ case S:
+ return SUBTITLE_BUF;
+ default:
+ return OTHER_BUF;
+ }
+}
+
static hb_buffer_t * generate_output_data(hb_stream_t *stream, int curstream)
{
hb_buffer_t *buf = NULL, *first = NULL;
- hb_pes_info_t pes_info;
- hb_buffer_t * b = stream->ts.list[curstream].buf;
- if ( !hb_parse_ps( stream, b->data, b->size, &pes_info ) )
+ hb_ts_stream_t * ts_stream = &stream->ts.list[curstream];
+ hb_buffer_t * b = ts_stream->buf;
+ if (!ts_stream->pes_info_valid)
{
- b->size = 0;
- return NULL;
+ if (!hb_parse_ps(stream, b->data, b->size, &ts_stream->pes_info))
+ {
+ b->size = 0;
+ ts_stream->packet_len = 0;
+ ts_stream->packet_offset = 0;
+ return NULL;
+ }
+ ts_stream->pes_info_valid = 1;
+ ts_stream->packet_offset = ts_stream->pes_info.header_len;
}
- uint8_t *tdat = b->data + pes_info.header_len;
- int size = b->size - pes_info.header_len;
+ uint8_t *tdat = b->data + ts_stream->packet_offset;
+ int es_size = b->size - ts_stream->packet_offset;
- if ( size <= 0 )
+ if (es_size <= 0)
{
- b->size = 0;
return NULL;
}
int pes_idx;
- pes_idx = stream->ts.list[curstream].pes_list;
- if( stream->need_keyframe )
+ pes_idx = ts_stream->pes_list;
+ hb_pes_stream_t *pes_stream = &stream->pes.list[pes_idx];
+ if (stream->need_keyframe)
{
// we're looking for the first video frame because we're
// doing random access during 'scan'
- int kind = stream->pes.list[pes_idx].stream_kind;
- if( kind != V || !isIframe( stream, tdat, size ) )
+ int kind = pes_stream->stream_kind;
+ if (kind != V || !isIframe(stream, tdat, es_size))
{
// not the video stream or didn't find an I frame
// but we'll only wait 255 video frames for an I frame.
- if ( kind != V || ++stream->need_keyframe < 512 )
+ if (kind != V || ++stream->need_keyframe < 512)
{
b->size = 0;
+ ts_stream->pes_info_valid = 0;
+ ts_stream->packet_len = 0;
+ ts_stream->packet_offset = 0;
return NULL;
}
}
stream->need_keyframe = 0;
}
+ // Some TS streams carry multiple substreams. E.g. DTS-HD contains
+ // a core DTS substream. We demux these as separate streams here.
// Check all substreams to see if this packet matches
- for ( pes_idx = stream->ts.list[curstream].pes_list; pes_idx != -1;
- pes_idx = stream->pes.list[pes_idx].next )
+ for (pes_idx = ts_stream->pes_list; pes_idx != -1;
+ pes_idx = stream->pes.list[pes_idx].next)
{
- if ( stream->pes.list[pes_idx].stream_id_ext != pes_info.stream_id_ext &&
- stream->pes.list[pes_idx].stream_id_ext != 0 )
+ hb_pes_stream_t *pes_stream = &stream->pes.list[pes_idx];
+ if (pes_stream->stream_id_ext != ts_stream->pes_info.stream_id_ext &&
+ pes_stream->stream_id_ext != 0)
{
continue;
}
@@ -4504,83 +4525,88 @@ static hb_buffer_t * generate_output_data(hb_stream_t *stream, int curstream)
// we want the whole TS stream including all substreams.
// DTS-HD is an example of this.
- if ( first == NULL )
- first = buf = hb_buffer_init( size );
+ if (first == NULL)
+ {
+ first = buf = hb_buffer_init(es_size);
+ }
else
{
- hb_buffer_t *tmp = hb_buffer_init( size );
+ hb_buffer_t *tmp = hb_buffer_init(es_size);
buf->next = tmp;
buf = tmp;
}
- buf->s.id = get_id( &stream->pes.list[pes_idx] );
- switch (stream->pes.list[pes_idx].stream_kind)
- {
- case A:
- buf->s.type = AUDIO_BUF;
- break;
-
- case V:
- buf->s.type = VIDEO_BUF;
- break;
+ buf->s.id = get_id(pes_stream);
+ buf->s.type = stream_kind_to_buf_type(pes_stream->stream_kind);
+ buf->s.discontinuity = stream->ts.discontinuity;
+ stream->ts.discontinuity = 0;
+ buf->s.new_chap = b->s.new_chap;
+ b->s.new_chap = 0;
- default:
- buf->s.type = OTHER_BUF;
- break;
- }
-
- if( b->sequence > stream->ts.pcr_out )
+ // put the PTS & possible DTS into 'start' & 'renderOffset'
+ // only put timestamps on the first output buffer for this PES packet.
+ if (ts_stream->packet_offset > 0)
{
- // we have a new pcr
- stream->ts.pcr_out = b->sequence;
- buf->s.pcr = b->s.pcr;
- if( b->sequence >= stream->ts.pcr_discontinuity )
- stream->ts.pcr_current = stream->ts.pcr_discontinuity;
+ buf->s.pcr = stream->ts.pcr;
+ stream->ts.pcr = AV_NOPTS_VALUE;
+ buf->s.start = ts_stream->pes_info.pts;
+ buf->s.renderOffset = ts_stream->pes_info.dts;
}
else
{
buf->s.pcr = AV_NOPTS_VALUE;
- }
-
- // check if this packet was referenced to an older pcr and if that
- // pcr was prior to a discontinuity.
- if( b->sequence < stream->ts.pcr_current )
- {
- // we've sent up a new pcr but have a packet referenced to an
- // old pcr and the difference was enough to trigger a discontinuity
- // correction. smash the timestamps or we'll mess up the correction.
buf->s.start = AV_NOPTS_VALUE;
buf->s.renderOffset = AV_NOPTS_VALUE;
- buf->s.stop = AV_NOPTS_VALUE;
- buf->s.pcr = AV_NOPTS_VALUE;
}
- else
- {
- // put the PTS & possible DTS into 'start' & 'renderOffset'
- // then strip off the PES header.
- buf->s.start = pes_info.pts;
- buf->s.renderOffset = pes_info.dts;
- }
- memcpy( buf->data, tdat, size );
+ // copy the elementary stream data into the buffer
+ memcpy(buf->data, tdat, es_size);
}
+ if (ts_stream->pes_info.packet_len > 0 &&
+ ts_stream->packet_len >= ts_stream->pes_info.packet_len + 6)
+ {
+ ts_stream->pes_info_valid = 0;
+ ts_stream->packet_len = 0;
+ }
b->size = 0;
+ ts_stream->packet_offset = 0;
return first;
}
-static void hb_ts_stream_append_pkt(hb_stream_t *stream, int idx, const uint8_t *buf, int len)
+static void hb_ts_stream_append_pkt(hb_stream_t *stream, int idx,
+ const uint8_t *buf, int len)
{
+ if (stream->ts.list[idx].skipbad || len <= 0)
+ return;
+
if (stream->ts.list[idx].buf->size + len > stream->ts.list[idx].buf->alloc)
{
int size;
- size = MAX( stream->ts.list[idx].buf->alloc * 2,
- stream->ts.list[idx].buf->size + len);
+ size = MAX(stream->ts.list[idx].buf->alloc * 2,
+ stream->ts.list[idx].buf->size + len);
hb_buffer_realloc(stream->ts.list[idx].buf, size);
}
- memcpy( stream->ts.list[idx].buf->data + stream->ts.list[idx].buf->size,
- buf, len);
+ memcpy(stream->ts.list[idx].buf->data + stream->ts.list[idx].buf->size,
+ buf, len);
stream->ts.list[idx].buf->size += len;
+ stream->ts.list[idx].packet_len += len;
+}
+
+static hb_buffer_t * flush_ts_streams( hb_stream_t *stream )
+{
+ hb_buffer_t *out, **last;
+ int ii;
+
+ last = &out;
+ for (ii = 0; ii < stream->ts.count; ii++)
+ {
+ *last = generate_output_data(stream, ii);
+ // generate_output_data can generate 0 or multiple output buffers
+ while (*last != NULL)
+ last = &(*last)->next;
+ }
+ return out;
}
/***********************************************************************
@@ -4588,7 +4614,8 @@ static void hb_ts_stream_append_pkt(hb_stream_t *stream, int idx, const uint8_t
***********************************************************************
*
**********************************************************************/
-hb_buffer_t * hb_ts_decode_pkt( hb_stream_t *stream, const uint8_t * pkt )
+hb_buffer_t * hb_ts_decode_pkt( hb_stream_t *stream, const uint8_t * pkt,
+ int chapter, int discontinuity )
{
/*
* stash the output buffer pointer in our stream so we don't have to
@@ -4596,7 +4623,19 @@ hb_buffer_t * hb_ts_decode_pkt( hb_stream_t *stream, const uint8_t * pkt )
*/
int video_index = ts_index_of_video(stream);
int curstream;
- hb_buffer_t *buf;
+ hb_buffer_t *out = NULL;
+ hb_buffer_t **last;
+
+ last = &out;
+
+ if (chapter > 0)
+ {
+ stream->chapter = chapter;
+ }
+ if (discontinuity)
+ {
+ stream->ts.discontinuity = 1;
+ }
/* This next section validates the packet */
@@ -4635,9 +4674,17 @@ hb_buffer_t * hb_ts_decode_pkt( hb_stream_t *stream, const uint8_t * pkt )
}
}
- if ( adapt_len > 0 )
+ if (discontinuity)
{
- if ( pkt[5] & 0x40 )
+ // If there is a discontinuity, flush all data
+ *last = flush_ts_streams(stream);
+ // flush_ts_streams can generate 0 or multiple output buffers
+ while (*last != NULL)
+ last = &(*last)->next;
+ }
+ if (adapt_len > 0)
+ {
+ if (pkt[5] & 0x40)
{
// found a random access point
}
@@ -4647,29 +4694,26 @@ hb_buffer_t * hb_ts_decode_pkt( hb_stream_t *stream, const uint8_t * pkt )
// JAS: I have a badly mastered BD that does adaptation field
// stuffing incorrectly which results in invalid PCRs. Test
// for all 0xff to guard against this.
- if ( adapt_len > 7 && ( pkt[5] & 0x10 ) != 0 &&
+ if (adapt_len > 7 && (pkt[5] & 0x10) != 0 &&
!(pkt[5] == 0xff && pkt[6] == 0xff && pkt[7] == 0xff &&
pkt[8] == 0xff && pkt[9] == 0xff && pkt[10] == 0xff))
{
+ // When we get a new pcr, we flush all data that was
+ // referenced to the last pcr. This makes it easier
+ // for reader to resolve pcr discontinuities.
+ *last = flush_ts_streams(stream);
+ // flush_ts_streams can generate 0 or multiple output buffers
+ while (*last != NULL)
+ last = &(*last)->next;
+
int64_t pcr;
- pcr = ( (uint64_t)pkt[6] << (33 - 8) ) |
- ( (uint64_t)pkt[7] << (33 - 16) ) |
- ( (uint64_t)pkt[8] << (33 - 24) ) |
- ( (uint64_t)pkt[9] << (33 - 32) ) |
- ( pkt[10] >> 7 );
- ++stream->ts.pcr_in;
+ pcr = ((uint64_t)pkt[ 6] << (33 - 8) ) |
+ ((uint64_t)pkt[ 7] << (33 - 16) ) |
+ ((uint64_t)pkt[ 8] << (33 - 24) ) |
+ ((uint64_t)pkt[ 9] << (33 - 32) ) |
+ ( pkt[10] >> 7 );
stream->ts.found_pcr = 1;
stream->ts_flags |= TS_HAS_PCR;
- // Check for a pcr discontinuity.
- // The reason for the uint cast on the pcr difference is that the
- // difference is significant if it advanced by more than 200ms or
- // if it went backwards by any amount. The negative numbers look
- // like huge unsigned ints so the cast allows both conditions to
- // be checked at once.
- if ( (uint64_t)( pcr - stream->ts.pcr ) > 200*90LL )
- {
- stream->ts.pcr_discontinuity = stream->ts.pcr_in;
- }
stream->ts.pcr = pcr;
}
}
@@ -4679,21 +4723,21 @@ hb_buffer_t * hb_ts_decode_pkt( hb_stream_t *stream, const uint8_t * pkt )
// Unfortunately the HD Home Run appears to null out the PCR so if
// we didn't detect a PCR during scan keep going and we'll use
// the video stream DTS for the PCR.
- if ( !stream->ts.found_pcr && ( stream->ts_flags & TS_HAS_PCR ) )
+ if (!stream->ts.found_pcr && (stream->ts_flags & TS_HAS_PCR))
{
- return NULL;
+ return out;
}
// Get continuity
// Continuity only increments for adaption values of 0x3 or 0x01
// and is not checked for start packets.
-
+ hb_ts_stream_t * ts_stream = &stream->ts.list[curstream];
int start = (pkt[1] & 0x40) != 0;
if ( (adaption & 0x01) != 0 )
{
int continuity = (pkt[3] & 0xF);
- if ( continuity == stream->ts.list[curstream].continuity )
+ if ( continuity == ts_stream->continuity )
{
// Spliced transport streams can have duplicate
// continuity counts at the splice boundary.
@@ -4712,45 +4756,45 @@ hb_buffer_t * hb_ts_decode_pkt( hb_stream_t *stream, const uint8_t * pkt )
{
memset(&summary[2], 0, 6);
}
- if ( memcmp( summary, stream->ts.list[curstream].pkt_summary, 8 ) == 0 )
+ if ( memcmp( summary, ts_stream->pkt_summary, 8 ) == 0 )
{
// 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.
- return NULL;
+ return out;
}
}
- if ( !start && (stream->ts.list[curstream].continuity != -1) &&
- !stream->ts.list[curstream].skipbad &&
- (continuity != ( (stream->ts.list[curstream].continuity + 1) & 0xf ) ) )
+ if ( !start && (ts_stream->continuity != -1) &&
+ !ts_stream->skipbad &&
+ (continuity != ( (ts_stream->continuity + 1) & 0xf ) ) )
{
ts_err( stream, curstream, "continuity error: got %d expected %d",
(int)continuity,
- (stream->ts.list[curstream].continuity + 1) & 0xf );
- stream->ts.list[curstream].continuity = continuity;
- return NULL;
+ (ts_stream->continuity + 1) & 0xf );
+ ts_stream->continuity = continuity;
+ return out;
}
- stream->ts.list[curstream].continuity = continuity;
+ ts_stream->continuity = continuity;
// Save a summary of this packet for later duplicate
// testing. The summary includes some header information
// and payload bytes. Should be enough to detect
// non-duplicates.
- stream->ts.list[curstream].pkt_summary[0] = adaption;
- stream->ts.list[curstream].pkt_summary[1] = adapt_len;
+ ts_stream->pkt_summary[0] = adaption;
+ ts_stream->pkt_summary[1] = adapt_len;
if (adapt_len + 4 + 6 + 9 <= 188)
{
- memcpy(&stream->ts.list[curstream].pkt_summary[2],
+ memcpy(&ts_stream->pkt_summary[2],
pkt+4+adapt_len+9, 6);
}
else
{
- memset(&stream->ts.list[curstream].pkt_summary[2], 0, 6);
+ memset(&ts_stream->pkt_summary[2], 0, 6);
}
}
- if ( ts_stream_kind( stream, curstream ) == P )
+ if (ts_stream_kind( stream, curstream ) == P)
{
// This is a stream that only contains PCRs. No need to process
// the remainder of the packet.
@@ -4758,107 +4802,104 @@ hb_buffer_t * hb_ts_decode_pkt( hb_stream_t *stream, const uint8_t * pkt )
// I ran across a poorly mastered BD that does not properly pad
// the adaptation field and causes parsing errors below if we
// do not exit early here.
- return NULL;
+ return out;
}
/* If we get here the packet is valid - process its data */
-
-
- if ( start )
+ if (start)
{
- // Found a random access point or we have finished generating a PES
- // and must start a new one.
+ // Found the start of a new PES packet.
+ // If we have previous packet data on this stream,
+ // output the elementary stream data for that packet.
+ if (ts_stream->buf->size > 0)
+ {
+ // we have to ship the old packet before updating the pcr
+ // since the packet we've been accumulating is referenced
+ // to the old pcr.
+ *last = generate_output_data(stream, curstream);
+ // generate_output_data can generate 0 or multiple output buffers
+ while (*last != NULL)
+ last = &(*last)->next;
+ ts_stream->pes_info_valid = 0;
+ ts_stream->packet_len = 0;
+ }
// PES must begin with an mpeg start code
const uint8_t *pes = pkt + adapt_len + 4;
- if ( pes[0] != 0x00 || pes[1] != 0x00 || pes[2] != 0x01 )
+ if (pes[0] != 0x00 || pes[1] != 0x00 || pes[2] != 0x01)
{
ts_err( stream, curstream, "missing start code" );
- stream->ts.list[curstream].skipbad = 1;
- return NULL;
+ ts_stream->skipbad = 1;
+ return out;
}
// If we were skipping a bad packet, start fresh on this new PES packet
- if (stream->ts.list[curstream].skipbad == 1)
- {
- stream->ts.list[curstream].skipbad = 0;
- }
+ ts_stream->skipbad = 0;
- if ( curstream == video_index )
+ if (curstream == video_index)
{
++stream->frames;
// if we don't have a pcr yet use the dts from this frame
// to attempt to detect discontinuities
- if ( !stream->ts.found_pcr )
+ if (!stream->ts.found_pcr)
{
// PES must begin with an mpeg start code & contain
// a DTS or PTS.
- const uint8_t *pes = pkt + adapt_len + 4;
- if ( pes[0] != 0x00 || pes[1] != 0x00 || pes[2] != 0x01 ||
- ( pes[7] >> 6 ) == 0 )
+ if (stream->ts.last_timestamp < 0 && (pes[7] >> 6) == 0)
{
- return NULL;
+ return out;
}
- // if we have a dts use it otherwise use the pts
- int64_t timestamp;
- timestamp = pes_timestamp( pes + ( pes[7] & 0x40?14:9 ) );
- if( stream->ts.last_timestamp < 0 ||
- timestamp - stream->ts.last_timestamp > 90 * 600 ||
- stream->ts.last_timestamp - timestamp > 90 * 600 )
+ if ((pes[7] >> 6) != 0)
{
- stream->ts.pcr = timestamp;
- ++stream->ts.pcr_in;
- stream->ts.pcr_discontinuity = stream->ts.pcr_in;
+ // if we have a dts use it otherwise use the pts
+ // We simulate a psuedo-PCR here by sampling a timestamp
+ // about every 600ms.
+ int64_t timestamp;
+ timestamp = pes_timestamp(pes + (pes[7] & 0x40 ? 14 : 9));
+ if (stream->ts.last_timestamp < 0 ||
+ timestamp - stream->ts.last_timestamp > 90 * 600 ||
+ stream->ts.last_timestamp - timestamp > 90 * 600)
+ {
+ stream->ts.pcr = timestamp;
+ }
+ stream->ts.last_timestamp = timestamp;
}
- stream->ts.last_timestamp = timestamp;
}
}
+ }
- // 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.list[curstream].buf->size )
- {
- // we have to ship the old packet before updating the pcr
- // since the packet we've been accumulating is referenced
- // to the old pcr.
- buf = generate_output_data(stream, curstream);
+ // Add the payload for this packet to the current buffer
+ hb_ts_stream_append_pkt(stream, curstream, pkt + 4 + adapt_len,
+ 184 - adapt_len);
+ if (stream->chapter > 0 &&
+ stream->pes.list[ts_stream->pes_list].stream_kind == V)
+ {
+ ts_stream->buf->s.new_chap = stream->chapter;
+ stream->chapter = 0;
+ }
- if ( buf )
- {
- // Output data is ready.
- // remember the pcr that was in effect when we started
- // this packet.
- stream->ts.list[curstream].buf->sequence = stream->ts.pcr_in;
- stream->ts.list[curstream].buf->s.pcr = stream->ts.pcr;
- hb_ts_stream_append_pkt(stream, curstream, pkt + 4 + adapt_len,
- 184 - adapt_len);
- return buf;
- }
+ if (!ts_stream->pes_info_valid && ts_stream->buf->size >= 19)
+ {
+ if (hb_parse_ps(stream, ts_stream->buf->data, ts_stream->buf->size,
+ &ts_stream->pes_info))
+ {
+ ts_stream->pes_info_valid = 1;
+ ts_stream->packet_offset = ts_stream->pes_info.header_len;
}
- // remember the pcr that was in effect when we started this packet.
- stream->ts.list[curstream].buf->sequence = stream->ts.pcr_in;
- stream->ts.list[curstream].buf->s.pcr = stream->ts.pcr;
}
- // Add the payload for this packet to the current buffer
- if (!stream->ts.list[curstream].skipbad && (184 - adapt_len) > 0)
- {
- hb_ts_stream_append_pkt(stream, curstream, pkt + 4 + adapt_len,
- 184 - adapt_len);
- // see if we've hit the end of this PES packet
- const uint8_t *pes = stream->ts.list[curstream].buf->data;
- int len = ( pes[4] << 8 ) + pes[5] + 6;
- if ( len > 6 && stream->ts.list[curstream].buf->size == len &&
- pes[0] == 0x00 && pes[1] == 0x00 && pes[2] == 0x01 )
- {
- buf = generate_output_data(stream, curstream);
- if ( buf )
- return buf;
- }
+ // see if we've hit the end of this PES packet
+ if (ts_stream->pes_info_valid &&
+ ts_stream->pes_info.packet_len > 0 &&
+ ts_stream->packet_len >= ts_stream->pes_info.packet_len + 6)
+ {
+ // generate_output_data can generate 0 or multiple output buffers
+ *last = generate_output_data(stream, curstream);
+ while (*last != NULL)
+ last = &(*last)->next;
}
- return NULL;
+ return out;
}
static hb_buffer_t * hb_ts_stream_decode( hb_stream_t *stream )
@@ -4877,7 +4918,7 @@ static hb_buffer_t * hb_ts_stream_decode( hb_stream_t *stream )
return NULL;
}
- b = hb_ts_decode_pkt( stream, buf );
+ b = hb_ts_decode_pkt( stream, buf, 0, 0 );
if ( b )
{
return b;
@@ -4908,8 +4949,6 @@ void hb_ts_stream_reset(hb_stream_t *stream)
{
if ( stream->ts.list[i].buf )
stream->ts.list[i].buf->size = 0;
- if ( stream->ts.list[i].extra_buf )
- stream->ts.list[i].extra_buf->size = 0;
stream->ts.list[i].skipbad = 1;
stream->ts.list[i].continuity = -1;
}
@@ -4917,10 +4956,7 @@ void hb_ts_stream_reset(hb_stream_t *stream)
stream->need_keyframe = 1;
stream->ts.found_pcr = 0;
- stream->ts.pcr_out = 0;
- stream->ts.pcr_in = 0;
stream->ts.pcr = AV_NOPTS_VALUE;
- stream->ts.pcr_current = -1;
stream->ts.last_timestamp = AV_NOPTS_VALUE;
stream->frames = 0;