summaryrefslogtreecommitdiffstats
path: root/libhb/stream.c
diff options
context:
space:
mode:
authorvan <[email protected]>2008-04-25 07:11:39 +0000
committervan <[email protected]>2008-04-25 07:11:39 +0000
commitb0efad118331a53c6928bba4e0ba5a855d2e3c28 (patch)
tree29a921457c2370ed7ea6c87e921f7eda0829067f /libhb/stream.c
parenta9bfe5ac17c00f334b813e78a4fb9394ad8dd148 (diff)
Changes to support unstructured program streams (such as those produced by a Tivo) and the three other forms of transport streams (m2ts, dvb-s & digicipher). Start of the changes necessary to support other types of video besides mpeg2.
git-svn-id: svn://svn.handbrake.fr/HandBrake/trunk@1439 b64f7644-9d1e-0410-96f1-a4d463321fa5
Diffstat (limited to 'libhb/stream.c')
-rwxr-xr-xlibhb/stream.c805
1 files changed, 463 insertions, 342 deletions
diff --git a/libhb/stream.c b/libhb/stream.c
index 1d1ea2b26..065a051e5 100755
--- a/libhb/stream.c
+++ b/libhb/stream.c
@@ -14,8 +14,9 @@
typedef enum {
hb_stream_type_unknown = 0,
- hb_stream_type_transport,
- hb_stream_type_program
+ transport,
+ program,
+ dvd_program
} hb_stream_type_t;
#define kMaxNumberVideoPIDS 1
@@ -30,10 +31,12 @@ struct hb_stream_s
int errors; /* total errors so far */
int last_error_frame; /* frame # at last error message */
int last_error_count; /* # errors at last error message */
+ int packetsize; /* Transport Stream packet size */
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 */
+ uint8_t *ts_packet; /* buffer for one TS packet */
uint8_t *ts_buf[kMaxNumberDecodeStreams];
int ts_pos[kMaxNumberDecodeStreams];
int8_t ts_foundfirst[kMaxNumberDecodeStreams];
@@ -50,19 +53,18 @@ struct hb_stream_s
* we learn during the initial scan but cache so it can be
* reused during the conversion read.
*/
- int16_t ts_video_pids[kMaxNumberVideoPIDS];
- int16_t ts_audio_pids[kMaxNumberAudioPIDS];
-
uint8_t ts_number_video_pids;
uint8_t ts_number_audio_pids;
+ int16_t ts_video_pids[kMaxNumberVideoPIDS];
+ int16_t ts_audio_pids[kMaxNumberAudioPIDS];
+
uint8_t ts_streamid[kMaxNumberDecodeStreams];
- uint8_t ts_video_stream_type[kMaxNumberDecodeStreams];
- uint8_t ts_audio_stream_type[kMaxNumberDecodeStreams];
+ uint8_t ts_stream_type[kMaxNumberDecodeStreams];
char *path;
FILE *file_handle;
- hb_stream_type_t stream_type;
+ hb_stream_type_t hb_stream_type;
int opentype;
struct {
@@ -112,7 +114,7 @@ static void hb_ts_stream_reset(hb_stream_t *stream);
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);
-static off_t align_to_next_packet(FILE* f);
+static off_t align_to_next_packet(hb_stream_t *stream);
/*
* streams have a bunch of state that's learned during the scan. We don't
@@ -147,7 +149,62 @@ static void hb_stream_state_delete( hb_stream_t *ss )
free( ss );
}
-static inline int check_ps_sync(const uint8_t *buf)
+/*
+ * logging routines.
+ * these frontend hb_log because transport streams can have a lot of errors
+ * so we want to rate limit messages. this routine limits the number of
+ * messages to at most one per minute of video. other errors that occur
+ * during the minute are counted & the count is output with the next
+ * error msg we print.
+ */
+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 check_ps_sync(const uint8_t *buf)
{
// a legal MPEG program stream must start with a Pack header in the
// first four bytes.
@@ -155,39 +212,61 @@ static inline int check_ps_sync(const uint8_t *buf)
(buf[2] == 0x01) && (buf[3] == 0xba);
}
-static inline int check_ts_sync(const uint8_t *buf)
+static int check_ps_sys(const uint8_t *buf)
+{
+ // a legal MPEG program stream must start with a Pack followed by a
+ // SYS. If we've already verified the pack, this skips over it and checks
+ // for the sys header.
+ int pos = 14 + ( buf[13] & 0x7 ); // skip over the PACK
+ return (buf[pos+0] == 0x00) && (buf[pos+1] == 0x00) &&
+ (buf[pos+2] == 0x01) && (buf[pos+3] == 0xbb);
+}
+
+static int check_ts_sync(const uint8_t *buf)
{
// must have initial sync byte, no scrambling & a legal adaptation ctrl
return (buf[0] == 0x47) && ((buf[3] >> 6) == 0) && ((buf[3] >> 4) > 0);
}
-static inline int have_ts_sync(const uint8_t *buf)
+static int have_ts_sync(const uint8_t *buf, int psize)
{
- return check_ts_sync(&buf[0*188]) && check_ts_sync(&buf[1*188]) &&
- check_ts_sync(&buf[2*188]) && check_ts_sync(&buf[3*188]) &&
- check_ts_sync(&buf[4*188]) && check_ts_sync(&buf[5*188]) &&
- check_ts_sync(&buf[6*188]) && check_ts_sync(&buf[7*188]);
+ return check_ts_sync(&buf[0*psize]) && check_ts_sync(&buf[1*psize]) &&
+ check_ts_sync(&buf[2*psize]) && check_ts_sync(&buf[3*psize]) &&
+ check_ts_sync(&buf[4*psize]) && check_ts_sync(&buf[5*psize]) &&
+ check_ts_sync(&buf[6*psize]) && check_ts_sync(&buf[7*psize]);
}
static int hb_stream_check_for_ts(const uint8_t *buf)
{
// transport streams should have a sync byte every 188 bytes.
- // search the first KB of buf looking for at least 8 consecutive
+ // search the first 8KB of buf looking for at least 8 consecutive
// correctly located sync patterns.
int offset = 0;
- for ( offset = 0; offset < 1024; ++offset )
+ for ( offset = 0; offset < 8*1024-8*188; ++offset )
{
- if ( have_ts_sync( &buf[offset]) )
- return 1;
+ if ( have_ts_sync( &buf[offset], 188) )
+ return 188 | (offset << 8);
+ if ( have_ts_sync( &buf[offset], 192) )
+ return 192 | (offset << 8);
+ if ( have_ts_sync( &buf[offset], 204) )
+ return 204 | (offset << 8);
+ if ( have_ts_sync( &buf[offset], 208) )
+ return 208 | (offset << 8);
}
return 0;
}
static int hb_stream_check_for_ps(const uint8_t *buf)
{
- // program streams should have a Pack header every 2048 bytes.
- // check that we have 4 of these.
+ // program streams should start with a PACK then a SYS header.
+ return check_ps_sync(buf) && check_ps_sys(buf);
+}
+
+static int hb_stream_check_for_dvd_ps(const uint8_t *buf)
+{
+ // DVD program streams should have a Pack header every 2048 bytes.
+ // check that we have 4 of these in a row.
return check_ps_sync(&buf[0*2048]) && check_ps_sync(&buf[1*2048]) &&
check_ps_sync(&buf[2*2048]) && check_ps_sync(&buf[3*2048]);
}
@@ -198,17 +277,28 @@ static int hb_stream_get_type(hb_stream_t *stream)
if ( fread(buf, 1, sizeof(buf), stream->file_handle) == sizeof(buf) )
{
- if ( hb_stream_check_for_ts(buf) != 0 )
+ int psize;
+ if ( ( psize = hb_stream_check_for_ts(buf) ) != 0 )
{
- hb_log("file is MPEG Transport Stream");
- stream->stream_type = hb_stream_type_transport;
+ int offset = psize >> 8;
+ psize &= 0xff;
+ hb_log("file is MPEG Transport Stream with %d byte packets"
+ " offset %d bytes", psize, offset);
+ stream->packetsize = psize;
+ stream->hb_stream_type = transport;
hb_ts_stream_init(stream);
return 1;
}
+ if ( hb_stream_check_for_dvd_ps(buf) != 0 )
+ {
+ hb_log("file is MPEG DVD Program Stream");
+ stream->hb_stream_type = dvd_program;
+ return 1;
+ }
if ( hb_stream_check_for_ps(buf) != 0 )
{
hb_log("file is MPEG Program Stream");
- stream->stream_type = hb_stream_type_program;
+ stream->hb_stream_type = program;
return 1;
}
}
@@ -225,6 +315,11 @@ static void hb_stream_delete_dynamic( hb_stream_t *d )
int i=0;
+ if ( d->ts_packet )
+ {
+ free( d->ts_packet );
+ d->ts_packet = NULL;
+ }
for (i = 0; i < kMaxNumberDecodeStreams; i++)
{
if (d->ts_buf[i])
@@ -291,8 +386,10 @@ hb_stream_t * hb_stream_open( char *path, int opentype )
d->opentype = opentype;
d->path = strdup( path );
- if ( d->stream_type == hb_stream_type_transport )
+ if ( d->hb_stream_type == transport )
{
+ d->ts_packet = malloc( d->packetsize );
+
int i = 0;
for ( ; i < d->ts_number_video_pids + d->ts_number_audio_pids; i++)
{
@@ -308,7 +405,6 @@ hb_stream_t * hb_stream_open( char *path, int opentype )
* If it's something we can deal with (MPEG2 PS or TS) return a stream
* reference structure & null otherwise.
*/
-
if ( ss != NULL )
{
hb_stream_state_delete( ss );
@@ -377,7 +473,7 @@ static void hb_stream_delete_audio_entry(hb_stream_t *stream, int indx)
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[1 + indx] = stream->ts_audio_stream_type[1+i];
+ stream->ts_stream_type[1 + indx] = stream->ts_stream_type[1+i];
stream->ts_streamid[1 + indx] = stream->ts_streamid[1 + i];
++indx;
}
@@ -438,7 +534,7 @@ hb_title_t * hb_stream_title_scan(hb_stream_t *stream)
// the elementary stream is an audio type.
// - For program streams read the first 4MB and take every unique
// audio stream we find.
- if (stream->stream_type == hb_stream_type_transport)
+ if (stream->hb_stream_type == transport)
{
int i;
@@ -471,27 +567,80 @@ hb_title_t * hb_stream_title_scan(hb_stream_t *stream)
}
/*
+ * read the next transport stream packet from 'stream'. Return NULL if
+ * we hit eof & a pointer to the sync byte otherwise.
+ */
+static const uint8_t *next_packet( hb_stream_t *stream )
+{
+ uint8_t *buf = stream->ts_packet + stream->packetsize - 188;
+
+ while ( 1 )
+ {
+ if ( fread(stream->ts_packet, 1, stream->packetsize, stream->file_handle) !=
+ stream->packetsize )
+ {
+ return NULL;
+ }
+ if (buf[0] == 0x47)
+ {
+ return buf;
+ }
+ // lost sync - back up to where we started then try to re-establish.
+ off_t pos = ftello(stream->file_handle) - stream->packetsize;
+ off_t pos2 = align_to_next_packet(stream);
+ if ( pos2 == 0 )
+ {
+ hb_log( "next_packet: eof while re-establishing sync @ %lld", pos );
+ return NULL;
+ }
+ ts_warn( stream, "next_packet: sync lost @ %lld, regained after %lld bytes",
+ pos, pos2 );
+ }
+}
+
+/*
+ * skip to the start of the next PACK header in program stream src_stream.
+ */
+static void skip_to_next_pack( hb_stream_t *src_stream )
+{
+ // scan forward until we find the start of the next pack
+ uint32_t strt_code = -1;
+ int c;
+
+ flockfile( src_stream->file_handle );
+ while ( ( c = getc_unlocked( src_stream->file_handle ) ) != EOF )
+ {
+ strt_code = ( strt_code << 8 ) | c;
+ if ( strt_code == 0x000001ba )
+ // we found the start of the next pack
+ break;
+ }
+ funlockfile( src_stream->file_handle );
+
+ // if we didn't terminate on an eof back up so the next read
+ // starts on the pack boundary.
+ if ( c != EOF )
+ {
+ fseeko( src_stream->file_handle, -4, SEEK_CUR );
+ }
+}
+
+/*
* scan the next MB of 'stream' to find the next start packet for
* the Packetized Elementary Stream associated with TS PID 'pid'.
*/
static const uint8_t *hb_ts_stream_getPEStype(hb_stream_t *stream, uint32_t pid)
{
- static uint8_t buf[188];
int npack = 100000; // max packets to read
while (--npack >= 0)
{
- if (fread(buf, 1, 188, stream->file_handle) != 188)
+ const uint8_t *buf = next_packet( stream );
+ if ( buf == NULL )
{
hb_log("hb_ts_stream_getPEStype: EOF while searching for PID 0x%x", pid);
return 0;
}
- if (buf[0] != 0x47)
- {
- hb_log("hb_ts_stream_getPEStype: lost sync while searching for PID 0x%x", pid);
- align_to_next_packet(stream->file_handle);
- continue;
- }
/*
* The PES header is only in TS packets with 'start' set so we check
@@ -541,7 +690,7 @@ static uint64_t hb_ps_stream_getVideoPTS(hb_stream_t *stream)
hb_buffer_t *es;
// 'buf' contains an MPEG2 PACK - get a list of all it's elementary streams
- hb_demux_ps(buf, list, 0);
+ hb_demux_ps( buf, list, 0 );
while ( ( es = hb_list_item( list, 0 ) ) )
{
@@ -604,18 +753,11 @@ static struct pts_pos hb_sample_pts(hb_stream_t *stream, uint64_t fpos)
{
struct pts_pos pp = { 0, 0 };
- if ( stream->stream_type == hb_stream_type_program )
- {
- // round address down to nearest dvd sector start
- fpos &=~ ( HB_DVD_READ_BUFFER_SIZE - 1 );
- fseeko( stream->file_handle, fpos, SEEK_SET );
- pp.pts = hb_ps_stream_getVideoPTS( stream );
- }
- else
+ if ( stream->hb_stream_type == transport )
{
const uint8_t *buf;
fseeko( stream->file_handle, fpos, SEEK_SET );
- align_to_next_packet( stream->file_handle );
+ align_to_next_packet( stream );
buf = hb_ts_stream_getPEStype( stream, stream->ts_video_pids[0] );
if ( buf == NULL )
{
@@ -633,8 +775,18 @@ static struct pts_pos hb_sample_pts(hb_stream_t *stream, uint64_t fpos)
( (uint64_t)buf[12] << 7 ) |
( (uint64_t)buf[13] >> 1 );
}
+ else
+ {
+ // round address down to nearest dvd sector start
+ fpos &=~ ( HB_DVD_READ_BUFFER_SIZE - 1 );
+ fseeko( stream->file_handle, fpos, SEEK_SET );
+ if ( stream->hb_stream_type == program )
+ {
+ skip_to_next_pack( stream );
+ }
+ pp.pts = hb_ps_stream_getVideoPTS( stream );
+ }
pp.pos = ftello(stream->file_handle);
- hb_log("hb_sample_pts: pts %lld at %llu", pp.pts, pp.pos );
return pp;
}
@@ -716,12 +868,59 @@ static void hb_stream_duration(hb_stream_t *stream, hb_title_t *inTitle)
**********************************************************************/
int hb_stream_read( hb_stream_t * src_stream, hb_buffer_t * b )
{
- if ( src_stream->stream_type == hb_stream_type_program )
+ if ( src_stream->hb_stream_type == dvd_program )
{
size_t amt_read = fread(b->data, HB_DVD_READ_BUFFER_SIZE, 1,
src_stream->file_handle);
return (amt_read > 0);
}
+ if ( src_stream->hb_stream_type == program )
+ {
+ // a general program stream has arbitrary sized pack's. we're
+ // currently positioned at the start of a pack so read up to but
+ // not including the start of the next, expanding the buffer
+ // as necessary.
+ uint8_t *cp = b->data;
+ uint8_t *ep = cp + b->alloc;
+ uint32_t strt_code = -1;
+ int c;
+
+ // consume the first byte of the initial pack so we don't match on
+ // it in the loop below.
+ if ( ( c = getc( src_stream->file_handle ) ) == EOF )
+ return 0;
+
+ *cp++ = c;
+
+ flockfile( src_stream->file_handle );
+ while ( ( c = getc_unlocked( src_stream->file_handle ) ) != EOF )
+ {
+ strt_code = ( strt_code << 8 ) | c;
+ if ( strt_code == 0x000001ba )
+ // we found the start of the next pack
+ break;
+ if ( cp >= ep )
+ {
+ // need to expand the buffer
+ int curSize = cp - b->data;
+ hb_buffer_realloc( b, curSize * 2 );
+ cp = b->data + curSize;
+ ep = b->data + b->alloc;
+ }
+ *cp++ = c;
+ }
+ funlockfile( src_stream->file_handle );
+
+ // if we didn't terminate on an eof back up so the next read
+ // starts on the pack boundary.
+ b->size = cp - b->data;
+ if ( c != EOF )
+ {
+ fseeko( src_stream->file_handle, -4, SEEK_CUR );
+ b->size -= 4;
+ }
+ return 1;
+ }
return hb_ts_stream_decode( src_stream, b->data );
}
@@ -732,29 +931,33 @@ int hb_stream_read( hb_stream_t * src_stream, hb_buffer_t * b )
**********************************************************************/
int hb_stream_seek( hb_stream_t * src_stream, float f )
{
- off_t stream_size, cur_pos, new_pos;
- double pos_ratio = f;
- cur_pos = ftello(src_stream->file_handle);
- fseeko(src_stream->file_handle,0 ,SEEK_END);
- stream_size = ftello(src_stream->file_handle);
- new_pos = (off_t) ((double) (stream_size) * pos_ratio);
- new_pos &=~ (HB_DVD_READ_BUFFER_SIZE - 1);
- int r = fseeko(src_stream->file_handle, new_pos, SEEK_SET);
-
- if (r == -1)
- {
- fseeko(src_stream->file_handle, cur_pos, SEEK_SET);
- return 0;
- }
+ off_t stream_size, cur_pos, new_pos;
+ double pos_ratio = f;
+ cur_pos = ftello( src_stream->file_handle );
+ fseeko( src_stream->file_handle, 0, SEEK_END );
+ stream_size = ftello( src_stream->file_handle );
+ new_pos = (off_t) ((double) (stream_size) * pos_ratio);
+ new_pos &=~ (HB_DVD_READ_BUFFER_SIZE - 1);
+
+ int r = fseeko( src_stream->file_handle, new_pos, SEEK_SET );
+ if (r == -1)
+ {
+ fseeko( src_stream->file_handle, cur_pos, SEEK_SET );
+ return 0;
+ }
- if (src_stream->stream_type == hb_stream_type_transport)
- {
- // We need to drop the current decoder output and move
- // forwards to the next transport stream packet.
- hb_ts_stream_reset(src_stream);
- }
+ if ( src_stream->hb_stream_type == transport )
+ {
+ // We need to drop the current decoder output and move
+ // forwards to the next transport stream packet.
+ hb_ts_stream_reset(src_stream);
+ }
+ else if ( src_stream->hb_stream_type == program )
+ {
+ skip_to_next_pack( src_stream );
+ }
- return 1;
+ return 1;
}
static void set_audio_description( hb_audio_t *audio, iso639_lang_t *lang )
@@ -785,7 +988,7 @@ static hb_audio_t *hb_ts_stream_set_audio_id_and_codec(hb_stream_t *stream,
const uint8_t *buf;
fseeko(stream->file_handle, 0, SEEK_SET);
- align_to_next_packet(stream->file_handle);
+ align_to_next_packet(stream);
buf = hb_ts_stream_getPEStype(stream, stream->ts_audio_pids[aud_pid_index]);
/* check that we found a PES header */
@@ -797,10 +1000,39 @@ static hb_audio_t *hb_ts_stream_set_audio_id_and_codec(hb_stream_t *stream,
audio->config.in.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[1 + aud_pid_index],
+ stream->ts_stream_type[1 + aud_pid_index],
audio->id);
- stream->ts_audio_stream_type[1 + aud_pid_index] = 0x81;
- stream->ts_streamid[1 + aud_pid_index] = buf[3];
+ stream->ts_stream_type[1 + aud_pid_index] = 0x81;
+ stream->ts_streamid[1 + aud_pid_index] = 0xbd;
+ }
+ else if (buf[3] == 0xfd)
+ {
+ /* XXX Extended stream id (ISO 13818-1(2000) Amd 2) - we have to look
+ * inside the PES extension to find out the real stream id then
+ * figure out what the heck it means. For now we just use the stream
+ * type if one was specified. */
+ const char *atype = 0;
+ switch (stream->ts_stream_type[1 + aud_pid_index])
+ {
+ case 0x81: // AC-3
+ atype = "AC-3";
+ audio->config.in.codec = HB_ACODEC_AC3;
+ break;
+ case 0x82: // HDMV DTS
+ case 0x8a: // DTS
+ atype = "DTS";
+ audio->config.in.codec = HB_ACODEC_DCA;
+ break;
+ case 0x83: // LPCM
+ atype = "PCM";
+ audio->config.in.codec = HB_ACODEC_LPCM;
+ break;
+ }
+ audio->id = 0x80bd | (aud_pid_index << 8);
+ stream->ts_streamid[1 + aud_pid_index] = 0xbd;
+ hb_log("transport stream pid 0x%x (type 0x%x) is %s audio id 0x%x",
+ stream->ts_audio_pids[aud_pid_index],
+ stream->ts_stream_type[1 + aud_pid_index], atype, audio->id);
}
else if ((buf[3] & 0xe0) == 0xc0)
{
@@ -808,9 +1040,9 @@ static hb_audio_t *hb_ts_stream_set_audio_id_and_codec(hb_stream_t *stream,
audio->config.in.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[1 + aud_pid_index],
+ stream->ts_stream_type[1 + aud_pid_index],
audio->id);
- stream->ts_audio_stream_type[1 + aud_pid_index] = 0x03;
+ stream->ts_stream_type[1 + aud_pid_index] = 0x03;
stream->ts_streamid[1 + aud_pid_index] = buf[3];
}
}
@@ -824,7 +1056,7 @@ static hb_audio_t *hb_ts_stream_set_audio_id_and_codec(hb_stream_t *stream,
{
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[1 + aud_pid_index]);
+ stream->ts_stream_type[1 + aud_pid_index]);
}
return audio;
}
@@ -884,7 +1116,7 @@ static void hb_ps_stream_find_audio_ids(hb_stream_t *stream, hb_title_t *title)
hb_buffer_t *es;
// 'buf' contains an MPEG2 PACK - get a list of all it's elementary streams
- hb_demux_ps(buf, list, 0);
+ hb_demux_ps( buf, list, 0 );
while ( ( es = hb_list_item( list, 0 ) ) )
{
@@ -930,54 +1162,53 @@ static void hb_ts_stream_init(hb_stream_t *stream)
stream-> ts_audio_pids[i] = -1;
}
+ stream->ts_packet = malloc( stream->packetsize );
+
// Find the audio and video pids in the stream
hb_ts_stream_find_pids(stream);
- stream->ts_streamid[0] = 0xE0; // stream 0 must be video
-
for (i = 0; i < stream->ts_number_video_pids + stream->ts_number_audio_pids; i++)
{
// demuxing buffer for TS to PS conversion
stream->ts_buf[i] = malloc( HB_DVD_READ_BUFFER_SIZE );
}
+
+ stream->ts_streamid[0] = 0xE0; // stream 0 must be video
}
-// ------------------------------------------------------------------------------------
+#define MAX_HOLE 208*80
-static off_t align_to_next_packet(FILE* f)
+static off_t align_to_next_packet(hb_stream_t *stream)
{
- unsigned char buf[188*20];
-
- off_t start = ftello(f);
+ uint8_t buf[MAX_HOLE];
off_t pos = 0;
+ off_t start = ftello(stream->file_handle);
- if (fread(buf, 188*20, 1, f) == 1)
- {
- int found = 0;
- while (!found && (pos < 188))
- {
- found = 1;
- int i = 0;
- for (i = 0; i < 188*20; i += 188)
- {
- unsigned char c = buf[pos+i];
- // Check sync byte
- if ((c != 0x47) && (c != 0x72) && (c != 0x29))
- {
- // this offset failed, try next
- found = 0;
- pos++;
- break;
- }
- }
- }
- }
-
- if (pos == 188)
- pos = 0; // failed to find anything!!!!!?
+ if ( start >= stream->packetsize ) {
+ start -= stream->packetsize;
+ fseeko(stream->file_handle, start, SEEK_SET);
+ }
- fseeko(f, start+pos, SEEK_SET);
+ if (fread(buf, sizeof(buf), 1, stream->file_handle) == 1)
+ {
+ const uint8_t *bp = buf;
+ int i;
+ for ( i = sizeof(buf); --i >= 0; ++bp )
+ {
+ if ( have_ts_sync( bp, stream->packetsize ) )
+ {
+ break;
+ }
+ }
+ if ( i >= 0 )
+ {
+ pos = ( bp - buf ) - stream->packetsize + 188;
+ if ( pos < 0 )
+ pos = 0;
+ }
+ }
+ fseeko(stream->file_handle, start+pos, SEEK_SET);
return pos;
}
@@ -1063,70 +1294,53 @@ static void decode_element_descriptors(hb_stream_t* stream, int esindx,
}
}
-/*
- * Get the name of the stream from the type - thanks to VLC for this.
- */
-static const char *get_stream_name (unsigned char stream_type)
+static const char * stream_type_name (uint8_t stream_type)
{
switch( stream_type )
{
- case 0x01: /* MPEG-1 video */
- case 0x02: /* MPEG-2 video */
- case 0x80: /* MPEG-2 MOTO video */
- return("MPEG 1/2 Video");
- break;
- case 0x03: /* MPEG-1 audio */
- case 0x04: /* MPEG-2 audio */
- return("MPEG Audio");
- break;
- case 0x11: /* MPEG4 (audio) */
- case 0x0f: /* ISO/IEC 13818-7 Audio with ADTS transport syntax */
- return("MPEG-4 Audio");
- break;
- case 0x10: /* MPEG4 (video) */
- return("MPEG-4 Video");
- break;
- case 0x1B: /* H264 <- check transport syntax/needed descriptor */
- return("H.264 Video");
- break;
-
- case 0x81: /* A52 (audio) */
- return("A52/AC-3 Audio");
- break;
- case 0x82: /* DVD_SPU (sub) */
- return("Subtitle");
- break;
- case 0x83: /* LPCM (audio) */
- return("LPCM Audio");
- break;
- case 0x84: /* SDDS (audio) */
- return("SDDS Audio");
- break;
- case 0x85: /* DTS (audio) */
- return("DTS Audio");
- break;
-
- case 0x91: /* A52 vls (audio) */
- return("A52b/AC-3 Audio");
- break;
- case 0x92: /* DVD_SPU vls (sub) */
- return("Subtitle");
- break;
-
- case 0x94: /* SDDS (audio) */
- return("SDDS Audio");
- break;
+ case 0x01: return("ISO 11172 (MPEG1) Video");
+ case 0x02: return("ISO 13818-2 (MPEG2) Video");
+ case 0x03: return("ISO 11172 (MPEG1) Audio");
+ case 0x04: return("ISO 13818-3 (MPEG2) Audio");
+ case 0x05: return("ISO 13818-1 private section");
+ case 0x06: return("ISO 13818-1 PES private data");
+ case 0x07: return("ISO 13522 MHEG");
+ case 0x08: return("ISO 13818-1 DSM-CC");
+ case 0x09: return("ISO 13818-1 auxiliary");
+ case 0x0a: return("ISO 13818-6 multi-protocol encap");
+ case 0x0b: return("ISO 13818-6 DSM-CC U-N msgs");
+ case 0x0c: return("ISO 13818-6 Stream descriptors");
+ case 0x0d: return("ISO 13818-6 Sections");
+ case 0x0e: return("ISO 13818-1 auxiliary");
+ case 0x0f: return("ISO 13818-7 AAC Audio");
+ case 0x10: return("MPEG4 Video");
+ case 0x11: return("MPEG4 Audio");
+ case 0x12: return("MPEG4 generic");
+
+ case 0x14: return("ISO 13818-6 DSM-CC download");
+
+ case 0x1b: return("H.264 Video");
+
+ case 0x80: return("DigiCipher II Video");
+ case 0x81: return("A52/AC-3 Audio");
+ case 0x82: return("HDMV DTS Audio");
+ case 0x83: return("LPCM Audio");
+ case 0x84: return("SDDS Audio");
+ case 0x85: return("ATSC Program ID");
+ case 0x86: return("SCTE 35 splice info");
+ case 0x87: return("ATSC E-AC-3");
+
+ case 0x8a: return("DTS Audio");
+
+ case 0x91: return("A52b/AC-3 Audio");
+ case 0x92: return("Subtitle");
+
+ case 0x94: return("SDDS Audio");
+ case 0xa0: return("MSCODEC Video");
- case 0xa0: /* MSCODEC vlc (video) (fixed later) */
- return("MSCODEC Video");
- break;
-
- case 0x06: /* PES_PRIVATE (fixed later) */
- case 0x12: /* MPEG-4 generic (sub/scene/...) (fixed later) */
- case 0xEA: /* Privately managed ES (VC-1) (fixed later */
- default:
- return("Other");
- break;
+ case 0xea: return("VC-1 Video");
+
+ default: return("Unknown");
}
}
@@ -1164,64 +1378,63 @@ int decode_program_map(hb_stream_t* stream)
int cur_pos = 9 /* data after the section length field*/ + program_info_length;
int done_reading_stream_types = 0;
while (!done_reading_stream_types)
- {
- unsigned char stream_type = get_bits(&bb, 8);
- get_bits(&bb, 3);
- unsigned int elementary_PID = get_bits(&bb, 13);
- get_bits(&bb, 4);
- unsigned int ES_info_length = get_bits(&bb, 12);
-
- int i=0;
- unsigned char *ES_info_buf = (unsigned char *) malloc(ES_info_length);
- for (i=0; i < ES_info_length; i++)
- {
- ES_info_buf[i] = get_bits(&bb, 8);
- }
-
-
- if (stream_type == 0x02 || stream_type == 0x10 || stream_type == 0x1B)
- {
- /* MPEG-2/MPEG-4/H.264 */
- if (stream->ts_number_video_pids <= kMaxNumberVideoPIDS)
- stream->ts_number_video_pids++;
- stream->ts_video_pids[stream->ts_number_video_pids-1] = elementary_PID;
- stream->ts_video_stream_type[stream->ts_number_video_pids-1] = stream_type;
- }
- else
- {
- // Defined audio stream types are 0x81 for AC-3/A52 audio and 0x03
- // for mpeg audio. But content producers seem to use other
- // values (0x04 and 0x06 have both been observed) so at this point
- // we say everything that isn't a video pid is audio then at the end
- // of hb_stream_title_scan we'll figure out which are really audio
- // by looking at the PES headers.
- i = stream->ts_number_audio_pids;
- if (i < kMaxNumberAudioPIDS)
- stream->ts_number_audio_pids++;
- stream->ts_audio_pids[i] = elementary_PID;
- stream->ts_audio_stream_type[1 + i] = stream_type;
-
- if (ES_info_length > 0)
- {
- decode_element_descriptors(stream, i, ES_info_buf, ES_info_length);
- }
- }
-
- cur_pos += 5 /* stream header */ + ES_info_length;
-
- free(ES_info_buf);
-
- if (cur_pos >= section_length - 4 /* stop before the CRC */)
- done_reading_stream_types = 1;
- }
+ {
+ unsigned char stream_type = get_bits(&bb, 8);
+ get_bits(&bb, 3);
+ unsigned int elementary_PID = get_bits(&bb, 13);
+ get_bits(&bb, 4);
+ unsigned int ES_info_length = get_bits(&bb, 12);
+
+ int i=0;
+ unsigned char *ES_info_buf = (unsigned char *) malloc(ES_info_length);
+ for (i=0; i < ES_info_length; i++)
+ {
+ ES_info_buf[i] = get_bits(&bb, 8);
+ }
+
+
+ if (stream->ts_number_video_pids == 0 &&
+ ( stream_type == 0x02 || stream_type == 0x10 || stream_type == 0x1B ) )
+ {
+ stream->ts_video_pids[0] = elementary_PID;
+ stream->ts_stream_type[0] = stream_type;
+ stream->ts_number_video_pids = 1;
+ }
+ else
+ {
+ // Defined audio stream types are 0x81 for AC-3/A52 audio and 0x03
+ // for mpeg audio. But content producers seem to use other
+ // values (0x04 and 0x06 have both been observed) so at this point
+ // we say everything that isn't a video pid is audio then at the end
+ // of hb_stream_title_scan we'll figure out which are really audio
+ // by looking at the PES headers.
+ i = stream->ts_number_audio_pids;
+ if (i < kMaxNumberAudioPIDS)
+ {
+ stream->ts_audio_pids[i] = elementary_PID;
+ stream->ts_stream_type[1 + i] = stream_type;
+ if (ES_info_length > 0)
+ {
+ decode_element_descriptors(stream, i, ES_info_buf,
+ ES_info_length);
+ }
+ ++stream->ts_number_audio_pids;
+ }
+ }
+
+ cur_pos += 5 /* stream header */ + ES_info_length;
+
+ free(ES_info_buf);
+
+ if (cur_pos >= section_length - 4 /* stop before the CRC */)
+ done_reading_stream_types = 1;
+ }
free(descriptor_buf);
return 1;
}
-// ------------------------------------------------------------------------------------
-
-int build_program_map(unsigned char *buf, hb_stream_t *stream)
+static int build_program_map(const uint8_t *buf, hb_stream_t *stream)
{
// Get adaption header info
int adapt_len = 0;
@@ -1283,7 +1496,7 @@ int build_program_map(unsigned char *buf, hb_stream_t *stream)
return 0;
}
-int decode_PAT(unsigned char *buf, hb_stream_t *stream)
+static int decode_PAT(const uint8_t *buf, hb_stream_t *stream)
{
unsigned char tablebuf[1024];
unsigned int tablepos = 0;
@@ -1395,48 +1608,20 @@ int decode_PAT(unsigned char *buf, hb_stream_t *stream)
static void hb_ts_stream_find_pids(hb_stream_t *stream)
{
- unsigned char buf[188];
-
// align to first packet
- align_to_next_packet(stream->file_handle);
+ align_to_next_packet(stream);
// Read the Transport Stream Packets (188 bytes each) looking at first for PID 0 (the PAT PID), then decode that
// to find the program map PID and then decode that to get the list of audio and video PIDs
- int bytesReadInPacket = 0;
for (;;)
{
- // Try to read packet..
- int bytesRead;
- if ((bytesRead = fread(buf+bytesReadInPacket, 1, 188-bytesReadInPacket, stream->file_handle)) != 188-bytesReadInPacket)
- {
- if (bytesRead < 0)
- bytesRead = 0;
- bytesReadInPacket += bytesRead;
-
+ const uint8_t *buf = next_packet( stream );
+ if ( buf == NULL )
+ {
hb_log("hb_ts_stream_find_pids - end of file");
break;
}
- else
- {
- bytesReadInPacket = 0;
- }
-
- // Check sync byte
- if ((buf[0] != 0x47) && (buf[0] != 0x72) && (buf[0] != 0x29))
- {
- 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;
- }
// Get pid
int pid = (((buf[1] & 0x1F) << 8) | buf[2]) & 0x1FFF;
@@ -1474,21 +1659,21 @@ static void hb_ts_stream_find_pids(hb_stream_t *stream)
hb_log("hb_ts_stream_find_pids - found the following PIDS");
hb_log(" Video PIDS : ");
- int i=0;
+ int i;
for (i=0; i < stream->ts_number_video_pids; i++)
{
- hb_log(" 0x%x (%d) [Type %s (0x%x)]",
- stream->ts_video_pids[i], stream->ts_video_pids[i],
- get_stream_name(stream->ts_video_stream_type[i]),
- stream->ts_video_stream_type[i]);
+ hb_log( " 0x%x type %s (0x%x)",
+ stream->ts_video_pids[i],
+ stream_type_name(stream->ts_stream_type[i]),
+ stream->ts_stream_type[i]);
}
hb_log(" Audio PIDS : ");
for (i = 0; i < stream->ts_number_audio_pids; i++)
{
- hb_log(" 0x%x (%d) [Type %s (0x%x)]",
- stream->ts_audio_pids[i], stream->ts_audio_pids[i],
- get_stream_name(stream->ts_audio_stream_type[i]),
- stream->ts_audio_stream_type[i] );
+ hb_log( " 0x%x type %s (0x%x)",
+ stream->ts_audio_pids[i],
+ stream_type_name(stream->ts_stream_type[i+1]),
+ stream->ts_stream_type[i+1] );
}
}
@@ -1582,7 +1767,7 @@ static void generate_output_data(hb_stream_t *stream, int curstream)
// 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)
+ if ( stream->ts_stream_type[curstream] == 0x81)
{
len += 4;
}
@@ -1623,7 +1808,7 @@ static void generate_output_data(hb_stream_t *stream, int curstream)
tdat[3] = stream->ts_streamid[curstream];
uint16_t plen = stream->ts_pos[curstream] - 6;
- if ( stream->ts_audio_stream_type[curstream] == 0x81)
+ if ( stream->ts_stream_type[curstream] == 0x81)
{
// We have to add an AC3 header in front of the data. Add its
// size to the PES packet length.
@@ -1659,7 +1844,7 @@ static void generate_output_data(hb_stream_t *stream, int curstream)
{
// 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)
+ if ( stream->ts_stream_type[curstream] != 0x81)
{
make_pes_header(stream, stream->ts_pos[curstream],
stream->ts_streamid[curstream]);
@@ -1691,53 +1876,6 @@ static void generate_output_data(hb_stream_t *stream, int curstream)
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
@@ -1778,10 +1916,6 @@ static int isIframe( const uint8_t *buf, int adapt_len )
**********************************************************************/
static int hb_ts_stream_decode( hb_stream_t *stream, uint8_t *obuf )
{
- int64_t pcr = stream->ts_lastpcr;
- int curstream;
- uint8_t buf[188];
-
/*
* stash the output buffer pointer in our stream so we don't have to
* pass it & its original value to everything we call.
@@ -1792,8 +1926,12 @@ static int hb_ts_stream_decode( hb_stream_t *stream, uint8_t *obuf )
// spin until we get a packet of data from some stream or hit eof
while ( 1 )
{
- if ((fread(buf, 188, 1, stream->file_handle)) != 1)
- {
+ int64_t pcr = stream->ts_lastpcr;
+ int curstream;
+
+ const uint8_t *buf = next_packet(stream);
+ if ( buf == NULL )
+ {
// 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");
@@ -1802,24 +1940,6 @@ static int hb_ts_stream_decode( hb_stream_t *stream, uint8_t *obuf )
/* This next section validates the packet */
- // Check sync byte
- if ((buf[0] != 0x47) && (buf[0] != 0x72) && (buf[0] != 0x29))
- {
- // lost sync - back up to where we started then try to
- // re-establish sync.
- 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_decode: eof while re-establishing sync @ %lld",
- pos );
- return 0;
- }
- ts_warn( stream, "hb_ts_stream_decode: sync lost @%lld, "
- "regained after %lld bytes", pos, pos2 );
- continue;
- }
-
// 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 )
@@ -1927,7 +2047,8 @@ static int hb_ts_stream_decode( hb_stream_t *stream, uint8_t *obuf )
{
if ( !stream->ts_foundfirst[0] )
{
- if ( !isIframe( buf, adapt_len ) )
+ if ( stream->ts_stream_type[0] == 2 &&
+ !isIframe( buf, adapt_len ) )
{
// didn't find an I frame
continue;
@@ -2008,6 +2129,6 @@ static void hb_ts_stream_reset(hb_stream_t *stream)
stream->last_error_frame = -10000;
stream->last_error_count = 0;
- align_to_next_packet(stream->file_handle);
+ align_to_next_packet(stream);
}