diff options
author | jstebbins <[email protected]> | 2011-09-14 16:03:52 +0000 |
---|---|---|
committer | jstebbins <[email protected]> | 2011-09-14 16:03:52 +0000 |
commit | 3cdb1f293da7bb6e1f506c899ced038068aeeddf (patch) | |
tree | 53552ce38e0c8f23675cbf71f4dc68cc302315b7 /libhb/stream.c | |
parent | ace57e1b754f84cad9603e7ed14aa9c6bb705461 (diff) |
Improve mpeg PS support
Adds support for MPEG-1 PS, HDDVD EVOB, and video codecs other
than mpeg1/2 in PS
Improves probing of unknown stream types by using Libav's probing
utilities
Use Libav to probe for dts profile in TS and PS files when profile is
unknown
Improves framerate detection (improved telecine detection)
Fixes preview generation for mpeg video that has only a single sequence
header
Patches Libav to handle VC-1 pulldown flags properly
Improve PS and TS stream log information
git-svn-id: svn://svn.handbrake.fr/HandBrake/trunk@4220 b64f7644-9d1e-0410-96f1-a4d463321fa5
Diffstat (limited to 'libhb/stream.c')
-rw-r--r-- | libhb/stream.c | 3749 |
1 files changed, 2664 insertions, 1085 deletions
diff --git a/libhb/stream.c b/libhb/stream.c index 517e29ee4..7b3a422c6 100644 --- a/libhb/stream.c +++ b/libhb/stream.c @@ -16,6 +16,7 @@ #include "libbluray/bluray.h" #define min(a, b) a < b ? a : b +#define HB_MAX_PROBE_SIZE (1*1024*1024) /* * This table defines how ISO MPEG stream type codes map to HandBrake @@ -43,12 +44,13 @@ typedef struct { [id] = { kind, codec, codec_param, name } static const stream2codec_t st2codec[256] = { + st(0x00, U, 0, 0, NULL), st(0x01, V, WORK_DECMPEG2, 0, "MPEG1"), st(0x02, V, WORK_DECMPEG2, 0, "MPEG2"), st(0x03, A, HB_ACODEC_FFMPEG, CODEC_ID_MP2, "MPEG1"), st(0x04, A, HB_ACODEC_FFMPEG, CODEC_ID_MP2, "MPEG2"), st(0x05, N, 0, 0, "ISO 13818-1 private section"), - st(0x06, N, 0, 0, "ISO 13818-1 PES private data"), + st(0x06, U, 0, 0, "ISO 13818-1 PES private data"), st(0x07, N, 0, 0, "ISO 13522 MHEG"), st(0x08, N, 0, 0, "ISO 13818-1 DSM-CC"), st(0x09, N, 0, 0, "ISO 13818-1 auxiliary"), @@ -66,22 +68,31 @@ static const stream2codec_t st2codec[256] = { st(0x1b, V, WORK_DECAVCODECV, CODEC_ID_H264, "H.264"), - st(0x80, N, HB_ACODEC_FFMPEG, CODEC_ID_PCM_BLURAY, "Digicipher II Video"), + st(0x80, U, HB_ACODEC_FFMPEG, CODEC_ID_PCM_BLURAY, "Digicipher II Video"), st(0x81, A, HB_ACODEC_AC3, 0, "AC3"), st(0x82, A, HB_ACODEC_DCA, 0, "DTS"), - st(0x83, A, HB_ACODEC_LPCM, 0, "LPCM"), + // 0x83 can be LPCM or BD TrueHD. Set to 'unknown' till we know more. + st(0x83, U, HB_ACODEC_LPCM, 0, "LPCM"), + // BD E-AC3 Primary audio st(0x84, U, 0, 0, "SDDS"), st(0x85, U, 0, 0, "ATSC Program ID"), - st(0x86, A, HB_ACODEC_DCA, 0, "DTS-HD MA"), + // 0x86 can be BD DTS-HD/DTS. Set to 'unknown' till we know more. + st(0x86, U, HB_ACODEC_DCA_HD, CODEC_ID_DTS, "DTS-HD MA"), st(0x87, A, HB_ACODEC_FFMPEG, CODEC_ID_EAC3, "E-AC3"), st(0x8a, A, HB_ACODEC_DCA, 0, "DTS"), - st(0x91, A, HB_ACODEC_AC3, 0, "AC3"), + st(0x90, N, 0, 0, "PGS Subtitle"), + // 0x91 can be AC3 or BD Interactive Graphics Stream. + st(0x91, U, 0, 0, "AC3/IGS"), st(0x92, N, 0, 0, "Subtitle"), st(0x94, U, 0, 0, "SDDS"), st(0xa0, V, 0, 0, "MSCODEC"), + // BD E-AC3 Secondary audio + st(0xa1, U, 0, 0, "E-AC3"), + // BD DTS-HD Secondary audio + st(0xa2, U, 0, 0, "DTS-HD MA"), st(0xea, V, WORK_DECAVCODECV, CODEC_ID_VC1, "VC-1"), }; @@ -91,12 +102,10 @@ typedef enum { hb_stream_type_unknown = 0, transport, program, - dvd_program, ffmpeg } hb_stream_type_t; -#define kMaxNumberDecodeStreams 32 -#define kMaxNumberDecodeSubStreams 2 +#define MAX_PS_PROBE_SIZE (5*1024*1024) #define kMaxNumberPMTStreams 32 typedef struct { @@ -105,22 +114,30 @@ typedef struct { int8_t skipbad; int8_t continuity; uint8_t pkt_summary[8]; - int16_t pid; + int pid; + uint8_t is_pcr; + int pes_list; +} hb_ts_stream_t; + +typedef struct { + int map_idx; + int stream_id; + uint8_t stream_id_ext; + uint8_t stream_type; + kind_t stream_kind; + int lang_code; uint32_t format_id; #define TS_FORMAT_ID_AC3 (('A' << 24) | ('C' << 16) | ('-' << 8) | '3') - uint8_t stream_type; - kind_t stream_kind; - int number_substreams; - uint8_t substream_type[kMaxNumberDecodeSubStreams]; - uint8_t current_substream; - struct { - int lang_code; - int flags; - int rate; - int bitrate; - } a52_info; -} hb_ts_stream_t; + int codec; // HB worker object id of codec + int codec_param; // param for codec (usually ffmpeg codec id) + char codec_name[80]; + int next; // next pointer for list + // hb_ts_stream_t points to a list of + // hb_pes_stream_t + hb_buffer_t *probe_buf; + int probe_next_size; +} hb_pes_stream_t; struct hb_stream_s { @@ -131,20 +148,35 @@ struct hb_stream_s int last_error_count; /* # errors at last error message */ int packetsize; /* Transport Stream packet size */ - uint8_t need_keyframe; // non-zero if want to start at a keyframe + int need_keyframe; // non-zero if want to start at a keyframe hb_buffer_t *fwrite_buf; /* PS buffer (set by hb_ts_stream_decode) */ int chapter; /* Chapter that we are currently in */ int64_t chapter_end; /* HB time that the current chapter ends */ - uint8_t ts_found_pcr; // non-zero if we've found at least one input pcr - int ts_pcr_out; // sequence number of most recent output pcr - int ts_pcr_in; // sequence number of most recent input pcr - int64_t ts_pcr; // most recent input pcr - int64_t ts_pcr_current; // circular buffer of output pcrs - uint8_t *ts_packet; /* buffer for one TS packet */ - hb_ts_stream_t ts[kMaxNumberDecodeStreams]; + struct + { + 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 + int64_t pcr; // most recent input pcr + int64_t pcr_current; // circular buffer of output pcrs + + uint8_t *packet; // buffer for one TS packet + hb_ts_stream_t *list; + int count; + int alloc; + } ts; + + struct + { + uint8_t found_scr; // non-zero if we've found at least one scr + int64_t scr; // most recent input scr + hb_pes_stream_t *list; + int count; + int alloc; + } pes; /* * Stuff before this point is dynamic state updated as we read the @@ -152,12 +184,11 @@ struct hb_stream_s * we learn during the initial scan but cache so it can be * reused during the conversion read. */ - uint8_t ts_number_pids; + uint8_t has_IDRs; // # IDRs found during duration scan uint8_t ts_flags; // stream characteristics: #define TS_HAS_PCR (1 << 0) // at least one PCR seen #define TS_HAS_RAP (1 << 1) // Random Access Point bit seen #define TS_HAS_RSEI (1 << 2) // "Restart point" SEI seen - uint8_t ts_IDRs; // # IDRs found during duration scan int recovery_frames; @@ -171,6 +202,9 @@ struct hb_stream_s AVPacket *ffmpeg_pkt; uint8_t ffmpeg_video_id; + uint32_t reg_desc; // 4 byte registration code that identifies + // stream semantics + struct { unsigned short program_number; @@ -185,18 +219,7 @@ struct hb_stream_s unsigned int tablepos; unsigned char current_continuity_counter; - int section_length; - int program_number; unsigned int PCR_PID; - uint32_t reg_desc; - int program_info_length; - struct - { - unsigned char stream_type; - unsigned short elementary_PID; - unsigned short ES_info_length; - unsigned char *es_info_descriptor_data; - } pmt_stream_info[kMaxNumberPMTStreams]; } pmt_info; }; @@ -207,16 +230,39 @@ 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 **********************************************************************/ static void hb_stream_duration(hb_stream_t *stream, hb_title_t *inTitle); +static off_t align_to_next_packet(hb_stream_t *stream); +static int64_t pes_timestamp( const uint8_t *pes ); + static void hb_ts_stream_init(hb_stream_t *stream); -static void hb_ts_stream_find_pids(hb_stream_t *stream); static hb_buffer_t * hb_ts_stream_decode(hb_stream_t *stream); -static void hb_ts_stream_set_audio_list(hb_list_t *list_audio, hb_stream_t *stream); -static void hb_ps_stream_find_audio_ids(hb_stream_t *stream, hb_title_t *title); -static off_t align_to_next_packet(hb_stream_t *stream); +static void hb_init_audio_list(hb_stream_t *stream, hb_title_t *title); +static void hb_ts_stream_find_pids(hb_stream_t *stream); + +static void hb_ps_stream_init(hb_stream_t *stream); +static hb_buffer_t * hb_ps_stream_decode(hb_stream_t *stream); +static void hb_ps_stream_find_streams(hb_stream_t *stream); +static int hb_ps_read_packet( hb_stream_t * stream, hb_buffer_t *b ); +static int update_ps_streams( hb_stream_t * stream, int stream_id, int stream_id_ext, int stream_type, int in_kind ); +static int update_ts_streams( hb_stream_t * stream, int pid, int stream_id_ext, int stream_type, int in_kind, int *pes_idx ); +static void update_pes_kind( hb_stream_t * stream, int idx ); static int ffmpeg_open( hb_stream_t *stream, hb_title_t *title, int scan ); static void ffmpeg_close( hb_stream_t *d ); @@ -224,10 +270,16 @@ static hb_title_t *ffmpeg_title_scan( hb_stream_t *stream, hb_title_t *title ); hb_buffer_t *hb_ffmpeg_read( hb_stream_t *stream ); static int ffmpeg_seek( hb_stream_t *stream, float frac ); static int ffmpeg_seek_ts( hb_stream_t *stream, int64_t ts ); -static inline unsigned int get_bits(bitbuf_t *bb, int bits); -static inline void set_buf(bitbuf_t *bb, uint8_t* buf, int bufsize, int clear); -static inline int buf_eob(bitbuf_t *bb); -static inline int read_ue(bitbuf_t *bb ); +static inline unsigned int bits_get(bitbuf_t *bb, int bits); +static inline void bits_init(bitbuf_t *bb, uint8_t* buf, int bufsize, int clear); +static inline int bits_eob(bitbuf_t *bb); +static inline int bits_read_ue(bitbuf_t *bb ); +static void pes_add_audio_to_title(hb_stream_t *s, int i, hb_title_t *t, int sort); +static int hb_parse_ps( hb_stream_t *stream, uint8_t *buf, int len, hb_pes_info_t *pes_info ); +static void hb_ts_resolve_pid_types(hb_stream_t *stream); +static void hb_ps_resolve_stream_types(hb_stream_t *stream); +void hb_ts_stream_reset(hb_stream_t *stream); +void hb_ps_stream_reset(hb_stream_t *stream); /* * streams have a bunch of state that's learned during the scan. We don't @@ -308,18 +360,31 @@ static void ts_warn( hb_stream_t *stream, char *log, ... ) va_end( args ); } -static kind_t ts_stream_kind( hb_stream_t *stream, int curstream ) +static int get_id(hb_pes_stream_t *pes) +{ + return ( pes->stream_id_ext << 16 ) + pes->stream_id; +} + +static int index_of_id(hb_stream_t *stream, int id) { - return st2codec[stream->ts[curstream].stream_type].kind; + int i; + + for ( i = 0; i < stream->pes.count; ++i ) + { + if ( id == get_id( &stream->pes.list[i] ) ) + return i; + } + + return -1; } static int index_of_pid(hb_stream_t *stream, int pid) { int i; - for ( i = 0; i < stream->ts_number_pids; ++i ) + for ( i = 0; i < stream->ts.count; ++i ) { - if ( pid == stream->ts[i].pid ) + if ( pid == stream->ts.list[i].pid ) { return i; } @@ -328,25 +393,77 @@ static int index_of_pid(hb_stream_t *stream, int pid) return -1; } -static int index_of_substream(hb_stream_t *stream, int pid, int substream_type) +static int index_of_ps_stream(hb_stream_t *stream, int id, int sid) { - int ii; - int idx = index_of_pid( stream, pid ); + int i; - for ( ii = 0; ii < stream->ts[idx].number_substreams; ii++ ) + for ( i = 0; i < stream->pes.count; ++i ) { - if ( stream->ts[idx].substream_type[ii] == substream_type ) - return ii; + if ( id == stream->pes.list[i].stream_id && + sid == stream->pes.list[i].stream_id_ext ) + { + return i; + } + } + // 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 ) + { + if ( id == stream->pes.list[i].stream_id && + 0 == stream->pes.list[i].stream_id_ext ) + { + return i; + } } + return -1; } -static int index_of_video(hb_stream_t *stream) +static kind_t ts_stream_kind( hb_stream_t * stream, int idx ) +{ + if ( stream->ts.list[idx].pes_list != -1 ) + { + // Retuns kind for the first pes substream in the pes list + // All substreams in a TS stream are the same kind. + return stream->pes.list[stream->ts.list[idx].pes_list].stream_kind; + } + else + { + return U; + } +} + +static kind_t ts_stream_type( hb_stream_t * stream, int idx ) +{ + if ( stream->ts.list[idx].pes_list != -1 ) + { + // Retuns stream type for the first pes substream in the pes list + // All substreams in a TS stream are the same stream type. + return stream->pes.list[stream->ts.list[idx].pes_list].stream_type; + } + else + { + return 0x00; + } +} + +static int pes_index_of_video(hb_stream_t *stream) { int i; - for ( i = 0; i < stream->ts_number_pids; ++i ) - if ( V == stream->ts[i].stream_kind ) + for ( i = 0; i < stream->pes.count; ++i ) + if ( V == stream->pes.list[i].stream_kind ) + return i; + + return -1; +} + +static int ts_index_of_video(hb_stream_t *stream) +{ + int i; + + for ( i = 0; i < stream->ts.count; ++i ) + if ( V == ts_stream_kind( stream, i ) ) return i; return -1; @@ -359,8 +476,8 @@ static void ts_err( hb_stream_t *stream, int curstream, char *log, ... ) ts_warn_helper( stream, log, args ); va_end( args ); - stream->ts[curstream].skipbad = 1; - stream->ts[curstream].continuity = -1; + stream->ts.list[curstream].skipbad = 1; + stream->ts.list[curstream].continuity = -1; } static int check_ps_sync(const uint8_t *buf) @@ -376,22 +493,36 @@ static int check_ps_sc(const uint8_t *buf) // a legal MPEG program stream must start with a Pack followed by a // some other start code. If we've already verified the pack, this skip // it and checks for a start code prefix. - int pos = 14 + ( buf[13] & 0x7 ); // skip over the PACK + int pos; + int mark = buf[4] >> 4; + if ( mark == 0x02 ) + { + // mpeg-1 pack header + pos = 12; // skip over the PACK + } + else + { + // mpeg-2 pack header + pos = 14 + ( buf[13] & 0x7 ); // skip over the PACK + } return (buf[pos+0] == 0x00) && (buf[pos+1] == 0x00) && (buf[pos+2] == 0x01); } 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); + // must have initial sync byte & a legal adaptation ctrl + return (buf[0] == 0x47) && (((buf[3] & 0x30) >> 4) > 0); } -static int have_ts_sync(const uint8_t *buf, int psize) +static int have_ts_sync(const uint8_t *buf, int psize, int count) { - 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]); + int ii; + for ( ii = 0; ii < count; ii++ ) + { + if ( !check_ts_sync(&buf[ii*psize]) ) + return 0; + } + return 1; } static int hb_stream_check_for_ts(const uint8_t *buf) @@ -400,16 +531,17 @@ static int hb_stream_check_for_ts(const uint8_t *buf) // search the first 8KB of buf looking for at least 8 consecutive // correctly located sync patterns. int offset = 0; + int count = 16; - for ( offset = 0; offset < 8*1024-8*188; ++offset ) + for ( offset = 0; offset < 8*1024-count*188; ++offset ) { - if ( have_ts_sync( &buf[offset], 188) ) + if ( have_ts_sync( &buf[offset], 188, count) ) return 188 | (offset << 8); - if ( have_ts_sync( &buf[offset], 192) ) + if ( have_ts_sync( &buf[offset], 192, count) ) return 192 | (offset << 8); - if ( have_ts_sync( &buf[offset], 204) ) + if ( have_ts_sync( &buf[offset], 204, count) ) return 204 | (offset << 8); - if ( have_ts_sync( &buf[offset], 208) ) + if ( have_ts_sync( &buf[offset], 208, count) ) return 208 | (offset << 8); } return 0; @@ -423,8 +555,8 @@ static int hb_stream_check_for_ps(hb_stream_t *stream) fseek(stream->file_handle, 0, SEEK_SET); - // program streams should start with a PACK then some other mpeg start - // code (usually a SYS but that might be missing if we only have a clip). + // program streams should start with a PACK then some other mpeg start + // code (usually a SYS but that might be missing if we only have a clip). while (pos < 512 * 1024) { int offset; @@ -441,7 +573,18 @@ static int hb_stream_check_for_ps(hb_stream_t *stream) uint8_t *b = buf+offset; // Skip the pack header - pes_offset = 14 + (b[13] & 0x7); + int mark = buf[4] >> 4; + if ( mark == 0x02 ) + { + // mpeg-1 pack header + pes_offset = 12; + } + else + { + // mpeg-2 pack header + pes_offset = 14 + ( buf[13] & 0x7 ); + } + b += pes_offset; // Get the next stream id sid = b[3]; @@ -454,7 +597,7 @@ static int hb_stream_check_for_ps(hb_stream_t *stream) fseek( stream->file_handle, pos, SEEK_SET ); if ( fread(sc_buf, 1, 4, stream->file_handle) != 4 ) return 0; - if (sc_buf[0] == 0x00 && sc_buf[1] == 0x00 && + if (sc_buf[0] == 0x00 && sc_buf[1] == 0x00 && sc_buf[2] == 0x01) { return 1; @@ -469,16 +612,6 @@ static int hb_stream_check_for_ps(hb_stream_t *stream) return 0; } -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_sc(&buf[0*2048]) && - check_ps_sync(&buf[1*2048]) && check_ps_sc(&buf[1*2048]) && - check_ps_sync(&buf[2*2048]) && check_ps_sc(&buf[2*2048]) && - check_ps_sync(&buf[3*2048]) && check_ps_sc(&buf[3*2048]); -} - static int hb_stream_get_type(hb_stream_t *stream) { uint8_t buf[2048*4]; @@ -495,22 +628,15 @@ static int hb_stream_get_type(hb_stream_t *stream) stream->packetsize = psize; stream->hb_stream_type = transport; hb_ts_stream_init(stream); - if ( index_of_video( stream ) < 0 ) - { - return 0; - } - 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(stream) != 0 ) { hb_log("file is MPEG Program Stream"); stream->hb_stream_type = program; + hb_ps_stream_init(stream); + // We default to mpeg codec for ps streams if no + // video found in program stream map return 1; } } @@ -527,19 +653,22 @@ static void hb_stream_delete_dynamic( hb_stream_t *d ) int i=0; - if ( d->ts_packet ) + if ( d->ts.packet ) { - free( d->ts_packet ); - d->ts_packet = NULL; + free( d->ts.packet ); + d->ts.packet = NULL; } - for (i = 0; i < kMaxNumberDecodeStreams; i++) + if ( d->ts.list ) { - if (d->ts[i].buf) + for (i = 0; i < d->ts.count; i++) { - hb_buffer_close(&(d->ts[i].buf)); - hb_buffer_close(&(d->ts[i].extra_buf)); - d->ts[i].buf = NULL; - d->ts[i].extra_buf = NULL; + 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; + } } } } @@ -547,79 +676,76 @@ static void hb_stream_delete_dynamic( hb_stream_t *d ) static void hb_stream_delete( hb_stream_t *d ) { hb_stream_delete_dynamic( d ); + free( d->ts.list ); + free( d->pes.list ); free( d->path ); free( d ); } -static int find_substream( hb_ts_stream_t *ts, int substream ) +static int audio_inactive( hb_stream_t *stream, int id, int stream_id_ext ) { - int i, all_match = -1; - - if ( ts->number_substreams == 0 ) + if ( id < 0 ) + { + // PID declared inactive by hb_stream_title_scan + return 1; + } + if ( id == stream->pmt_info.PCR_PID ) + { + // PCR PID is always active return 0; + } - for ( i = 0; i < ts->number_substreams; i++ ) + int i; + for ( i = 0; i < hb_list_count( stream->title->list_audio ); ++i ) { - if ( ts->substream_type[i] == 0 ) - all_match = 0; - if ( ts->substream_type[i] == substream ) + hb_audio_t *audio = hb_list_item( stream->title->list_audio, i ); + if ( audio->id == ((stream_id_ext << 16) | id) ) { - return ts->substream_type[i]; + return 0; } } - return all_match; + return 1; } -static void remove_substream( hb_ts_stream_t *ts, int idx ) +/* when the file was first opened we made entries for all the audio elementary + * streams we found in it. Streams that were later found during the preview scan + * now have an audio codec, type, rate, etc., associated with them. At the end + * of the scan we delete all the audio entries that weren't found by the scan + * or don't have a format we support. This routine deletes audio entry 'indx' + * by setting its PID to an invalid value so no packet will match it. (We can't + * move any of the entries since the index of the entry is used as the id + * of the media stream for HB. */ +static void hb_stream_delete_ts_entry(hb_stream_t *stream, int indx) { - int i; - - for ( i = idx; i < ts->number_substreams-1; i++ ) + if ( stream->ts.list[indx].pid > 0 ) { - ts->substream_type[i] = ts->substream_type[i+1]; + stream->ts.list[indx].pid = -stream->ts.list[indx].pid; } - ts->number_substreams--; } -static int audio_inactive( hb_stream_t *stream, int idx ) +static int hb_stream_try_delete_ts_entry(hb_stream_t *stream, int indx) { - int pid = stream->ts[idx].pid; + int ii; - if ( pid < 0 ) - { - // PID declared inactive by hb_stream_title_scan + if ( stream->ts.list[indx].pid < 0 ) return 1; - } - if ( pid == stream->pmt_info.PCR_PID ) + + for ( ii = stream->ts.list[indx].pes_list; ii != -1; + ii = stream->pes.list[ii].next ) { - // PCR PID is always active - return 0; + if ( stream->pes.list[ii].stream_id >= 0 ) + return 0; } + stream->ts.list[indx].pid = -stream->ts.list[indx].pid; + return 1; +} - // Must go backwards thru the list of substreams because we may - // remove one which would shift the list down. - int j; - for ( j = stream->ts[idx].number_substreams - 1; j >= 0; j-- ) +static void hb_stream_delete_ps_entry(hb_stream_t *stream, int indx) +{ + if ( stream->pes.list[indx].stream_id > 0 ) { - int substream = stream->ts[idx].substream_type[j]; - - // see if we should make the stream inactive because scan.c didn't - // find a valid audio bitstream. - int i; - for ( i = 0; i < hb_list_count( stream->title->list_audio ); ++i ) - { - hb_audio_t *audio = hb_list_item( stream->title->list_audio, i ); - if ( audio->id == ((substream << 16) | pid) ) - { - return 0; - } - } - remove_substream( &stream->ts[idx], j ); + stream->pes.list[indx].stream_id = -stream->pes.list[indx].stream_id; } - - // not in the title's audio list - declare the PID inactive - stream->ts[idx].pid = -stream->ts[idx].pid; - return 1; } /*********************************************************************** @@ -665,23 +791,69 @@ hb_stream_t * hb_stream_open( char *path, hb_title_t *title, int scan ) if ( d->hb_stream_type == transport ) { - d->ts_packet = malloc( d->packetsize ); + d->ts.packet = malloc( d->packetsize ); - int i; - for ( i = 0; i < d->ts_number_pids; i++) + int ii, jj; + for ( ii = 0; ii < d->ts.count; ii++) { - if ( d->ts[i].stream_kind == A && - audio_inactive( d, i ) ) + // If probing didn't find audio or video, and the pid + // is not the PCR, remove the track + if ( ts_stream_kind ( d, ii ) == U && + !d->ts.list[ii].is_pcr ) + { + hb_stream_delete_ts_entry(d, ii); + continue; + } + + if ( ts_stream_kind ( d, ii ) == A ) + { + for ( jj = d->ts.list[ii].pes_list; jj != -1; + jj = d->pes.list[jj].next ) + { + if ( audio_inactive( d, d->pes.list[jj].stream_id, + d->pes.list[jj].stream_id_ext ) ) + { + hb_stream_delete_ps_entry(d, jj); + } + } + if ( !d->ts.list[ii].is_pcr && + hb_stream_try_delete_ts_entry(d, ii) ) + { + continue; + } + } + 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; + } + // reset to beginning of file and reset some stream + // state information + hb_stream_seek( d, 0. ); + } + else if ( d->hb_stream_type == program ) + { + int ii; + for ( ii = 0; ii < d->pes.count; ii++) + { + // If probing didn't find audio or video, remove the track + if ( d->pes.list[ii].stream_kind == U ) + { + hb_stream_delete_ps_entry(d, ii); + } + + if ( d->pes.list[ii].stream_kind == A && + audio_inactive( d, d->pes.list[ii].stream_id, + d->pes.list[ii].stream_id_ext ) ) { // this PID isn't wanted (we don't have a codec for it // or scan didn't find audio parameters) + hb_stream_delete_ps_entry(d, ii); continue; } - d->ts[i].buf = hb_buffer_init(d->packetsize); - d->ts[i].extra_buf = hb_buffer_init(d->packetsize); - d->ts[i].buf->size = 0; - d->ts[i].extra_buf->size = 0; } + // reset to beginning of file and reset some stream + // state information hb_stream_seek( d, 0. ); } return d; @@ -726,6 +898,55 @@ hb_stream_t * hb_stream_open( char *path, hb_title_t *title, int scan ) return NULL; } +static int new_pid( hb_stream_t * stream ) +{ + int num = stream->ts.alloc; + + if ( stream->ts.count == stream->ts.alloc ) + { + num = stream->ts.alloc ? stream->ts.alloc * 2 : 32; + stream->ts.list = realloc( stream->ts.list, + sizeof( hb_ts_stream_t ) * num ); + } + int ii; + for ( ii = stream->ts.alloc; ii < num; ii++ ) + { + memset(&stream->ts.list[ii], 0, sizeof( hb_ts_stream_t )); + stream->ts.list[ii].continuity = -1; + stream->ts.list[ii].pid = -1; + stream->ts.list[ii].pes_list = -1; + } + stream->ts.alloc = num; + num = stream->ts.count; + stream->ts.count++; + + return num; +} + +static int new_pes( hb_stream_t * stream ) +{ + int num = stream->pes.alloc; + + if ( stream->pes.count == stream->pes.alloc ) + { + num = stream->pes.alloc ? stream->pes.alloc * 2 : 32; + stream->pes.list = realloc( stream->pes.list, + sizeof( hb_pes_stream_t ) * num ); + } + int ii; + for ( ii = stream->pes.alloc; ii < num; ii++ ) + { + memset(&stream->pes.list[ii], 0, sizeof( hb_pes_stream_t )); + stream->pes.list[ii].stream_id = -1; + stream->pes.list[ii].next = -1; + } + stream->pes.alloc = num; + num = stream->pes.count; + stream->pes.count++; + + return num; +} + hb_stream_t * hb_bd_stream_open( hb_title_t *title ) { int ii; @@ -737,86 +958,45 @@ hb_stream_t * hb_bd_stream_open( hb_title_t *title ) return NULL; } - for (ii = 0; ii < kMaxNumberDecodeStreams; ii++) - { - d->ts[ii].continuity = -1; - d->ts[ii].pid = -1; - } - d->file_handle = NULL; d->title = title; d->path = NULL; - d->ts_packet = NULL; + d->ts.packet = NULL; - d->ts_number_pids = 0; - d->ts[0].pid = title->video_id; - d->ts[0].stream_type = title->video_stream_type; - d->ts[0].stream_kind = V; - d->ts[0].substream_type[0] = 0; - d->ts[0].number_substreams = 1; - d->ts_number_pids++; + int pid = title->video_id; + int stream_type = title->video_stream_type; + update_ts_streams( d, pid, 0, stream_type, V, NULL ); hb_audio_t * audio; for ( ii = 0; ( audio = hb_list_item( title->list_audio, ii ) ); ++ii ) { - int pid = audio->id & 0xFFFF; - int idx = index_of_pid( d, pid ); - int substream_type = audio->config.in.substream_type; + int stream_id_ext = audio->config.in.substream_type; + pid = audio->id & 0xFFFF; + stream_type = audio->config.in.stream_type; - if ( idx < 0 ) - { - // New pid - d->ts[d->ts_number_pids].pid = audio->id & 0xFFFF; - d->ts[d->ts_number_pids].stream_type = audio->config.in.stream_type; - d->ts[d->ts_number_pids].substream_type[0] = substream_type; - d->ts[d->ts_number_pids].number_substreams = 1; - d->ts[d->ts_number_pids].stream_kind = A; - d->ts_number_pids++; - } - // Only add substream if it has not already been added. - else if ( index_of_substream( d, pid, substream_type ) < 0 ) - { - if ( d->ts[idx].number_substreams < kMaxNumberDecodeSubStreams ) - { - d->ts[idx].substream_type[d->ts[idx].number_substreams] = - substream_type; - d->ts[idx].number_substreams++; - d->ts[idx].stream_kind = A; - } - else - { - hb_error( "hb_bd_stream_open: Too many substreams. Dropping audio 0x%x.", audio->id ); - } - } + update_ts_streams( d, pid, stream_id_ext, stream_type, A, NULL ); } - d->ts_flags = TS_HAS_RAP; + d->ts_flags = TS_HAS_RAP | TS_HAS_PCR; // When scanning, title->job == NULL. We don't need to wait for // a PCR when scanning. In fact, it trips us up on the first // preview of every title since we would have to read quite a // lot of data before finding the PCR. - if (title->pcr_pid != 0xFFFF && title->job) + if ( title->job ) { - if ( index_of_pid( d, title->pcr_pid ) < 0 ) - { - // BD PCR PID is specified to always be 0x1001 - d->ts[d->ts_number_pids].pid = 0x1001; - d->ts[d->ts_number_pids].stream_kind = P; - d->ts[d->ts_number_pids].number_substreams = 1; - d->ts[d->ts_number_pids].substream_type[0] = 0; - d->ts_number_pids++; - } + // BD PCR PID is specified to always be 0x1001 + update_ts_streams( d, 0x1001, 0, -1, P, NULL ); } d->packetsize = 192; d->hb_stream_type = transport; - for ( ii = 0; ii < d->ts_number_pids; ii++ ) + for ( ii = 0; ii < d->ts.count; ii++ ) { - d->ts[ii].buf = hb_buffer_init(d->packetsize); - d->ts[ii].extra_buf = hb_buffer_init(d->packetsize); - d->ts[ii].buf->size = 0; - d->ts[ii].extra_buf->size = 0; + 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; @@ -866,22 +1046,6 @@ void hb_stream_close( hb_stream_t ** _d ) *_d = NULL; } -/* when the file was first opened we made entries for all the audio elementary - * streams we found in it. Streams that were later found during the preview scan - * now have an audio codec, type, rate, etc., associated with them. At the end - * of the scan we delete all the audio entries that weren't found by the scan - * or don't have a format we support. This routine deletes audio entry 'indx' - * by setting its PID to an invalid value so no packet will match it. (We can't - * move any of the entries since the index of the entry is used as the id - * of the media stream for HB. */ -static void hb_stream_delete_entry(hb_stream_t *stream, int indx) -{ - if ( stream->ts[indx].pid > 0 ) - { - stream->ts[indx].pid = -stream->ts[indx].pid; - } -} - /*********************************************************************** * hb_ps_stream_title_scan *********************************************************************** @@ -893,32 +1057,17 @@ hb_title_t * hb_stream_title_scan(hb_stream_t *stream, hb_title_t * title) return ffmpeg_title_scan( stream, title ); // 'Barebones Title' - hb_title_t *aTitle = title; - aTitle->type = HB_STREAM_TYPE; - aTitle->index = 1; + title->type = HB_STREAM_TYPE; + title->index = 1; // Copy part of the stream path to the title name char *sep = strrchr(stream->path, '/'); if (sep) - strcpy(aTitle->name, sep+1); - char *dot_term = strrchr(aTitle->name, '.'); + strcpy(title->name, sep+1); + char *dot_term = strrchr(title->name, '.'); if (dot_term) *dot_term = '\0'; - // Height, width, rate and aspect ratio information is filled in when the previews are built - - hb_stream_duration(stream, aTitle); - - // One Chapter - hb_chapter_t * chapter; - chapter = calloc( sizeof( hb_chapter_t ), 1 ); - chapter->index = 1; - chapter->duration = aTitle->duration; - chapter->hours = aTitle->hours; - chapter->minutes = aTitle->minutes; - chapter->seconds = aTitle->seconds; - hb_list_add( aTitle->list_chapter, chapter ); - // Figure out how many audio streams we really have: // - For transport streams, for each PID listed in the PMT (whether // or not it was an audio stream type) read the bitstream until we @@ -926,64 +1075,56 @@ hb_title_t * hb_stream_title_scan(hb_stream_t *stream, hb_title_t * title) // the elementary stream is an audio type. // - For program streams read the first 4MB and take every unique // audio stream we find. - if (stream->hb_stream_type == transport) - { - int i; + hb_init_audio_list(stream, title); - hb_ts_stream_set_audio_list(aTitle->list_audio, stream); + // set the video id, codec & muxer + int idx = pes_index_of_video( stream ); + if ( idx < 0 ) + { + hb_title_close( &title ); + return NULL; + } - // make sure we're grabbing the PCR PID - if ( index_of_pid( stream, stream->pmt_info.PCR_PID ) < 0 ) - { - stream->ts[stream->ts_number_pids].pid = stream->pmt_info.PCR_PID; - stream->ts[stream->ts_number_pids].stream_kind = P; - stream->ts[stream->ts_number_pids].number_substreams = 1; - stream->ts[stream->ts_number_pids].substream_type[0] = 0; - stream->ts_number_pids++; - } + title->video_id = get_id( &stream->pes.list[idx] ); + title->video_codec = stream->pes.list[idx].codec; + title->video_codec_param = stream->pes.list[idx].codec_param; + title->demuxer = HB_MPEG_DEMUXER; - for (i = 0; i < stream->ts_number_pids; i++) - { - kind_t kind = stream->ts[i].stream_kind; + if (stream->hb_stream_type == transport) + { - if ( kind == N || kind == U ) - { - hb_stream_delete_entry(stream, i); - } - } + // make sure we're grabbing the PCR PID + update_ts_streams( stream, stream->pmt_info.PCR_PID, 0, -1, P, NULL ); + } - // set the video id, codec & muxer - int idx = index_of_video( stream ); - if ( idx < 0 ) - { - hb_title_close( &aTitle ); - return NULL; - } + hb_stream_duration(stream, title); - stream->ts[idx].number_substreams = 1; - stream->ts[idx].substream_type[0] = 0; - aTitle->video_id = stream->ts[idx].pid; - aTitle->video_codec = st2codec[stream->ts[idx].stream_type].codec; - aTitle->video_codec_param = st2codec[stream->ts[idx].stream_type].codec_param; - aTitle->demuxer = HB_MPEG2_TS_DEMUXER; + // One Chapter + hb_chapter_t * chapter; + chapter = calloc( sizeof( hb_chapter_t ), 1 ); + chapter->index = 1; + chapter->duration = title->duration; + chapter->hours = title->hours; + chapter->minutes = title->minutes; + chapter->seconds = title->seconds; + hb_list_add( title->list_chapter, chapter ); - if ( ( stream->ts_flags & TS_HAS_PCR ) == 0 ) - { - hb_log( "transport stream missing PCRs - using video DTS instead" ); - } - if ( stream->ts_IDRs < 1 ) - { - hb_log( "transport stream doesn't seem to have video IDR frames" ); - aTitle->flags |= HBTF_NO_IDR; - } + if ( stream->has_IDRs < 1 ) + { + hb_log( "stream doesn't seem to have video IDR frames" ); + title->flags |= HBTF_NO_IDR; } - else + + if ( stream->hb_stream_type == transport && + ( stream->ts_flags & TS_HAS_PCR ) == 0 ) { - hb_ps_stream_find_audio_ids(stream, aTitle); + hb_log( "transport stream missing PCRs - using video DTS instead" ); } - return aTitle; + // Height, width, rate and aspect ratio information is filled in + // when the previews are built + return title; } /* @@ -992,11 +1133,11 @@ hb_title_t * hb_stream_title_scan(hb_stream_t *stream, hb_title_t * title) */ static const uint8_t *next_packet( hb_stream_t *stream ) { - uint8_t *buf = stream->ts_packet + stream->packetsize - 188; + uint8_t *buf = stream->ts.packet + stream->packetsize - 188; while ( 1 ) { - if ( fread(stream->ts_packet, 1, stream->packetsize, stream->file_handle) != + if ( fread(stream->ts.packet, 1, stream->packetsize, stream->file_handle) != stream->packetsize ) { return NULL; @@ -1121,8 +1262,8 @@ static int isRecoveryPoint( const uint8_t *buf, int len ) if ( type == 6 ) { bitbuf_t bb; - set_buf(&bb, nal+ii, size, 0); - int count = read_ue( &bb ); + bits_init(&bb, nal+ii, size, 0); + int count = bits_read_ue( &bb ); recovery_frames = count + 3; break; } @@ -1134,22 +1275,25 @@ static int isRecoveryPoint( const uint8_t *buf, int len ) return recovery_frames; } -static int isIframe( hb_stream_t *stream, const uint8_t *buf, int adapt_len ) +static int isIframe( hb_stream_t *stream, const uint8_t *buf, int len ) { // For mpeg2: look for a gop start or i-frame picture start // for h.264: look for idr nal type or a slice header for an i-frame // for vc1: look for a Sequence header - int i; + int ii; uint32_t strid = 0; - int vid = index_of_video(stream); - if ( stream->ts[vid].stream_type <= 2 ) + int vid = pes_index_of_video( stream ); + hb_pes_stream_t *pes = &stream->pes.list[vid]; + if ( pes->stream_type <= 2 || + pes->codec_param == CODEC_ID_MPEG1VIDEO || + pes->codec_param == CODEC_ID_MPEG2VIDEO ) { // This section of the code handles MPEG-1 and MPEG-2 video streams - for (i = 13 + adapt_len; i < 188; i++) + for (ii = 0; ii < len; ii++) { - strid = (strid << 8) | buf[i]; + strid = (strid << 8) | buf[ii]; if ( ( strid >> 8 ) == 1 ) { // we found a start code @@ -1162,10 +1306,10 @@ static int isIframe( hb_stream_t *stream, const uint8_t *buf, int adapt_len ) case 0x00: // picture_start_code // picture_header, let's see if it's an I-frame - if (i<185) + if (ii < len - 3) { // check if picture_coding_type == 1 - if ((buf[i+2] & (0x7 << 3)) == (1 << 3)) + if ((buf[ii+2] & (0x7 << 3)) == (1 << 3)) { // found an I-frame picture return 1; @@ -1178,12 +1322,12 @@ static int isIframe( hb_stream_t *stream, const uint8_t *buf, int adapt_len ) // didn't find an I-frame return 0; } - if ( stream->ts[vid].stream_type == 0x1b ) + if ( pes->stream_type == 0x1b || pes->codec_param == CODEC_ID_H264 ) { - // we have an h.264 stream - for (i = 13 + adapt_len; i < 188; i++) + // we have an h.264 stream + for (ii = 0; ii < len; ii++) { - strid = (strid << 8) | buf[i]; + strid = (strid << 8) | buf[ii]; if ( ( strid >> 8 ) == 1 ) { // we found a start code - remove the ref_idc from the nal type @@ -1195,22 +1339,24 @@ static int isIframe( hb_stream_t *stream, const uint8_t *buf, int adapt_len ) } else if ( nal_type == 0x06 ) { - int off = i + 1; - int recovery_frames = isRecoveryPoint( buf+off, 188-off ); + int off = ii + 1; + int recovery_frames = isRecoveryPoint( buf+off, len-off ); if ( recovery_frames ) + { return recovery_frames; + } } } } // didn't find an I-frame return 0; } - if ( stream->ts[vid].stream_type == 0xea ) + if ( pes->stream_type == 0xea || pes->codec_param == CODEC_ID_VC1 ) { - // we have an vc1 stream - for (i = 13 + adapt_len; i < 188; i++) + // we have an vc1 stream + for (ii = 0; ii < len; ii++) { - strid = (strid << 8) | buf[i]; + strid = (strid << 8) | buf[ii]; if ( strid == 0x10f ) { // the ffmpeg vc1 decoder requires a seq hdr code in the first @@ -1227,6 +1373,11 @@ static int isIframe( hb_stream_t *stream, const uint8_t *buf, int adapt_len ) return 1; } +static int ts_isIframe( hb_stream_t *stream, const uint8_t *buf, int adapt_len ) +{ + return isIframe( stream, buf + 13 + adapt_len, 188 - ( 13 + adapt_len ) ); +} + /* * scan the next MB of 'stream' to find the next start packet for * the Packetized Elementary Stream associated with TS PID 'pid'. @@ -1300,41 +1451,49 @@ static const uint8_t *hb_ts_stream_getPEStype(hb_stream_t *stream, uint32_t pid, return 0; } -static uint64_t hb_ps_stream_getVideoPTS(hb_stream_t *stream) +static hb_buffer_t * hb_ps_stream_getVideo( + hb_stream_t *stream, + hb_pes_info_t *pi) { - hb_buffer_t *buf; - hb_list_t *list = hb_list_init(); + hb_buffer_t *buf = hb_buffer_init(HB_DVD_READ_BUFFER_SIZE); + hb_pes_info_t pes_info; // how many blocks we read while searching for a video PES header - int blksleft = 1024; - uint64_t pts = 0; + int blksleft = 2048; - while (--blksleft >= 0 && (buf = hb_stream_read(stream)) != NULL) + while (--blksleft >= 0) { - hb_buffer_t *es; - - // 'buf' contains an MPEG2 PACK - get a list of all it's - // elementary streams - hb_demux_ps( buf, list, 0 ); + buf->size = 0; + int len = hb_ps_read_packet( stream, buf ); + if ( len == 0 ) + { + // EOF + break; + } + if ( !hb_parse_ps( stream, buf->data, buf->size, &pes_info ) ) + continue; - while ( ( es = hb_list_item( list, 0 ) ) ) + int idx; + if ( pes_info.stream_id == 0xbd ) { - hb_list_rem( list, es ); - if ( es->id == 0xe0 ) + idx = index_of_ps_stream( stream, pes_info.stream_id, + pes_info.bd_substream_id ); + } + else + { + idx = index_of_ps_stream( stream, pes_info.stream_id, + pes_info.stream_id_ext ); + } + if ( stream->pes.list[idx].stream_kind == V ) + { + if ( pes_info.pts != -1 ) { - // this PES contains video - if there's a PTS we're done - // hb_demux_ps left the PTS in buf_es->start. - if ( es->start != ~0 ) - { - pts = es->start; - blksleft = 0; - break; - } + *pi = pes_info; + return buf; } - hb_buffer_close( &es ); } } - hb_list_empty( &list ); - return pts; + hb_buffer_close( &buf ); + return NULL; } /*********************************************************************** @@ -1382,7 +1541,7 @@ static struct pts_pos hb_sample_pts(hb_stream_t *stream, uint64_t fpos) int adapt_len; fseeko( stream->file_handle, fpos, SEEK_SET ); align_to_next_packet( stream ); - int pid = stream->ts[index_of_video(stream)].pid; + int pid = stream->ts.list[ts_index_of_video(stream)].pid; buf = hb_ts_stream_getPEStype( stream, pid, &adapt_len ); if ( buf == NULL ) { @@ -1401,15 +1560,15 @@ static struct pts_pos hb_sample_pts(hb_stream_t *stream, uint64_t fpos) ( (uint64_t)pes[12] << 7 ) | ( (uint64_t)pes[13] >> 1 ); - if ( isIframe( stream, buf, adapt_len ) ) + if ( ts_isIframe( stream, buf, adapt_len ) ) { - if ( stream->ts_IDRs < 255 ) + if ( stream->has_IDRs < 255 ) { - ++stream->ts_IDRs; + ++stream->has_IDRs; } } pp.pos = ftello(stream->file_handle); - if ( !stream->ts_IDRs ) + if ( !stream->has_IDRs ) { // Scan a little more to see if we will stumble upon one int ii; @@ -1418,9 +1577,9 @@ static struct pts_pos hb_sample_pts(hb_stream_t *stream, uint64_t fpos) buf = hb_ts_stream_getPEStype( stream, pid, &adapt_len ); if ( buf == NULL ) break; - if ( isIframe( stream, buf, adapt_len ) ) + if ( ts_isIframe( stream, buf, adapt_len ) ) { - ++stream->ts_IDRs; + ++stream->has_IDRs; break; } } @@ -1428,6 +1587,9 @@ static struct pts_pos hb_sample_pts(hb_stream_t *stream, uint64_t fpos) } else { + hb_buffer_t *buf; + hb_pes_info_t pes_info; + // round address down to nearest dvd sector start fpos &=~ ( HB_DVD_READ_BUFFER_SIZE - 1 ); fseeko( stream->file_handle, fpos, SEEK_SET ); @@ -1435,7 +1597,46 @@ static struct pts_pos hb_sample_pts(hb_stream_t *stream, uint64_t fpos) { skip_to_next_pack( stream ); } - pp.pts = hb_ps_stream_getVideoPTS( stream ); + buf = hb_ps_stream_getVideo( stream, &pes_info ); + if ( buf == NULL ) + { + hb_log("hb_sample_pts: couldn't find video packet near %"PRIu64, fpos); + return pp; + } + if ( pes_info.pts < 0 ) + { + hb_log("hb_sample_pts: no PTS in video packet near %"PRIu64, fpos); + hb_buffer_close( &buf ); + return pp; + } + if ( isIframe( stream, buf->data, buf->size ) ) + { + if ( stream->has_IDRs < 255 ) + { + ++stream->has_IDRs; + } + } + hb_buffer_close( &buf ); + if ( !stream->has_IDRs ) + { + // Scan a little more to see if we will stumble upon one + int ii; + for ( ii = 0; ii < 10; ii++ ) + { + buf = hb_ps_stream_getVideo( stream, &pes_info ); + if ( buf == NULL ) + break; + if ( isIframe( stream, buf->data, buf->size ) ) + { + ++stream->has_IDRs; + hb_buffer_close( &buf ); + break; + } + hb_buffer_close( &buf ); + } + } + + pp.pts = pes_info.pts; pp.pos = ftello(stream->file_handle); } return pp; @@ -1525,95 +1726,9 @@ hb_buffer_t * hb_stream_read( hb_stream_t * src_stream ) { return hb_ffmpeg_read( src_stream ); } - if ( src_stream->hb_stream_type == dvd_program ) - { - hb_buffer_t *b = hb_buffer_init(HB_DVD_READ_BUFFER_SIZE); - size_t amt_read = fread(b->data, HB_DVD_READ_BUFFER_SIZE, 1, - src_stream->file_handle); - if (amt_read <= 0) - hb_buffer_close( &b ); - return b; - } 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. - 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; - - hb_buffer_t *b = hb_buffer_init(HB_DVD_READ_BUFFER_SIZE); - uint8_t *cp = b->data; - uint8_t *ep = cp + b->alloc; - uint32_t strt_code = -1; - - *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; - // Non-video streams can emulate start codes, so we need - // to inspect PES packets and skip over their data - // sections to avoid mis-detection of the next pack header. - if ( ( strt_code >> 8 ) == 0x000001 && - ( strt_code & 0xff ) >= 0xbb ) - { - int len = 0; - c = getc_unlocked( src_stream->file_handle ); - if ( c == EOF ) - break; - len = c << 8; - c = getc_unlocked( src_stream->file_handle ); - if ( c == EOF ) - break; - len |= c; - if ( cp+len+2 > ep ) - { - // need to expand the buffer - int curSize = cp - b->data; - if ( curSize * 2 > curSize+len+2 ) - hb_buffer_realloc( b, curSize * 2 ); - else - hb_buffer_realloc( b, curSize + len + 2 ); - cp = b->data + curSize; - ep = b->data + b->alloc; - } - *cp++ = len >> 8; - *cp++ = len & 0xff; - fread( cp, 1, len, src_stream->file_handle ); - cp += len; - } - } - 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 ); - // Only 3 of the 4 bytes read were added to the buffer. - b->size -= 3; - } - return b; + return hb_ps_stream_decode( src_stream ); } return hb_ts_stream_decode( src_stream ); } @@ -1711,7 +1826,7 @@ int hb_stream_seek( hb_stream_t * stream, float f ) // forwards to the next transport stream packet. hb_ts_stream_reset(stream); align_to_next_packet(stream); - if ( stream->ts_IDRs ) + if ( stream->has_IDRs ) { // the stream has IDRs so look for one. stream->need_keyframe = 1; @@ -1719,7 +1834,13 @@ int hb_stream_seek( hb_stream_t * stream, float f ) } else if ( stream->hb_stream_type == program ) { + hb_ps_stream_reset(stream); skip_to_next_pack( stream ); + if ( stream->has_IDRs ) + { + // the stream has IDRs so look for one. + stream->need_keyframe = 1; + } } return 1; @@ -1734,6 +1855,66 @@ int hb_stream_seek_ts( hb_stream_t * stream, int64_t ts ) return -1; } +static char* strncpyupper( char *dst, const char *src, int len ) +{ + int ii; + + for ( ii = 0; ii < len-1 && src[ii]; ii++ ) + { + dst[ii] = islower(src[ii]) ? toupper(src[ii]) : src[ii]; + } + dst[ii] = '\0'; + return dst; +} + +static const char *stream_type_name2(hb_stream_t *stream, hb_pes_stream_t *pes) +{ + static char codec_name_caps[80]; + + if ( stream->reg_desc == STR4_TO_UINT32("HDMV") ) + { + // Names for streams we know about. + switch ( pes->stream_type ) + { + case 0x80: + return "BD LPCM"; + + case 0x83: + return "TrueHD"; + + case 0x84: + return "E-AC3"; + + case 0x85: + return "DTS-HD HRA"; + + case 0x86: + return "DTS-HD MA"; + + default: + break; + } + } + if ( st2codec[pes->stream_type].name ) + { + return st2codec[pes->stream_type].name; + } + if ( pes->codec_name[0] != 0 ) + { + return pes->codec_name; + } + if ( pes->codec & HB_ACODEC_FF_MASK ) + { + AVCodec * codec = avcodec_find_decoder( pes->codec_param ); + if ( codec && codec->name && codec->name[0] ) + { + strncpyupper( codec_name_caps, codec->name, 80 ); + return codec_name_caps; + } + } + return "Unknown"; +} + static const char *stream_type_name (uint32_t reg_desc, uint8_t stream_type) { if ( reg_desc == STR4_TO_UINT32("HDMV") ) @@ -1763,24 +1944,29 @@ static const char *stream_type_name (uint32_t reg_desc, uint8_t stream_type) return st2codec[stream_type].name ? st2codec[stream_type].name : "Unknown"; } -static void set_ts_audio_description( hb_audio_t *audio, iso639_lang_t *lang ) +static void set_audio_description( + hb_stream_t * stream, + hb_audio_t *audio, + iso639_lang_t *lang) { - /* XXX - * This is a duplicate of code in dvd.c - it should get factored out - * into a common routine. We probably should only be putting the lang - * code or a lang pointer into the audio config & let the common description - * formatting routine in scan.c do all the stuff below. - */ + int idx; const char *codec_name; + char codec_name_caps[80]; + AVCodecContext *cc = NULL; + + if ( stream && stream->ffmpeg_info_ic ) + { + cc = stream->ffmpeg_info_ic->streams[audio->id]->codec; + } // Names for streams we know about. - if ( audio->config.in.stream_type == 0x80 && + if ( audio->config.in.stream_type == 0x80 && audio->config.in.reg_desc == STR4_TO_UINT32("HDMV") ) { // LPCM audio in bluray have an stype of 0x80 codec_name = "BD LPCM"; } - else if ( audio->config.in.stream_type == 0x83 && + else if ( audio->config.in.stream_type == 0x83 && audio->config.in.reg_desc == STR4_TO_UINT32("HDMV") ) { // This is an interleaved TrueHD/AC-3 stream and the esid of @@ -1790,30 +1976,30 @@ static void set_ts_audio_description( hb_audio_t *audio, iso639_lang_t *lang ) else codec_name = "TrueHD"; } - else if ( audio->config.in.stream_type == 0x86 && + else if ( audio->config.in.stream_type == 0x86 && audio->config.in.reg_desc == STR4_TO_UINT32("HDMV") ) { - // This is an interleaved DTS-HD MA/DTS stream and the + // This is an interleaved DTS-HD MA/DTS stream and the // esid of the DTS is 0x71 if (audio->config.in.substream_type == HB_SUBSTREAM_BD_DTS) codec_name = "DTS"; else codec_name = "DTS-HD MA"; } - else if ( audio->config.in.stream_type == 0x85 && + else if ( audio->config.in.stream_type == 0x85 && audio->config.in.reg_desc == STR4_TO_UINT32("HDMV") ) { // DTS-HD HRA audio in bluray has an stype of 0x85 // which conflicts with ATSC Program ID // To distinguish, Bluray streams have a reg_desc of HDMV - // This is an interleaved DTS-HD HRA/DTS stream and the + // This is an interleaved DTS-HD HRA/DTS stream and the // esid of the DTS is 0x71 if (audio->config.in.substream_type == HB_SUBSTREAM_BD_DTS) codec_name = "DTS"; else codec_name = "DTS-HD HRA"; } - else if ( audio->config.in.stream_type == 0x84 && + else if ( audio->config.in.stream_type == 0x84 && audio->config.in.reg_desc == STR4_TO_UINT32("HDMV") ) { // EAC3 audio in bluray has an stype of 0x84 @@ -1821,58 +2007,10 @@ static void set_ts_audio_description( hb_audio_t *audio, iso639_lang_t *lang ) // To distinguish, Bluray streams have a reg_desc of HDMV codec_name = "E-AC3"; } - else if ( st2codec[audio->config.in.stream_type].kind == A ) - { - codec_name = stream_type_name(audio->config.in.reg_desc, - audio->config.in.stream_type); - } - // For streams demuxed by us and decoded by ffmpeg, we can lookup the - // decoder name. - else if ( ( audio->config.in.codec & HB_ACODEC_FF_MASK ) && - avcodec_find_decoder( audio->config.in.codec_param ) ) - { - codec_name = avcodec_find_decoder( audio->config.in.codec_param )->name; - } - else - { - codec_name = audio->config.in.codec == HB_ACODEC_AC3 ? "AC3" : - audio->config.in.codec == HB_ACODEC_DCA ? "DTS" : - audio->config.in.codec == HB_ACODEC_DCA_HD ? "DTS-HD" : - audio->config.in.codec == HB_ACODEC_LPCM ? "LPCM" : - (audio->config.in.codec & HB_ACODEC_FF_MASK) ? "FFmpeg" : - "Unknown"; - } - snprintf( audio->config.lang.description, - sizeof( audio->config.lang.description ), "%s (%s)", - strlen(lang->native_name) ? lang->native_name : lang->eng_name, - codec_name ); - - snprintf( audio->config.lang.simple, sizeof( audio->config.lang.simple ), "%s", - strlen(lang->native_name) ? lang->native_name : lang->eng_name ); - snprintf( audio->config.lang.iso639_2, sizeof( audio->config.lang.iso639_2 ), - "%s", lang->iso639_2); -} - -static void set_audio_description( hb_stream_t * stream, hb_audio_t *audio, iso639_lang_t *lang ) -{ - /* XXX - * This is a duplicate of code in dvd.c - it should get factored out - * into a common routine. We probably should only be putting the lang - * code or a lang pointer into the audio config & let the common description - * formatting routine in scan.c do all the stuff below. - */ - const char *codec_name; - AVCodecContext *cc = NULL; - - if ( stream && stream->ffmpeg_info_ic ) - { - cc = stream->ffmpeg_info_ic->streams[audio->id]->codec; - } - // For streams demuxed and decoded by ffmpeg, we have a cached context. // Use it to get the name and profile information. Obtaining // the profile requires that ffmpeg has already probed the stream. - if ( ( audio->config.in.codec & HB_ACODEC_FF_MASK ) && cc && + else if ( ( audio->config.in.codec & HB_ACODEC_FF_MASK ) && cc && avcodec_find_decoder( cc->codec_id ) ) { AVCodec *codec = avcodec_find_decoder( cc->codec_id ); @@ -1885,21 +2023,35 @@ static void set_audio_description( hb_stream_t * stream, hb_audio_t *audio, iso6 codec_name = profile_name; } } + else if ( stream->hb_stream_type != ffmpeg && + (idx = index_of_id( stream, audio->id ) ) >= 0 ) + { + codec_name = stream_type_name2( stream, &stream->pes.list[idx] ); + } + else if ( st2codec[audio->config.in.stream_type].kind == A ) + { + codec_name = stream_type_name(audio->config.in.reg_desc, + audio->config.in.stream_type); + } // For streams demuxed by us and decoded by ffmpeg, we can lookup the // decoder name. else if ( ( audio->config.in.codec & HB_ACODEC_FF_MASK ) && avcodec_find_decoder( audio->config.in.codec_param ) ) { codec_name = avcodec_find_decoder( audio->config.in.codec_param )->name; + strncpyupper( codec_name_caps, codec_name, 80 ); + codec_name = codec_name_caps; } else { codec_name = audio->config.in.codec == HB_ACODEC_AC3 ? "AC3" : - audio->config.in.codec == HB_ACODEC_DCA ? "DTS" : - audio->config.in.codec == HB_ACODEC_LPCM ? "LPCM" : - (audio->config.in.codec & HB_ACODEC_FF_MASK) ? "FFmpeg" : - "Unknown"; + audio->config.in.codec == HB_ACODEC_DCA ? "DTS" : + audio->config.in.codec == HB_ACODEC_DCA_HD ? "DTS-HD" : + audio->config.in.codec == HB_ACODEC_LPCM ? "LPCM" : + (audio->config.in.codec & HB_ACODEC_FF_MASK) ? "FFmpeg" : + "Unknown"; } + snprintf( audio->config.lang.description, sizeof( audio->config.lang.description ), "%s (%s)", strlen(lang->native_name) ? lang->native_name : lang->eng_name, @@ -1922,271 +2074,108 @@ static void set_audio_description( hb_stream_t * stream, hb_audio_t *audio, iso6 "%s", lang->iso639_2); } -static void add_audio(int track, hb_list_t *list_audio, hb_stream_t *stream, int substream_type, uint32_t codec, uint32_t codec_param ) +// Sort specifies the index in the audio list where you would +// like sorted items to begin. +static void pes_add_audio_to_title( + hb_stream_t *stream, + int idx, + hb_title_t *title, + int sort) { - hb_audio_t *audio; - hb_ts_stream_t *ts = &stream->ts[track]; - - audio = calloc( sizeof( hb_audio_t ), 1 ); - - audio->id = (substream_type << 16) | ts->pid; - audio->config.in.reg_desc = stream->pmt_info.reg_desc; - audio->config.in.stream_type = ts->stream_type; - audio->config.in.substream_type = substream_type; - - audio->config.in.codec = codec; - audio->config.in.codec_param = codec_param; - - set_ts_audio_description( audio, - lang_for_code( ts->a52_info.lang_code ) ); + hb_pes_stream_t *pes = &stream->pes.list[idx]; - hb_log("transport stream pid 0x%x (type 0x%x substream 0x%x) audio 0x%x", - ts->pid, ts->stream_type, substream_type, audio->id); - - audio->config.in.track = track; - hb_list_add( list_audio, audio ); -} - -static void hb_ts_stream_set_audio_list( - hb_list_t *list_audio, - hb_stream_t *stream) -{ - off_t cur_pos; - const uint8_t *buf; + // Sort by id when adding to the list + // This assures that they are always displayed in the same order + int id = get_id( pes ); int i; + hb_audio_t *tmp = NULL; - cur_pos = ftello(stream->file_handle); - for ( i = 0; i < stream->ts_number_pids; i++ ) - { - stream->ts[i].substream_type[0] = 0; - stream->ts[i].number_substreams = 1; + int count = hb_list_count( title->list_audio ); - if ( stream->ts[i].stream_type == 0x80 && - stream->pmt_info.reg_desc == STR4_TO_UINT32("HDMV") ) - { - // LPCM audio in bluray have an stype of 0x80 - // 0x80 is used for other DigiCipher normally - // To distinguish, Bluray streams have a reg_desc of HDMV - stream->ts[i].stream_kind = A; - add_audio(i, list_audio, stream, 0, - HB_ACODEC_FFMPEG, CODEC_ID_PCM_BLURAY ); - continue; - } + // Don't add the same audio twice. Search for audio. + for ( i = 0; i < count; i++ ) + { + tmp = hb_list_item( title->list_audio, i ); + if ( id == tmp->id ) + return; + } - // The blu ray consortium apparently forgot to read the portion - // of the MPEG spec that says one PID should map to one media - // stream and multiplexed multiple types of audio into one PID - // using the extended stream identifier of the PES header to - // distinguish them. So we have to check if that's happening and - // if so tell the runtime what esid we want. - if ( stream->ts[i].stream_type == 0x83 && - stream->pmt_info.reg_desc == STR4_TO_UINT32("HDMV") ) - { - // This is an interleaved TrueHD/AC-3 stream and the esid of - // the AC-3 is 0x76 - stream->ts[i].stream_kind = A; - stream->ts[i].substream_type[0] = HB_SUBSTREAM_BD_AC3; - add_audio(i, list_audio, stream, HB_SUBSTREAM_BD_AC3, - HB_ACODEC_AC3, 0 ); - stream->ts[i].substream_type[1] = HB_SUBSTREAM_BD_TRUEHD; - add_audio(i, list_audio, stream, HB_SUBSTREAM_BD_TRUEHD, - HB_ACODEC_FFMPEG, CODEC_ID_TRUEHD ); - stream->ts[i].number_substreams = 2; - continue; - } - if ( stream->ts[i].stream_type == 0x86 && - stream->pmt_info.reg_desc == STR4_TO_UINT32("HDMV") ) - { - // This is an interleaved DTS-HD MA/DTS stream and the - // esid of the DTS is 0x71 - stream->ts[i].stream_kind = A; - stream->ts[i].substream_type[0] = HB_SUBSTREAM_BD_DTS; - add_audio(i, list_audio, stream, HB_SUBSTREAM_BD_DTS, - HB_ACODEC_DCA, 0 ); - stream->ts[i].substream_type[1] = 0; - add_audio(i, list_audio, stream, 0, HB_ACODEC_DCA_HD, CODEC_ID_DTS ); - stream->ts[i].number_substreams = 2; - continue; - } - if ( stream->ts[i].stream_type == 0x85 && - stream->pmt_info.reg_desc == STR4_TO_UINT32("HDMV") ) - { - // DTS-HD HRA audio in bluray has an stype of 0x85 - // which conflicts with ATSC Program ID - // To distinguish, Bluray streams have a reg_desc of HDMV - // This is an interleaved DTS-HD HRA/DTS stream and the - // esid of the DTS is 0x71 - stream->ts[i].stream_kind = A; - stream->ts[i].substream_type[0] = HB_SUBSTREAM_BD_DTS; - add_audio(i, list_audio, stream, HB_SUBSTREAM_BD_DTS, - HB_ACODEC_DCA, 0 ); - stream->ts[i].substream_type[1] = 0; - add_audio(i, list_audio, stream, 0, HB_ACODEC_DCA_HD, CODEC_ID_DTS ); - stream->ts[i].number_substreams = 2; - continue; - } - if ( stream->ts[i].stream_type == 0x84 && - stream->pmt_info.reg_desc == STR4_TO_UINT32("HDMV") ) - { - // EAC3 audio in bluray has an stype of 0x84 - // which conflicts with SDDS - // To distinguish, Bluray streams have a reg_desc of HDMV - stream->ts[i].stream_kind = A; - add_audio(i, list_audio, stream, 0, HB_ACODEC_FFMPEG, CODEC_ID_EAC3 ); - continue; - } + hb_audio_t *audio = calloc( sizeof( hb_audio_t ), 1 ); - if ( stream->ts[i].stream_kind == A ) - { - add_audio(i, list_audio, stream, 0, - st2codec[stream->ts[i].stream_type].codec, - st2codec[stream->ts[i].stream_type].codec_param ); - continue; - } + audio->id = id; + audio->config.in.reg_desc = stream->reg_desc; + audio->config.in.stream_type = pes->stream_type; + audio->config.in.substream_type = pes->stream_id_ext; - if ( stream->ts[i].stream_kind != U ) - { - // Not audio - continue; - } + audio->config.in.codec = pes->codec; + audio->config.in.codec_param = pes->codec_param; - // If we get here, we have a stream type that we are not yet - // sure about. Probe some data to see if it looks like - // a known stream type. - fseeko(stream->file_handle, 0, SEEK_SET); - align_to_next_packet(stream); + set_audio_description( stream, audio, lang_for_code( pes->lang_code ) ); - int adapt_len; - buf = hb_ts_stream_getPEStype(stream, stream->ts[i].pid, &adapt_len); + hb_log("stream id 0x%x (type 0x%x substream 0x%x) audio 0x%x", + pes->stream_id, pes->stream_type, pes->stream_id_ext, audio->id); - const uint8_t *pes = buf + 4 + adapt_len; + audio->config.in.track = idx; - /* check that we found a PES header */ - if (buf && pes[0] == 0x00 && pes[1] == 0x00 && pes[2] == 0x01) + // Search for the sort position + if ( sort >= 0 ) + { + sort = sort < count ? sort : count; + for ( i = sort; i < count; i++ ) { - // 0xbd ("private stream 1") is the normal container for non-ISO - // media - AC3/DCA/PCM/etc. - if ( pes[3] == 0xbd ) - { - // XXX assume unknown stream types are AC-3 (if they're not - // audio we'll find that out during the scan but if they're - // some other type of audio we'll end up ignoring them). - add_audio(i, list_audio, stream, 0, HB_ACODEC_AC3, 0 ); - } - else if ((pes[3] & 0xe0) == 0xc0) - { - // XXX assume unknown stream types are MPEG audio - // 0xC0 - 0xCF are the normal containers for ISO-standard - // media (mpeg2 audio and mpeg4 AAC). - add_audio(i, list_audio, stream, 0, - HB_ACODEC_FFMPEG, CODEC_ID_MP2 ); - } - else + tmp = hb_list_item( title->list_audio, i ); + int sid = tmp->id & 0xffff; + int ssid = tmp->id >> 16; + if ( pes->stream_id < sid ) + break; + else if ( pes->stream_id <= sid && + pes->stream_id_ext <= ssid ) { - hb_log("transport stream pid 0x%x (type 0x%x, substream 0x%x) " - "isn't audio", stream->ts[i].pid, - stream->ts[i].stream_type, pes[3]); + break; } } - else - { - hb_log("transport stream pid 0x%x (type 0x%x) isn't audio", - stream->ts[i].pid, - stream->ts[i].stream_type); - } - - fseeko(stream->file_handle, cur_pos, SEEK_SET); + hb_list_insert( title->list_audio, i, audio ); } -} - -static void add_audio_to_title(hb_title_t *title, int id) -{ - hb_audio_t *audio = calloc( sizeof( hb_audio_t ), 1 ); - - audio->id = id; - switch ( id >> 12 ) - { - case 0x0: - audio->config.in.codec = HB_ACODEC_MP3; - hb_log("add_audio_to_title: added MPEG audio stream 0x%x", id); - break; - case 0x2: - // type 2 is a DVD subtitle stream - just ignore it */ - free( audio ); - return; - case 0x8: - audio->config.in.codec = HB_ACODEC_AC3; - hb_log("add_audio_to_title: added AC3 audio stream 0x%x", id); - break; - case 0xa: - audio->config.in.codec = HB_ACODEC_LPCM; - hb_log("add_audio_to_title: added LPCM audio stream 0x%x", id); - break; - default: - hb_log("add_audio_to_title: unknown audio stream type 0x%x", id); - free( audio ); - return; - - } - set_audio_description( NULL, audio, lang_for_code( 0 ) ); - - // Sort by id when adding to the list - int i; - for ( i = 0; i < hb_list_count( title->list_audio ); i++ ) + else { - hb_audio_t *tmp = hb_list_item( title->list_audio, i ); - if ( audio->id < tmp->id ) - break; + hb_list_add( title->list_audio, audio ); } - hb_list_insert( title->list_audio, i, audio ); } -static void hb_ps_stream_find_audio_ids(hb_stream_t *stream, hb_title_t *title) +static void hb_init_audio_list(hb_stream_t *stream, hb_title_t *title) { - off_t cur_pos = ftello(stream->file_handle); - hb_buffer_t *buf; - hb_list_t *list = hb_list_init(); - // how many blocks we read while searching for audio streams - int blksleft = 4096; - // there can be at most 16 unique streams in an MPEG PS (8 in a DVD) - // so we use a bitmap to keep track of the ones we've already seen. - // Bit 'i' of smap is set if we've already added the audio for - // audio substream id 'i' to the title's audio list. - uint32_t smap = 0; - - // start looking 20% into the file since there's occasionally no - // audio at the beginning (particularly for vobs). - hb_stream_seek(stream, 0.2f); + int ii; + int map_idx; + int largest = -1; - while (--blksleft >= 0 && (buf = hb_stream_read(stream)) != NULL) + // First add all that were found in a map. + for ( map_idx = 0; 1; map_idx++ ) { - hb_buffer_t *es; - - // 'buf' contains an MPEG2 PACK - get a list of all it's - // elementary streams - hb_demux_ps( buf, list, 0 ); - - while ( ( es = hb_list_item( list, 0 ) ) ) + for ( ii = 0; ii < stream->pes.count; ii++ ) { - hb_list_rem( list, es ); - if ( (es->id & 0xff) == 0xbd || (es->id & 0xe0) == 0xc0 ) + if ( stream->pes.list[ii].stream_kind == A ) { - // this PES contains some kind of audio - get the substream id - // and check if we've seen it already. - int ssid = (es->id > 0xff ? es->id >> 8 : es->id) & 0xf; - if ( (smap & (1 << ssid)) == 0 ) + if ( stream->pes.list[ii].map_idx == map_idx ) { - // we haven't seen this stream before - add it to the - // title's list of audio streams. - smap |= (1 << ssid); - add_audio_to_title(title, es->id); + pes_add_audio_to_title( stream, ii, title, -1 ); } + if ( stream->pes.list[ii].map_idx > largest ) + largest = stream->pes.list[ii].map_idx; } - hb_buffer_close( &es ); + } + if ( map_idx > largest ) + break; + } + + int count = hb_list_count( title->list_audio ); + // Now add the reset. Sort them by stream id. + for ( ii = 0; ii < stream->pes.count; ii++ ) + { + if ( stream->pes.list[ii].stream_kind == A ) + { + pes_add_audio_to_title( stream, ii, title, count ); } } - hb_list_empty( &list ); - fseeko(stream->file_handle, cur_pos, SEEK_SET); } /*********************************************************************** @@ -2199,25 +2188,144 @@ static void hb_ts_stream_init(hb_stream_t *stream) { int i; - for (i=0; i < kMaxNumberDecodeStreams; i++) + if ( stream->ts.list ) { - stream->ts[i].continuity = -1; - stream->ts[i].pid = -1; - stream->ts[i].number_substreams = 1; - stream->ts[i].substream_type[0] = 0; + for (i=0; i < stream->ts.alloc; i++) + { + stream->ts.list[i].continuity = -1; + stream->ts.list[i].pid = -1; + stream->ts.list[i].pes_list = -1; + } } - stream->ts_packet = malloc( stream->packetsize ); + stream->ts.count = 0; + + if ( stream->pes.list ) + { + for (i=0; i < stream->pes.alloc; i++) + { + stream->pes.list[i].stream_id = -1; + stream->pes.list[i].next = -1; + } + } + stream->pes.count = 0; + + stream->ts.packet = malloc( stream->packetsize ); // Find the audio and video pids in the stream hb_ts_stream_find_pids(stream); - for (i = 0; i < stream->ts_number_pids; i++) + // hb_ts_resolve_pid_types reads some data, so the TS buffers + // are needed here. + for (i = 0; i < stream->ts.count; i++) { // demuxing buffer for TS to PS conversion - stream->ts[i].buf = hb_buffer_init(stream->packetsize); - stream->ts[i].extra_buf = hb_buffer_init(stream->packetsize); - stream->ts[i].buf->size = 0; - stream->ts[i].extra_buf->size = 0; + 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); + + hb_log("Found the following PIDS"); + hb_log(" Video PIDS : "); + for (i=0; i < stream->ts.count; i++) + { + if ( ts_stream_kind( stream, i ) == V ) + { + hb_log( " 0x%x type %s (0x%x)%s", + stream->ts.list[i].pid, + 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)" : ""); + } + } + hb_log(" Audio PIDS : "); + for (i = 0; i < stream->ts.count; i++) + { + if ( ts_stream_kind( stream, i ) == A ) + { + hb_log( " 0x%x type %s (0x%x)%s", + stream->ts.list[i].pid, + 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)" : ""); + } + } + hb_log(" Other PIDS : "); + for (i = 0; i < stream->ts.count; i++) + { + if ( ts_stream_kind( stream, i ) == N || + ts_stream_kind( stream, i ) == P ) + { + hb_log( " 0x%x type %s (0x%x)%s", + stream->ts.list[i].pid, + 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)" : ""); + } + } +} + +static void hb_ps_stream_init(hb_stream_t *stream) +{ + int i; + + if ( stream->pes.list ) + { + for (i=0; i < stream->pes.alloc; i++) + { + stream->pes.list[i].stream_id = -1; + stream->pes.list[i].next = -1; + } + } + stream->pes.count = 0; + + // Find the audio and video pids in the stream + hb_ps_stream_find_streams(stream); + hb_ps_resolve_stream_types(stream); + + hb_log("Found the following streams"); + hb_log(" Video Streams : "); + for (i=0; i < stream->pes.count; i++) + { + if ( stream->pes.list[i].stream_kind == V ) + { + hb_log( " 0x%x-0x%x type %s (0x%x)", + stream->pes.list[i].stream_id, + stream->pes.list[i].stream_id_ext, + stream_type_name2(stream, + &stream->pes.list[i]), + stream->pes.list[i].stream_type); + } + } + hb_log(" Audio Streams : "); + for (i = 0; i < stream->pes.count; i++) + { + if ( stream->pes.list[i].stream_kind == A ) + { + hb_log( " 0x%x-0x%x type %s (0x%x)", + stream->pes.list[i].stream_id, + stream->pes.list[i].stream_id_ext, + stream_type_name2(stream, + &stream->pes.list[i]), + stream->pes.list[i].stream_type ); + } + } + hb_log(" Other Streams : "); + for (i = 0; i < stream->pes.count; i++) + { + if ( stream->pes.list[i].stream_kind == N ) + { + hb_log( " 0x%x-0x%x type %s (0x%x)", + stream->pes.list[i].stream_id, + stream->pes.list[i].stream_id_ext, + stream_type_name2(stream, + &stream->pes.list[i]), + stream->pes.list[i].stream_type ); + } } } @@ -2245,7 +2353,7 @@ static off_t align_to_next_packet(hb_stream_t *stream) for ( i = sizeof(buf) - 8 * stream->packetsize; --i >= 0; ++bp ) { - if ( have_ts_sync( bp, stream->packetsize ) ) + if ( have_ts_sync( bp, stream->packetsize, 8 ) ) { break; } @@ -2273,7 +2381,7 @@ static const unsigned int bitmask[] = { 0x1ffff,0x3ffff,0x7ffff,0xfffff,0x1fffff,0x3fffff,0x7fffff,0xffffff, 0x1ffffff,0x3ffffff,0x7ffffff,0xfffffff,0x1fffffff,0x3fffffff,0x7fffffff,0xffffffff}; -static inline void set_buf(bitbuf_t *bb, uint8_t* buf, int bufsize, int clear) +static inline void bits_init(bitbuf_t *bb, uint8_t* buf, int bufsize, int clear) { bb->pos = 0; bb->buf = buf; @@ -2282,14 +2390,56 @@ static inline void set_buf(bitbuf_t *bb, uint8_t* buf, int bufsize, int clear) (bb->buf[2] << 8) | bb->buf[3]; if (clear) memset(bb->buf, 0, bufsize); + bb->size = bufsize; +} + +static inline void bits_clone( bitbuf_t *dst, bitbuf_t *src, int bufsize ) +{ + *dst = *src; + dst->size = (dst->pos >> 3) + bufsize; } -static inline int buf_eob(bitbuf_t *bb) +static inline int bits_bytes_left(bitbuf_t *bb) +{ + return bb->size - (bb->pos >> 3); +} + +static inline int bits_eob(bitbuf_t *bb) { return bb->pos >> 3 == bb->size; } -static inline unsigned int get_bits(bitbuf_t *bb, int bits) +static inline unsigned int bits_peek(bitbuf_t *bb, int bits) +{ + unsigned int val; + int left = 32 - (bb->pos & 31); + + if (bits < left) + { + val = (bb->val >> (left - bits)) & bitmask[bits]; + } + else + { + val = (bb->val & bitmask[left]) << (bits - left); + int bpos = bb->pos + left; + bits -= left; + + + if (bits > 0) + { + int pos = bpos >> 3; + 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]; + } + } + + return val; +} + +static inline unsigned int bits_get(bitbuf_t *bb, int bits) { unsigned int val; int left = 32 - (bb->pos & 31); @@ -2318,15 +2468,28 @@ static inline unsigned int get_bits(bitbuf_t *bb, int bits) return val; } -static inline int read_ue(bitbuf_t *bb ) +static inline int bits_read_ue(bitbuf_t *bb ) { int ii = 0; - while( get_bits( bb, 1 ) == 0 && !buf_eob( bb ) && ii < 32 ) + while( bits_get( bb, 1 ) == 0 && !bits_eob( bb ) && ii < 32 ) { ii++; } - return( ( 1 << ii) - 1 + get_bits( bb, ii ) ); + return( ( 1 << ii) - 1 + bits_get( bb, ii ) ); +} + +static inline int bits_skip(bitbuf_t *bb, int bits) +{ + if (bits <= 0) + return 0; + while (bits > 32) + { + bits_get(bb, 32); + bits -= 32; + } + bits_get(bb, bits); + return 0; } // extract what useful information we can from the elementary stream @@ -2334,68 +2497,105 @@ static inline int read_ue(bitbuf_t *bb ) // Descriptors with info we don't currently use are ignored. // The descriptor list & descriptor item formats are defined in // ISO 13818-1 (2000E) section 2.6 (pg. 62). -static void decode_element_descriptors(hb_stream_t* stream, int esindx, - const uint8_t *dp, uint8_t dlen) +static void decode_element_descriptors( + hb_stream_t *stream, + int pes_idx, + bitbuf_t *bb) { - const uint8_t *ep = dp + dlen; + int ii; - while (dp < ep) + while( bits_bytes_left( bb ) > 2 ) { - switch (dp[0]) + uint8_t tag = bits_get(bb, 8); + uint8_t len = bits_get(bb, 8); + switch ( tag ) { case 5: // Registration descriptor - stream->ts[esindx].format_id = (dp[2] << 24) | (dp[3] << 16) | - (dp[4] << 8) | dp[5]; + stream->pes.list[pes_idx].format_id = bits_get(bb, 32); + bits_skip(bb, 8 * (len - 4)); break; case 10: // ISO_639_language descriptor - stream->ts[esindx].a52_info.lang_code = lang_to_code(lang_for_code2((const char *)&dp[2])); - break; + { + char code[3]; + for (ii = 0; ii < 3; ii++) + { + code[ii] = bits_get(bb, 8); + } + stream->pes.list[pes_idx].lang_code = + lang_to_code(lang_for_code2(code)); + bits_skip(bb, 8 * (len - 3)); + } break; + + case 0x56: // DVB Teletext descriptor + { + // We don't currently process teletext from + // TS or PS streams. Set stream 'kind' to N + stream->pes.list[pes_idx].stream_type = 0x00; + stream->pes.list[pes_idx].stream_kind = N; + strncpy(stream->pes.list[pes_idx].codec_name, + "DVB Teletext", 80); + bits_skip(bb, 8 * len); + } break; + + case 0x59: // DVB Subtitleing descriptor + { + // We don't currently process subtitles from + // TS or PS streams. Set stream 'kind' to N + stream->pes.list[pes_idx].stream_type = 0x00; + stream->pes.list[pes_idx].stream_kind = N; + strncpy(stream->pes.list[pes_idx].codec_name, + "DVB Subtitling", 80); + bits_skip(bb, 8 * len); + } break; case 0x6a: // DVB AC-3 descriptor - stream->ts[esindx].stream_type = 0x81; - break; + { + stream->pes.list[pes_idx].stream_type = 0x81; + update_pes_kind( stream, pes_idx ); + bits_skip(bb, 8 * len); + } break; case 0x7a: // DVB EAC-3 descriptor - stream->ts[esindx].stream_type = 0x87; - break; + { + stream->pes.list[pes_idx].stream_type = 0x87; + update_pes_kind( stream, pes_idx ); + bits_skip(bb, 8 * len); + } break; default: + bits_skip(bb, 8 * len); break; } - dp += dp[1] + 2; } } int decode_program_map(hb_stream_t* stream) { bitbuf_t bb; - set_buf(&bb, stream->pmt_info.tablebuf, stream->pmt_info.tablepos, 0); - - get_bits(&bb, 8); // table_id - get_bits(&bb, 4); - unsigned int section_length = get_bits(&bb, 12); - stream->pmt_info.section_length = section_length; - - unsigned int program_number = get_bits(&bb, 16); - stream->pmt_info.program_number = program_number; - get_bits(&bb, 2); - get_bits(&bb, 5); // version_number - get_bits(&bb, 1); - get_bits(&bb, 8); // section_number - get_bits(&bb, 8); // last_section_number - get_bits(&bb, 3); - stream->pmt_info.PCR_PID = get_bits(&bb, 13); - get_bits(&bb, 4); - int program_info_length = get_bits(&bb, 12); - stream->pmt_info.program_info_length = program_info_length; + bits_init(&bb, stream->pmt_info.tablebuf, stream->pmt_info.tablepos, 0); + + bits_get(&bb, 8); // table_id + bits_get(&bb, 4); + unsigned int section_length = bits_get(&bb, 12); + + bits_get(&bb, 16); // program number + bits_get(&bb, 2); + bits_get(&bb, 5); // version_number + bits_get(&bb, 1); + bits_get(&bb, 8); // section_number + bits_get(&bb, 8); // last_section_number + bits_get(&bb, 3); + stream->pmt_info.PCR_PID = bits_get(&bb, 13); + bits_get(&bb, 4); + int program_info_length = bits_get(&bb, 12); int i; for (i = 0; i < program_info_length - 2; ) { uint8_t tag, len; - tag = get_bits(&bb, 8); - len = get_bits(&bb, 8); + tag = bits_get(&bb, 8); + len = bits_get(&bb, 8); i += 2; if ( i + len > program_info_length ) { @@ -2404,71 +2604,58 @@ int decode_program_map(hb_stream_t* stream) if (tag == 0x05 && len >= 4) { // registration descriptor - stream->pmt_info.reg_desc = get_bits(&bb, 32); + stream->reg_desc = bits_get(&bb, 32); i += 4; len -= 4; } int j; for ( j = 0; j < len; j++ ) { - get_bits(&bb, 8); + bits_get(&bb, 8); } i += len; } for ( ; i < program_info_length; i++ ) { - get_bits(&bb, 8); + bits_get(&bb, 8); } int cur_pos = 9 /* data after the section length field*/ + program_info_length; int done_reading_stream_types = 0; + int ii = 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++) + unsigned char stream_type = bits_get(&bb, 8); + bits_get(&bb, 3); + unsigned int elementary_PID = bits_get(&bb, 13); + bits_get(&bb, 4); + unsigned int info_len = bits_get(&bb, 12); + // 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. + int pes_idx; + update_ts_streams( stream, elementary_PID, 0, + stream_type, -1, &pes_idx ); + if ( pes_idx >= 0 ) + stream->pes.list[pes_idx].map_idx = ii; + if (info_len > 0) { - ES_info_buf[i] = get_bits(&bb, 8); - } - - if ( index_of_pid( stream, elementary_PID ) < 0 ) - { - // 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_pids; - if (i < kMaxNumberDecodeStreams) - { - stream->ts[i].pid = elementary_PID; - stream->ts[i].stream_type = stream_type; - stream->ts[i].number_substreams = 1; - stream->ts[i].substream_type[0] = 0; - if (ES_info_length > 0) - { - decode_element_descriptors(stream, i, ES_info_buf, - ES_info_length); - } - stream->ts[i].stream_kind = ts_stream_kind(stream, i); - ++stream->ts_number_pids; - } + bitbuf_t bb_desc; + bits_clone( &bb_desc, &bb, info_len ); + if ( pes_idx >= 0 ) + decode_element_descriptors( stream, pes_idx, &bb_desc ); + bits_skip(&bb, 8 * info_len); } - cur_pos += 5 /* stream header */ + ES_info_length; - - free(ES_info_buf); + cur_pos += 5 /* stream header */ + info_len; if (cur_pos >= section_length - 4 /* stop before the CRC */) done_reading_stream_types = 1; + ii++; } return 1; @@ -2523,7 +2710,7 @@ static int build_program_map(const uint8_t *buf, hb_stream_t *stream) { // We have enough to check the section length int length; - length = ((stream->pmt_info.tablebuf[1] << 8) + + length = ((stream->pmt_info.tablebuf[1] << 8) + stream->pmt_info.tablebuf[2]) & 0xFFF; if (stream->pmt_info.tablepos > length + 1) { @@ -2595,17 +2782,17 @@ static int decode_PAT(const uint8_t *buf, hb_stream_t *stream) //while (pos < tablepos) { bitbuf_t bb; - set_buf(&bb, tablebuf + pos, tablepos - pos, 0); - - unsigned char section_id = get_bits(&bb, 8); - get_bits(&bb, 4); - unsigned int section_len = get_bits(&bb, 12); - get_bits(&bb, 16); // transport_id - get_bits(&bb, 2); - get_bits(&bb, 5); // version_num - get_bits(&bb, 1); // current_next - get_bits(&bb, 8); // section_num - get_bits(&bb, 8); // last_section + bits_init(&bb, tablebuf + pos, tablepos - pos, 0); + + unsigned char section_id = bits_get(&bb, 8); + bits_get(&bb, 4); + unsigned int section_len = bits_get(&bb, 12); + bits_get(&bb, 16); // transport_id + bits_get(&bb, 2); + bits_get(&bb, 5); // version_num + bits_get(&bb, 1); // current_next + bits_get(&bb, 8); // section_num + bits_get(&bb, 8); // last_section switch (section_id) { @@ -2618,17 +2805,17 @@ static int decode_PAT(const uint8_t *buf, hb_stream_t *stream) stream->ts_number_pat_entries = 0; while ((curr_pos < section_len) && (stream->ts_number_pat_entries < kMaxNumberPMTStreams)) { - unsigned int pkt_program_num = get_bits(&bb, 16); + unsigned int pkt_program_num = bits_get(&bb, 16); stream->pat_info[stream->ts_number_pat_entries].program_number = pkt_program_num; - get_bits(&bb, 3); // Reserved + bits_get(&bb, 3); // Reserved if (pkt_program_num == 0) { - get_bits(&bb, 13); // pkt_network_id + bits_get(&bb, 13); // pkt_network_id } else { - unsigned int pkt_program_map_PID = get_bits(&bb, 13); + unsigned int pkt_program_map_PID = bits_get(&bb, 13); stream->pat_info[stream->ts_number_pat_entries].program_map_PID = pkt_program_map_PID; } curr_pos += 4; @@ -2654,6 +2841,1415 @@ static int decode_PAT(const uint8_t *buf, hb_stream_t *stream) return 1; } +// convert a PES PTS or DTS to an int64 +static int64_t parse_pes_timestamp( bitbuf_t *bb ) +{ + int64_t ts; + + ts = ( (uint64_t) bits_get(bb, 3) << 30 ) + + bits_skip(bb, 1) + + ( bits_get(bb, 15) << 15 ) + + bits_skip(bb, 1) + + bits_get(bb, 15); + bits_skip(bb, 1); + return ts; +} + +static int parse_pes_header( + hb_stream_t *stream, + bitbuf_t *bb, + hb_pes_info_t *pes_info ) +{ + if ( bits_bytes_left(bb) < 6 ) + { + return 0; + } + + bits_skip(bb, 8 * 4); + pes_info->packet_len = bits_get(bb, 16); + + /* + * This would normally be an error. But the decoders can generally + * recover well from missing data. So let the packet pass. + if ( bits_bytes_left(bb) < pes_info->packet_len ) + { + return 0; + } + */ + + int mark = bits_peek(bb, 2); + if ( mark == 0x02 ) + { + // mpeg2 pes + if ( bits_bytes_left(bb) < 3 ) + { + return 0; + } + + /* + bits_skip(bb, 2); + bits_get(bb, 2); // scrambling + bits_get(bb, 1); // priority + bits_get(bb, 1); // alignment + bits_get(bb, 1); // copyright + bits_get(bb, 1); // original + */ + bits_get(bb, 8); // skip all of the above + + int has_pts = bits_get(bb, 2); + int has_escr = bits_get(bb, 1); + int has_esrate = bits_get(bb, 1); + int has_dsm = bits_get(bb, 1); + int has_copy_info = bits_get(bb, 1); + int has_crc = bits_get(bb, 1); + int has_ext = bits_get(bb, 1); + int hdr_len = pes_info->header_len = bits_get(bb, 8); + pes_info->header_len += bb->pos >> 3; + + bitbuf_t bb_hdr; + bits_clone(&bb_hdr, bb, hdr_len); + + if ( bits_bytes_left(&bb_hdr) < hdr_len ) + { + 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 + + has_ext; + + if ( bits_bytes_left(&bb_hdr) < expect ) + { + return 0; + } + + if( has_pts ) + { + if ( bits_bytes_left(&bb_hdr) < 5 ) + { + return 0; + } + bits_skip(&bb_hdr, 4); + pes_info->pts = parse_pes_timestamp( &bb_hdr ); + if ( has_pts & 1 ) + { + if ( bits_bytes_left(&bb_hdr) < 5 ) + { + return 0; + } + bits_skip(&bb_hdr, 4); + pes_info->dts = parse_pes_timestamp( &bb_hdr ); + } + else + { + pes_info->dts = pes_info->pts; + } + } + + if ( has_escr ) + bits_skip(&bb_hdr, 8 * 6); + if ( has_esrate ) + bits_skip(&bb_hdr, 8 * 3); + if ( has_dsm ) + bits_skip(&bb_hdr, 8); + if ( has_copy_info ) + bits_skip(&bb_hdr, 8); + if ( has_crc ) + bits_skip(&bb_hdr, 8 * 2); + + if ( has_ext ) + { + int has_private = bits_get(&bb_hdr, 1); + int has_pack = bits_get(&bb_hdr, 1); + int has_counter = bits_get(&bb_hdr, 1); + int has_pstd = bits_get(&bb_hdr, 1); + bits_skip(&bb_hdr, 3); // reserved bits + int has_ext2 = bits_get(&bb_hdr, 1); + + expect = (has_private) * 16 + has_pack + has_counter * 2 + + has_pstd * 2 + has_ext2 * 2; + + if ( bits_bytes_left(&bb_hdr) < expect ) + { + return 0; + } + + if ( has_private ) + { + bits_skip(&bb_hdr, 8 * 16); + expect -= 2; + } + if ( has_pack ) + { + int len = bits_get(&bb_hdr, 8); + expect -= 1; + if ( bits_bytes_left(&bb_hdr) < len + expect ) + { + return 0; + } + bits_skip(&bb_hdr, 8 * len); + } + if ( has_counter ) + bits_skip(&bb_hdr, 8 * 2); + if ( has_pstd ) + bits_skip(&bb_hdr, 8 * 2); + + if ( has_ext2 ) + { + bits_skip(&bb_hdr, 1); // marker + bits_get(&bb_hdr, 7); // extension length + pes_info->has_stream_id_ext = !bits_get(&bb_hdr, 1); + if ( pes_info->has_stream_id_ext ) + pes_info->stream_id_ext = bits_get(&bb_hdr, 7); + } + } + // eat header stuffing + bits_skip(bb, 8 * hdr_len); + } + else + { + // mpeg1 pes + + // Skip stuffing + while ( bits_peek(bb, 1) && bits_bytes_left(bb) ) + bits_get(bb, 8); + + if ( !bits_bytes_left(bb) ) + return 0; + + // Skip std buffer info + int mark = bits_get(bb, 2); + if ( mark == 0x01 ) + { + if ( bits_bytes_left(bb) < 2 ) + return 0; + bits_skip(bb, 8 * 2); + } + + int has_pts = bits_get(bb, 2); + if( has_pts == 0x02 ) + { + pes_info->pts = parse_pes_timestamp( bb ); + pes_info->dts = pes_info->pts; + } + else if( has_pts == 0x03 ) + { + pes_info->pts = parse_pes_timestamp( bb ); + bits_skip(bb, 4); + pes_info->dts = parse_pes_timestamp( bb ); + } + else + { + bits_skip(bb, 8); // 0x0f flag + } + if ( bits_bytes_left(bb) < 0 ) + return 0; + pes_info->header_len = bb->pos >> 3; + } + if ( pes_info->stream_id == 0xbd && stream->hb_stream_type == program ) + { + if ( bits_bytes_left(bb) < 4 ) + { + return 0; + } + int ssid = bits_peek(bb, 8); + if( ( ssid >= 0xa0 && ssid <= 0xaf ) || + ( ssid >= 0x20 && ssid <= 0x2f ) ) + { + // DVD LPCM or DVD SPU (subtitles) + pes_info->bd_substream_id = bits_get(bb, 8); + pes_info->header_len += 1; + } + else if ( ssid >= 0xb0 && ssid <= 0xbf ) + { + // HD-DVD TrueHD has a 4 byte header + pes_info->bd_substream_id = bits_get(bb, 8); + bits_skip(bb, 8 * 4); + pes_info->header_len += 5; + } + else if( ( ssid >= 0x80 && ssid <= 0x9f ) || + ( ssid >= 0xc0 && ssid <= 0xcf ) ) + { + // AC3, E-AC3, DTS, and DTS-HD has 3 byte header + pes_info->bd_substream_id = bits_get(bb, 8); + bits_skip(bb, 8 * 3); + pes_info->header_len += 4; + } + } + return 1; +} + +static int parse_pack_header( + hb_stream_t *stream, + bitbuf_t *bb, + hb_pes_info_t *pes_info ) +{ + if ( bits_bytes_left(bb) < 12) + { + return 0; + } + + bits_skip(bb, 8 * 4); + int mark = bits_get(bb, 2); + + if ( mark == 0x00 ) + { + // mpeg1 pack + bits_skip(bb, 2); // marker + } + pes_info->scr = parse_pes_timestamp( bb ); + + if ( mark == 0x00 ) + { + bits_skip(bb, 24); + pes_info->header_len = (bb->pos >> 3); + } + else + { + bits_skip(bb, 39); + int stuffing = bits_get(bb, 3); + pes_info->header_len = stuffing; + pes_info->header_len += (bb->pos >> 3); + } + return 1; +} + +// Returns the length of the header +static int hb_parse_ps( + hb_stream_t *stream, + uint8_t *buf, + int len, + hb_pes_info_t *pes_info ) +{ + memset( pes_info, 0, sizeof( hb_pes_info_t ) ); + pes_info->pts = -1; + pes_info->dts = -1; + + bitbuf_t bb, cc; + bits_init(&bb, buf, len, 0); + bits_clone(&cc, &bb, len); + + if ( bits_bytes_left(&bb) < 4 ) + return 0; + + // Validate start code + if ( bits_get(&bb, 8 * 3) != 0x000001 ) + { + return 0; + } + + pes_info->stream_id = bits_get(&bb, 8); + if ( pes_info->stream_id == 0xb9 ) + { + // Program stream end code + return 1; + } + else if ( pes_info->stream_id == 0xba ) + { + return parse_pack_header( stream, &cc, pes_info ); + } + else if ( pes_info->stream_id >= 0xbd && + pes_info->stream_id != 0xbe && + pes_info->stream_id != 0xbf && + pes_info->stream_id != 0xf0 && + pes_info->stream_id != 0xf1 && + pes_info->stream_id != 0xf2 && + pes_info->stream_id != 0xf8 && + pes_info->stream_id != 0xff ) + { + return parse_pes_header( stream, &cc, pes_info ); + } + else + { + if ( bits_bytes_left(&bb) < 2 ) + { + return 0; + } + pes_info->packet_len = bits_get(&bb, 16); + pes_info->header_len = bb.pos >> 3; + return 1; + } +} + +static int hb_ps_read_packet( hb_stream_t * stream, hb_buffer_t *b ) +{ + // Appends to buffer if size != 0 + int start_code = -1; + int pos = b->size; + int stream_id = -1; + int c; + +#define cp (b->data) + flockfile( stream->file_handle ); + while ( ( c = getc_unlocked( stream->file_handle ) ) != EOF ) + { + start_code = ( start_code << 8 ) | c; + if ( ( start_code >> 8 )== 0x000001 ) + // we found the start of the next start + break; + } + if ( c == EOF ) + goto done; + + if ( pos + 4 > b->alloc ) + { + // need to expand the buffer + hb_buffer_realloc( b, b->alloc * 2 ); + } + cp[pos++] = ( start_code >> 24 ) & 0xff; + cp[pos++] = ( start_code >> 16 ) & 0xff; + cp[pos++] = ( start_code >> 8 ) & 0xff; + cp[pos++] = ( start_code ) & 0xff; + stream_id = start_code & 0xff; + + if ( stream_id == 0xba ) + { + int start = pos - 4; + // Read pack header + if ( pos + 21 >= b->alloc ) + { + // need to expand the buffer + hb_buffer_realloc( b, b->alloc * 2 ); + } + + // There are at least 8 bytes. More if this is mpeg2 pack. + fread( cp+pos, 1, 8, stream->file_handle ); + int mark = cp[pos] >> 4; + pos += 8; + + if ( mark != 0x02 ) + { + // mpeg-2 pack, + fread( cp+pos, 1, 2, stream->file_handle ); + pos += 2; + int len = cp[start+13] & 0x7; + fread( cp+pos, 1, len, stream->file_handle ); + pos += len; + } + } + // Non-video streams can emulate start codes, so we need + // to inspect PES packets and skip over their data + // sections to avoid mis-detection of the next pack or pes start code + else if ( stream_id >= 0xbb ) + { + int len = 0; + c = getc_unlocked( stream->file_handle ); + if ( c == EOF ) + goto done; + len = c << 8; + c = getc_unlocked( stream->file_handle ); + if ( c == EOF ) + goto done; + len |= c; + if ( pos + len + 2 > b->alloc ) + { + if ( b->alloc * 2 > pos + len + 2 ) + hb_buffer_realloc( b, b->alloc * 2 ); + else + hb_buffer_realloc( b, b->alloc * 2 + len + 2 ); + } + cp[pos++] = len >> 8; + cp[pos++] = len & 0xff; + if ( len ) + { + // Length is non-zero, read the packet all at once + len = fread( cp+pos, 1, len, stream->file_handle ); + pos += len; + } + else + { + // Length is zero, read bytes till we find a start code. + // Only video PES packets are allowed to have zero length. + start_code = -1; + while ( ( c = getc_unlocked( stream->file_handle ) ) != EOF ) + { + start_code = ( start_code << 8 ) | c; + if ( pos >= b->alloc ) + { + // need to expand the buffer + hb_buffer_realloc( b, b->alloc * 2 ); + } + cp[pos++] = c; + if ( ( start_code >> 8 ) == 0x000001 && + ( start_code & 0xff ) >= 0xb9 ) + { + // we found the start of the next start + break; + } + } + if ( c == EOF ) + goto done; + pos -= 4; + fseeko( stream->file_handle, -4, SEEK_CUR ); + } + } + else + { + // Unknown, find next start code + start_code = -1; + while ( ( c = getc_unlocked( stream->file_handle ) ) != EOF ) + { + start_code = ( start_code << 8 ) | c; + if ( pos >= b->alloc ) + { + // need to expand the buffer + hb_buffer_realloc( b, b->alloc * 2 ); + } + cp[pos++] = c; + if ( ( start_code >> 8 ) == 0x000001 && + ( start_code & 0xff ) >= 0xb9 ) + // we found the start of the next start + break; + } + if ( c == EOF ) + goto done; + pos -= 4; + fseeko( stream->file_handle, -4, SEEK_CUR ); + } +done: + // Parse packet for information we might need + funlockfile( stream->file_handle ); + int len = pos - b->size; + b->size = pos; +#undef cp + return len; +} + +static hb_buffer_t * hb_ps_stream_decode( hb_stream_t *stream ) +{ + hb_pes_info_t pes_info; + hb_buffer_t *buf = hb_buffer_init(HB_DVD_READ_BUFFER_SIZE); + + while (1) + { + buf->size = 0; + int len = hb_ps_read_packet( stream, buf ); + if ( len == 0 ) + { + // End of file + hb_buffer_close( &buf ); + return buf; + } + if ( !hb_parse_ps( stream, buf->data, buf->size, &pes_info ) ) + { + ++stream->errors; + continue; + } + // pack header + if ( pes_info.stream_id == 0xba ) + { + stream->pes.found_scr = 1; + stream->ts_flags |= TS_HAS_PCR; + stream->pes.scr = pes_info.scr; + continue; + } + + // If we don't have a SCR yet but the stream has SCRs just loop + // so we don't process anything until we have a clock reference. + if ( !stream->pes.found_scr && ( stream->ts_flags & TS_HAS_PCR ) ) + { + continue; + } + + // system header + if ( pes_info.stream_id == 0xbb ) + continue; + + int idx; + if ( pes_info.stream_id == 0xbd ) + { + idx = index_of_ps_stream( stream, pes_info.stream_id, + pes_info.bd_substream_id ); + } + else + { + idx = index_of_ps_stream( stream, pes_info.stream_id, + pes_info.stream_id_ext ); + } + + // Is this a stream carrying data that we care about? + if ( idx < 0 ) + continue; + + switch (stream->pes.list[idx].stream_kind) + { + case A: + buf->type = AUDIO_BUF; + break; + + case V: + buf->type = VIDEO_BUF; + break; + + default: + buf->type = OTHER_BUF; + break; + } + + if ( stream->need_keyframe ) + { + // we're looking for the first video frame because we're + // doing random access during 'scan' + if ( buf->type == VIDEO_BUF ) + stream->recovery_frames = isIframe( stream, buf->data, buf->size ); + if ( buf->type != VIDEO_BUF || !stream->recovery_frames ) + { + // not the video stream or didn't find an I frame + // but we'll only wait 255 video frames for an I frame. + if ( buf->type != VIDEO_BUF || ++stream->need_keyframe < 512 ) + { + continue; + } + } + stream->need_keyframe = 0; + } + if ( buf->type == VIDEO_BUF ) + ++stream->frames; + + buf->id = get_id( &stream->pes.list[idx] ); + buf->pcr = stream->pes.scr; + buf->start = pes_info.pts; + buf->renderOffset = pes_info.dts; + memmove( buf->data, buf->data + pes_info.header_len, + buf->size - pes_info.header_len ); + buf->size -= pes_info.header_len; + if ( buf->size == 0 ) + continue; + stream->pes.scr = -1; + return buf; + } +} + +static int update_ps_streams( hb_stream_t * stream, int stream_id, int stream_id_ext, int stream_type, int in_kind ) +{ + int ii; + int same_stream = -1; + kind_t kind = in_kind == -1 ? st2codec[stream_type].kind : in_kind; + + for ( ii = 0; ii < stream->pes.count; ii++ ) + { + if ( stream->pes.list[ii].stream_id == stream_id ) + same_stream = ii; + + if ( stream->pes.list[ii].stream_id == stream_id && + stream->pes.list[ii].stream_id_ext == 0 && + stream->pes.list[ii].stream_kind == U ) + { + // This is an unknown stream type that hasn't been + // given a stream_id_ext. So match only to stream_id + // + // is the stream_id_ext being updated? + if ( stream_id_ext != 0 ) + break; + + // If stream is already in the list and the new 'kind' is + // PCR, Unknown, or same as before, just return the index + // to the entry found. + if ( kind == P || kind == U || kind == stream->pes.list[ii].stream_kind ) + return ii; + // Update stream_type and kind + break; + } + if ( stream_id == stream->pes.list[ii].stream_id && + stream_id_ext == stream->pes.list[ii].stream_id_ext ) + { + // If stream is already in the list and the new 'kind' is + // PCR and the old 'kind' is unknown, set the new 'kind' + if ( kind == P && stream->pes.list[ii].stream_kind == U ) + break; + + // If stream is already in the list and the new 'kind' is + // PCR, Unknown, or same as before, just return the index + // to the entry found. + if ( kind == P || kind == U || kind == stream->pes.list[ii].stream_kind ) + return ii; + // Replace unknown 'kind' with known 'kind' + break; + } + // Resolve multiple videos + if ( kind == V && stream->pes.list[ii].stream_kind == V ) + { + if ( stream_id <= stream->pes.list[ii].stream_id && + stream_id_ext <= stream->pes.list[ii].stream_id_ext ) + { + // 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 + // debug and informational purposes. + int jj = new_pes( stream ); + memcpy( &stream->pes.list[jj], &stream->pes.list[ii], + sizeof( hb_pes_stream_t ) ); + break; + } + } + } + + if ( ii == stream->pes.count ) + { + ii = new_pes( stream ); + if ( same_stream >= 0 ) + { + memcpy( &stream->pes.list[ii], &stream->pes.list[same_stream], + sizeof( hb_pes_stream_t ) ); + } + else + { + stream->pes.list[ii].map_idx = -1; + } + } + + stream->pes.list[ii].stream_id = stream_id; + stream->pes.list[ii].stream_id_ext = stream_id_ext; + stream->pes.list[ii].stream_type = stream_type; + stream->pes.list[ii].stream_kind = kind; + return ii; +} + +static void update_pes_kind( hb_stream_t * stream, int idx ) +{ + kind_t kind = st2codec[stream->pes.list[idx].stream_type].kind; + if ( kind != U && kind != N ) + { + stream->pes.list[idx].stream_kind = kind; + } +} + +static void ts_pes_list_add( hb_stream_t *stream, int ts_idx, int pes_idx ) +{ + int ii = stream->ts.list[ts_idx].pes_list; + if ( ii == -1 ) + { + stream->ts.list[ts_idx].pes_list = pes_idx; + return; + } + + int idx; + while ( ii != -1 ) + { + if ( ii == pes_idx ) // Already in list + return; + idx = ii; + ii = stream->pes.list[ii].next; + } + stream->pes.list[idx].next = pes_idx; +} + +static int update_ts_streams( hb_stream_t * stream, int pid, int stream_id_ext, int stream_type, int in_kind, int *out_pes_idx ) +{ + int ii; + int pes_idx = update_ps_streams( stream, pid, stream_id_ext, + stream_type, in_kind ); + + if ( out_pes_idx ) + *out_pes_idx = pes_idx; + + if ( pes_idx < 0 ) + return -1; + + kind_t kind = stream->pes.list[pes_idx].stream_kind; + for ( ii = 0; ii < stream->ts.count; ii++ ) + { + if ( pid == stream->ts.list[ii].pid ) + { + break; + } + // Resolve multiple videos + 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], + sizeof( hb_ts_stream_t ) ); + break; + } + } + if ( ii == stream->ts.count ) + ii = new_pid( stream ); + + stream->ts.list[ii].pid = pid; + ts_pes_list_add( stream, ii, pes_idx ); + if ( in_kind == P ) + stream->ts.list[ii].is_pcr = 1; + + return ii; +} + +static int decode_ps_map( hb_stream_t * stream, uint8_t *buf, int len ) +{ + int retval = 1; + bitbuf_t bb; + bits_init(&bb, buf, len, 0); + + if ( bits_bytes_left(&bb) < 10 ) + return 0; + + // Skip stuff not needed + bits_skip(&bb, 8 * 8); + int info_len = bits_get(&bb, 16); + if ( bits_bytes_left(&bb) < info_len ) + return 0; + + if ( info_len ) + { + bitbuf_t cc; + bits_clone( &cc, &bb, info_len ); + + while ( bits_bytes_left(&cc) >= 2 ) + { + uint8_t tag, len; + + tag = bits_get(&cc, 8); + len = bits_get(&cc, 8); + if ( bits_bytes_left(&cc) < len ) + return 0; + + if (tag == 0x05 && len >= 4) + { + // registration descriptor + stream->reg_desc = bits_get(&cc, 32); + bits_skip(&cc, 8 * (len - 4)); + } + else + { + bits_skip(&cc, 8 * len); + } + } + bits_skip(&bb, 8 * info_len); + } + + int map_len = bits_get(&bb, 16); + if ( bits_bytes_left(&bb) < map_len ) + return 0; + + // Process the map + int ii = 0; + while ( bits_bytes_left(&bb) >= 8 ) + { + int pes_idx; + int stream_type = bits_get(&bb, 8); + int stream_id = bits_get(&bb, 8); + info_len = bits_get(&bb, 16); + if ( info_len > bits_bytes_left(&bb) ) + return 0; + + int substream_id = 0; + switch ( stream_type ) + { + case 0x81: // ac3 + case 0x82: // dts + case 0x83: // lpcm + case 0x87: // eac3 + // If the stream_id isn't one of the standard mpeg + // stream ids, assume it is an private stream 1 substream id. + // 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 + // stream and differs from how everyone else specifies + // this type of audio. + if ( stream_id < 0xb9 ) + { + substream_id = stream_id; + stream_id = 0xbd; + } + break; + default: + break; + } + + pes_idx = update_ps_streams( stream, stream_id, substream_id, + stream_type, -1 ); + if ( pes_idx >= 0 ) + stream->pes.list[pes_idx].map_idx = ii; + + if ( info_len > 0 ) + { + bitbuf_t bb_desc; + bits_clone( &bb_desc, &bb, info_len ); + if ( pes_idx >= 0 ) + decode_element_descriptors( stream, pes_idx, &bb_desc ); + bits_skip(&bb, 8 * info_len); + } + ii++; + } + // skip CRC 32 + return retval; +} + +static void hb_ps_stream_find_streams(hb_stream_t *stream) +{ + int ii, jj; + hb_buffer_t *buf = hb_buffer_init(HB_DVD_READ_BUFFER_SIZE); + + fseeko( stream->file_handle, 0, SEEK_SET ); + // Scan beginning of file, then if no program stream map is found + // seek to 20% and scan again since there's occasionally no + // audio at the beginning (particularly for vobs). + for ( ii = 0; ii < 2; ii++ ) + { + for ( jj = 0; jj < MAX_PS_PROBE_SIZE; jj += buf->size ) + { + int stream_type; + int len; + + hb_pes_info_t pes_info; + buf->size = 0; + len = hb_ps_read_packet( stream, buf ); + if ( len == 0 ) + { + // Must have reached EOF + break; + } + if ( !hb_parse_ps( stream, buf->data, buf->size, &pes_info ) ) + { + hb_deep_log( 2, "hb_ps_stream_find_streams: Error parsing PS packet"); + continue; + } + if ( pes_info.stream_id == 0xba ) + { + stream->ts_flags |= TS_HAS_PCR; + } + else if ( pes_info.stream_id == 0xbc ) + { + // program stream map + // Note that if there is a program map, any + // 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 ) ) + { + hb_log("Found program stream map"); + goto done; + } + else + { + hb_error("Error parsing program stream map"); + } + } + else if ( ( pes_info.stream_id & 0xe0 ) == 0xc0 ) + { + // MPeg audio (c0 - df) + stream_type = 0x04; + update_ps_streams( stream, pes_info.stream_id, + pes_info.stream_id_ext, stream_type, -1 ); + } + else if ( pes_info.stream_id == 0xbd ) + { + int ssid = pes_info.bd_substream_id; + // Add a potentail audio stream + // Check dvd substream id + if ( ssid >= 0x20 && ssid <= 0x37 ) + { + // Skip dvd subtitles + int idx = update_ps_streams( stream, pes_info.stream_id, + pes_info.bd_substream_id, 0, -1 ); + stream->pes.list[idx].stream_kind = N; + strncpy(stream->pes.list[idx].codec_name, + "DVD Subtitle", 80); + continue; + } + if ( ssid >= 0x80 && ssid <= 0x87 ) + { + stream_type = 0x81; // ac3 + } + else if ( ( ssid >= 0x88 && ssid <= 0x8f ) || + ( ssid >= 0x98 && ssid <= 0x9f ) ) + { + // Could be either dts or dts-hd + // will have to probe to resolve + int idx = update_ps_streams( stream, pes_info.stream_id, + pes_info.bd_substream_id, 0, U ); + stream->pes.list[idx].codec = HB_ACODEC_DCA_HD; + stream->pes.list[idx].codec_param = CODEC_ID_DTS; + continue; + } + else if ( ssid >= 0xa0 && ssid <= 0xaf ) + { + stream_type = 0x83; // lpcm + // This is flagged as an unknown stream type in + // st2codec because it can be either LPCM or + // BD TrueHD. In this case it is LPCM. + update_ps_streams( stream, pes_info.stream_id, + pes_info.bd_substream_id, stream_type, A ); + continue; + } + else if ( ssid >= 0xb0 && ssid <= 0xbf ) + { + // HD-DVD TrueHD + int idx = update_ps_streams( stream, pes_info.stream_id, + pes_info.bd_substream_id, 0, A ); + stream->pes.list[idx].codec = HB_ACODEC_FFMPEG; + stream->pes.list[idx].codec_param = CODEC_ID_TRUEHD; + continue; + } + else if ( ssid >= 0xc0 && ssid <= 0xcf ) + { + // 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, + buf->size - pes_info.header_len, 0); + int sync = bits_get(&bb, 16); + if ( sync == 0x0b77 ) + { + bits_skip(&bb, 24); + int bsid = bits_get(&bb, 5); + if ( bsid <= 10 ) + { + // ac3 + stream_type = 0x81; // ac3 + } + else + { + // eac3 + stream_type = 0x87; // eac3 + } + } + else + { + // Doesn't look like an ac3 stream. Probe it. + stream_type = 0x00; + } + } + else + { + // Unknown. Probe it. + stream_type = 0x00; + } + update_ps_streams( stream, pes_info.stream_id, + pes_info.bd_substream_id, stream_type, -1 ); + } + 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 + // this for other types of video. + // + // Also, the hddvd tards decided to use 0xe2 and 0xe3 for + // h.264 video :( and the twits decided not to put a + // program stream map in the stream :'( + // + // So set this to an unknown stream type and probe. + stream_type = 0x00; + update_ps_streams( stream, pes_info.stream_id, + pes_info.stream_id_ext, stream_type, -1 ); + } + else if ( pes_info.stream_id == 0xfd ) + { + if ( pes_info.stream_id_ext == 0x55 || + pes_info.stream_id_ext == 0x56 ) + { + // hddvd uses this for vc-1. + stream_type = 0xea; + } + else + { + // mark as unknown and probe. + stream_type = 0x00; + } + update_ps_streams( stream, pes_info.stream_id, + pes_info.stream_id_ext, stream_type, -1 ); + } + } + hb_stream_seek( stream, 0.2 ); + } +done: + hb_buffer_close( &buf ); +} + +static int probe_dts_profile( hb_pes_stream_t *pes ) +{ + hb_work_info_t info; + hb_work_object_t *w = hb_codec_decoder( pes->codec ); + + w->codec_param = pes->codec_param; + int ret = w->bsinfo( w, pes->probe_buf, &info ); + if ( ret < 0 ) + { + hb_log( "probe_dts_profile: no info type %d/0x%x for id 0x%x", + pes->codec, pes->codec_param, pes->stream_id ); + + } + switch (info.profile) + { + case FF_PROFILE_DTS: + case FF_PROFILE_DTS_96_24: + pes->codec = HB_ACODEC_DCA; + pes->stream_type = 0x82; + pes->stream_kind = A; + break; + + case FF_PROFILE_DTS_ES: + pes->stream_type = 0; + pes->stream_kind = A; + break; + + case FF_PROFILE_DTS_HD_HRA: + pes->stream_type = 0; + pes->stream_kind = A; + break; + + case FF_PROFILE_DTS_HD_MA: + pes->stream_type = 0; + pes->stream_kind = A; + break; + + default: + return 0; + } + const char *profile_name; + AVCodec *codec = avcodec_find_decoder( pes->codec_param ); + profile_name = av_get_profile_name( codec, info.profile ); + if ( profile_name ) + { + strncpy(pes->codec_name, profile_name, 80); + pes->codec_name[79] = 0; + } + return 1; +} + +static int do_probe( hb_pes_stream_t *pes, hb_buffer_t *buf ) +{ + // Check upper limit of per stream data to probe + if ( pes->probe_buf == NULL ) + { + pes->probe_buf = hb_buffer_init( 0 ); + } + if ( pes->probe_buf->size > HB_MAX_PROBE_SIZE ) + { + pes->stream_kind = N; + hb_buffer_close( &pes->probe_buf ); + return 1; + } + + // Add this stream buffer to probe buffer and perform probe + AVInputFormat *fmt = NULL; + int score = 0; + AVProbeData pd = {0,}; + int size = pes->probe_buf->size + buf->size; + + hb_buffer_realloc(pes->probe_buf, size + AVPROBE_PADDING_SIZE ); + memcpy( pes->probe_buf->data + pes->probe_buf->size, buf->data, buf->size ); + pes->probe_buf->size = size; + + if ( pes->codec == HB_ACODEC_DCA_HD ) + { + // We need to probe for the profile of DTS audio in this stream. + return probe_dts_profile( pes ); + } + + // Probing is slow, so we don't want to re-probe the probe + // buffer for every packet we add to it. Grow the buffer + // by a factor of 2 before probing again. + if ( pes->probe_buf->size < pes->probe_next_size ) + return 0; + + pes->probe_next_size = pes->probe_buf->size * 2; + pd.buf = pes->probe_buf->data; + pd.buf_size = pes->probe_buf->size; + fmt = av_probe_input_format2( &pd, 1, &score ); + if ( fmt && score > AVPROBE_SCORE_MAX / 2 ) + { + AVCodec *codec = avcodec_find_decoder_by_name( fmt->name ); + if ( codec ) + { + pes->codec_param = codec->id; + if ( codec->type == AVMEDIA_TYPE_VIDEO ) + { + pes->stream_kind = V; + switch ( codec->id ) + { + case CODEC_ID_MPEG1VIDEO: + pes->codec = WORK_DECMPEG2; + pes->stream_type = 0x01; + break; + + case CODEC_ID_MPEG2VIDEO: + pes->codec = WORK_DECMPEG2; + pes->stream_type = 0x02; + break; + + case CODEC_ID_H264: + pes->codec = WORK_DECAVCODECV; + pes->stream_type = 0x1b; + break; + + case CODEC_ID_VC1: + pes->codec = WORK_DECAVCODECV; + pes->stream_type = 0xea; + break; + + default: + pes->codec = WORK_DECAVCODECV; + } + } + else if ( codec->type == AVMEDIA_TYPE_AUDIO ) + { + pes->stream_kind = A; + switch ( codec->id ) + { + case CODEC_ID_AC3: + pes->codec = HB_ACODEC_AC3; + break; + default: + pes->codec = HB_ACODEC_FFMPEG; + } + } + else + { + pes->stream_kind = N; + } + strncpy(pes->codec_name, fmt->name, 79); + pes->codec_name[79] = 0; + } + else + { + pes->stream_kind = N; + } + hb_buffer_close( &pes->probe_buf ); + return 1; + } + return 0; +} + +static void hb_ts_resolve_pid_types(hb_stream_t *stream) +{ + int ii, probe = 0; + + for ( ii = 0; ii < stream->ts.count; ii++ ) + { + int pid = stream->ts.list[ii].pid; + int stype = ts_stream_type( stream, ii ); + int pes_idx; + + if ( stype == 0x80 && + stream->reg_desc == STR4_TO_UINT32("HDMV") ) + { + // LPCM audio in bluray have an stype of 0x80 + // 0x80 is used for other DigiCipher normally + // To distinguish, Bluray streams have a reg_desc of HDMV + update_ts_streams( stream, pid, 0, stype, A, &pes_idx ); + stream->pes.list[pes_idx].codec = HB_ACODEC_FFMPEG; + stream->pes.list[pes_idx].codec_param = CODEC_ID_PCM_BLURAY; + continue; + } + + // The blu ray consortium apparently forgot to read the portion + // of the MPEG spec that says one PID should map to one media + // stream and multiplexed multiple types of audio into one PID + // using the extended stream identifier of the PES header to + // distinguish them. So we have to check if that's happening and + // if so tell the runtime what esid we want. + if ( stype == 0x83 && + stream->reg_desc == STR4_TO_UINT32("HDMV") ) + { + // This is an interleaved TrueHD/AC-3 stream and the esid of + // the AC-3 is 0x76 + update_ts_streams( stream, pid, HB_SUBSTREAM_BD_AC3, + stype, A, &pes_idx ); + stream->pes.list[pes_idx].codec = HB_ACODEC_AC3; + + update_ts_streams( stream, pid, HB_SUBSTREAM_BD_TRUEHD, + stype, A, &pes_idx ); + stream->pes.list[pes_idx].codec = HB_ACODEC_FFMPEG; + stream->pes.list[pes_idx].codec_param = CODEC_ID_TRUEHD; + continue; + } + if ( ( stype == 0x84 || stype == 0xa1 ) && + stream->reg_desc == STR4_TO_UINT32("HDMV") ) + { + // EAC3 audio in bluray has an stype of 0x84 + // which conflicts with SDDS + // To distinguish, Bluray streams have a reg_desc of HDMV + update_ts_streams( stream, pid, 0, stype, A, &pes_idx ); + stream->pes.list[pes_idx].codec = HB_ACODEC_FFMPEG; + stream->pes.list[pes_idx].codec_param = CODEC_ID_EAC3; + continue; + } + if ( ( stype == 0x85 || stype == 0xa2 ) && + stream->reg_desc == STR4_TO_UINT32("HDMV") ) + { + // DTS-HD HRA audio in bluray has an stype of 0x85 + // which conflicts with ATSC Program ID + // To distinguish, Bluray streams have a reg_desc of HDMV + // This is an interleaved DTS-HD HRA/DTS stream and the + // esid of the DTS is 0x71 + update_ts_streams( stream, pid, HB_SUBSTREAM_BD_DTS, + stype, A, &pes_idx ); + stream->pes.list[pes_idx].codec = HB_ACODEC_DCA; + + update_ts_streams( stream, pid, 0, stype, A, &pes_idx ); + stream->pes.list[pes_idx].codec = HB_ACODEC_DCA_HD; + stream->pes.list[pes_idx].codec_param = CODEC_ID_DTS; + continue; + } + if ( stype == 0x86 && + stream->reg_desc == STR4_TO_UINT32("HDMV") ) + { + // This is an interleaved DTS-HD MA/DTS stream and the + // esid of the DTS is 0x71 + update_ts_streams( stream, pid, HB_SUBSTREAM_BD_DTS, + stype, A, &pes_idx ); + stream->pes.list[pes_idx].codec = HB_ACODEC_DCA; + + update_ts_streams( stream, pid, 0, stype, A, &pes_idx ); + stream->pes.list[pes_idx].codec = HB_ACODEC_DCA_HD; + stream->pes.list[pes_idx].codec_param = CODEC_ID_DTS; + continue; + } + + // stype == 0 indicates a type not in st2codec table + if ( stype != 0 && + ( ts_stream_kind( stream, ii ) == A || + ts_stream_kind( stream, ii ) == V ) ) + { + // Assuming there are no substreams. + // This should be true before probing. + // This function is only called before + // probing. + pes_idx = stream->ts.list[ii].pes_list; + stream->pes.list[pes_idx].codec = st2codec[stype].codec; + stream->pes.list[pes_idx].codec_param = st2codec[stype].codec_param; + continue; + } + + if ( ts_stream_kind( stream, ii ) == U ) + { + probe++; + } + } + + // Probe remaining unknown streams for stream types + hb_stream_seek( stream, 0.0 ); + stream->need_keyframe = 0; + + int total_size = 0; + hb_buffer_t *buf; + + if ( probe ) + hb_log("Probing %d unknown stream%s", probe, probe > 1 ? "s" : "" ); + + while ( probe && ( buf = hb_ts_stream_decode( stream ) ) != NULL ) + { + // Check upper limit of total data to probe + total_size += buf->size; + + if ( total_size > HB_MAX_PROBE_SIZE * 2 ) + break; + + int idx; + idx = index_of_id( stream, buf->id ); + + if (idx < 0 || stream->pes.list[idx].stream_kind != U ) + continue; + + hb_pes_stream_t *pes = &stream->pes.list[idx]; + + if ( do_probe( pes, buf ) ) + { + probe--; + if ( pes->stream_kind != N ) + { + hb_log(" Probe: Found stream %s. stream id 0x%x-0x%x", + pes->codec_name, pes->stream_id, pes->stream_id_ext); + } + else + { + hb_log(" Probe: Unsupported stream %s. stream id 0x%x-0x%x", + pes->codec_name, pes->stream_id, pes->stream_id_ext); + } + } + } + // Clean up any probe buffers and set all remaining unknown + // streams to 'kind' N + for ( ii = 0; ii < stream->pes.count; ii++ ) + { + if ( stream->pes.list[ii].stream_kind == U ) + stream->pes.list[ii].stream_kind = N; + hb_buffer_close( &stream->pes.list[ii].probe_buf ); + stream->pes.list[ii].probe_next_size = 0; + } +} + +static void hb_ps_resolve_stream_types(hb_stream_t *stream) +{ + int ii, probe = 0; + + for ( ii = 0; ii < stream->pes.count; ii++ ) + { + int stype = stream->pes.list[ii].stream_type; + + // stype == 0 indicates a type not in st2codec table + if ( stype != 0 && + ( stream->pes.list[ii].stream_kind == A || + stream->pes.list[ii].stream_kind == V ) ) + { + stream->pes.list[ii].codec = st2codec[stype].codec; + stream->pes.list[ii].codec_param = st2codec[stype].codec_param; + continue; + } + + if ( stream->pes.list[ii].stream_kind == U ) + { + probe++; + } + } + + // Probe remaining unknown streams for stream types + hb_stream_seek( stream, 0.0 ); + stream->need_keyframe = 0; + + int total_size = 0; + hb_buffer_t *buf; + + if ( probe ) + hb_log("Probing %d unknown stream%s", probe, probe > 1 ? "s" : "" ); + + while ( probe && ( buf = hb_ps_stream_decode( stream ) ) != NULL ) + { + // Check upper limit of total data to probe + total_size += buf->size; + + if ( total_size > HB_MAX_PROBE_SIZE * 2 ) + break; + + int idx; + idx = index_of_id( stream, buf->id ); + + if (idx < 0 || stream->pes.list[idx].stream_kind != U ) + continue; + + hb_pes_stream_t *pes = &stream->pes.list[idx]; + + if ( do_probe( pes, buf ) ) + { + probe--; + if ( pes->stream_kind != N ) + { + hb_log(" Probe: Found stream %s. stream id 0x%x-0x%x", + pes->codec_name, pes->stream_id, pes->stream_id_ext); + } + else + { + hb_log(" Probe: Unsupported stream %s. stream id 0x%x-0x%x", + pes->codec_name, pes->stream_id, pes->stream_id_ext); + } + } + } + // Clean up any probe buffers and set all remaining unknown + // streams to 'kind' N + for ( ii = 0; ii < stream->pes.count; ii++ ) + { + if ( stream->pes.list[ii].stream_kind == U ) + stream->pes.list[ii].stream_kind = N; + hb_buffer_close( &stream->pes.list[ii].probe_buf ); + stream->pes.list[ii].probe_next_size = 0; + } +} + + static void hb_ts_stream_find_pids(hb_stream_t *stream) { // To be different from every other broadcaster in the world, New Zealand TV @@ -2690,16 +4286,21 @@ static void hb_ts_stream_find_pids(hb_stream_t *stream) int pat_index = 0; for (pat_index = 0; pat_index < stream->ts_number_pat_entries; pat_index++) { - // There are some streams where the PAT table has multiple entries as if their are - // multiple programs in the same transport stream, and yet there's actually only one - // program really in the stream. This seems to be true for transport streams that - // originate in the HDHomeRun but have been output by EyeTV's export utility. What I think - // is happening is that the HDHomeRun is sending the entire transport stream as broadcast, - // but the EyeTV is only recording a single (selected) program number and not rewriting the - // PAT info on export to match what's actually on the stream. - // Until we have a way of handling multiple programs per transport stream elegantly we'll match - // on the first pat entry for which we find a matching program map PID. The ideal solution would - // be to build a title choice popup from the PAT program number details and then select from + // There are some streams where the PAT table has multiple + // entries as if their are multiple programs in the same + // transport stream, and yet there's actually only one + // program really in the stream. This seems to be true for + // transport streams that originate in the HDHomeRun but have + // been output by EyeTV's export utility. What I think is + // happening is that the HDHomeRun is sending the entire + // transport stream as broadcast, but the EyeTV is only + // recording a single (selected) program number and not + // rewriting the PAT info on export to match what's actually + // on the stream. Until we have a way of handling multiple + // programs per transport stream elegantly we'll match on the + // first pat entry for which we find a matching program map PID. + // The ideal solution would be to build a title choice popup + // from the PAT program number details and then select from // their - but right now the API's not capable of that. if (stream->pat_info[pat_index].program_number != 0 && pid == stream->pat_info[pat_index].program_map_PID) @@ -2709,36 +4310,10 @@ static void hb_ts_stream_find_pids(hb_stream_t *stream) } } // Keep going until we have a complete set of PIDs - if ( index_of_video( stream ) >= 0 ) + if ( ts_index_of_video( stream ) >= 0 ) break; } - - hb_log("hb_ts_stream_find_pids - found the following PIDS"); - hb_log(" Video PIDS : "); - int i; - for (i=0; i < stream->ts_number_pids; i++) - { - if ( stream->ts[i].stream_kind == V ) - { - hb_log( " 0x%x type %s (0x%x)", - stream->ts[i].pid, - stream_type_name(stream->pmt_info.reg_desc, - stream->ts[i].stream_type), - stream->ts[i].stream_type); - } - } - hb_log(" Audio PIDS : "); - for (i = 0; i < stream->ts_number_pids; i++) - { - if ( stream->ts[i].stream_kind != V ) - { - hb_log( " 0x%x type %s (0x%x)", - stream->ts[i].pid, - stream_type_name(stream->pmt_info.reg_desc, - stream->ts[i].stream_type), - stream->ts[i].stream_type ); - } - } + update_ts_streams( stream, stream->pmt_info.PCR_PID, 0, -1, P, NULL ); } @@ -2758,127 +4333,141 @@ static void fwrite64( hb_buffer_t * buf, void *data, int len ) } // convert a PES PTS or DTS to an int64 -static int64_t pes_timestamp( const uint8_t *pes ) +static int64_t pes_timestamp( const uint8_t *buf ) { - int64_t ts = ( (uint64_t)(pes[0] & 0xe ) << 29 ); - ts |= ( pes[1] << 22 ) | ( ( pes[2] >> 1 ) << 15 ) | - ( pes[3] << 7 ) | ( pes[4] >> 1 ); + int64_t ts; + + ts = ( (uint64_t) ( buf[0] & 0x0e ) << 29 ) + + ( buf[1] << 22 ) + + ( ( buf[2] >> 1 ) << 15 ) + + ( buf[3] << 7 ) + + ( buf[4] >> 1 ); return ts; } static hb_buffer_t * generate_output_data(hb_stream_t *stream, int curstream) { hb_buffer_t *buf = NULL, *first = NULL; - int substream, i; + 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 ) ) + { + b->size = 0; + return NULL; + } + + uint8_t *tdat = b->data; + int size = b->size - pes_info.header_len; - for ( i = 0; i < stream->ts[curstream].number_substreams; i++ ) + if ( size <= 0 ) { - substream = stream->ts[curstream].substream_type[i]; - if ( substream == 0 || - substream == stream->ts[curstream].current_substream ) + b->size = 0; + return NULL; + } + + // Check all substreams to see if this packet matches + int pes_idx; + for ( pes_idx = stream->ts.list[curstream].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 ) { - uint8_t *tdat = stream->ts[curstream].buf->data; - int hlen = tdat[8] + 9; - int size = stream->ts[curstream].buf->size - hlen; + continue; + } + // The substreams match. + // Note that when stream->pes.list[pes_idx].stream_id_ext == 0, + // we want the whole TS stream including all substreams. + // DTS-HD is an example of this. - if ( size <= 0 ) - continue; + if ( first == NULL ) + first = buf = hb_buffer_init( size ); + else + { + hb_buffer_t *tmp = hb_buffer_init( size ); + buf->next = tmp; + buf = tmp; + } - if ( first == NULL ) - { - first = buf = hb_buffer_init( size ); - } - else - { - hb_buffer_t * tmp; - tmp = hb_buffer_init( size ); - buf->next = tmp; - buf = tmp; - } - buf->size = 0; + buf->size = 0; - buf->id = (substream << 16) | stream->ts[curstream].pid; - switch (stream->ts[curstream].stream_kind) - { - case A: - buf->type = AUDIO_BUF; - break; + buf->id = get_id( &stream->pes.list[pes_idx] ); + switch (stream->pes.list[pes_idx].stream_kind) + { + case A: + buf->type = AUDIO_BUF; + break; - case V: - buf->type = VIDEO_BUF; - break; + case V: + buf->type = VIDEO_BUF; + break; - default: - buf->type = OTHER_BUF; - break; - } + default: + buf->type = OTHER_BUF; + break; + } - // check if this packet was referenced to an older pcr and if that - // pcr was significantly different than the one we're using now. - // (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. - int64_t bufpcr = stream->ts[curstream].buf->pcr; - int64_t curpcr = stream->ts_pcr_current; - if ( stream->ts[curstream].buf->cur < stream->ts_pcr_out && - bufpcr != -1 && curpcr != -1 && curpcr - bufpcr > 200*90LL ) - { - // 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->start = -1; - buf->renderOffset = -1; - buf->stop = -1; - buf->pcr = -1; + // check if this packet was referenced to an older pcr and if that + // pcr was significantly different than the one we're using now. + // (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. + int64_t bufpcr = b->pcr; + int64_t curpcr = stream->ts.pcr_current; + if ( b->cur < stream->ts.pcr_out && + bufpcr != -1 && curpcr != -1 && curpcr - bufpcr > 200*90LL ) + { + // 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->start = -1; + buf->renderOffset = -1; + buf->stop = -1; + buf->pcr = -1; + } + else + { + if ( stream->ts.pcr_out != stream->ts.pcr_in ) + { + // we have a new pcr + stream->ts.pcr_out = stream->ts.pcr_in; + buf->pcr = stream->ts.pcr; + stream->ts.pcr_current = stream->ts.pcr; } else { - if ( stream->ts_pcr_out != stream->ts_pcr_in ) - { - // we have a new pcr - stream->ts_pcr_out = stream->ts_pcr_in; - buf->pcr = stream->ts_pcr; - stream->ts_pcr_current = stream->ts_pcr; - } - else - { - buf->pcr = -1; - } - - // put the PTS & possible DTS into 'start' & 'renderOffset' then strip - // off the PES header. - if ( tdat[7] & 0xc0 ) - { - buf->start = pes_timestamp( tdat + 9 ); - buf->renderOffset = ( tdat[7] & 0x40 )? pes_timestamp( tdat + 14 ) : - buf->start; - } - else - { - buf->start = -1; - buf->renderOffset = -1; - } + buf->pcr = -1; } - fwrite64( buf, tdat + hlen, size ); + // put the PTS & possible DTS into 'start' & 'renderOffset' + // then strip off the PES header. + buf->start = pes_info.pts; + buf->renderOffset = pes_info.dts; } + + fwrite64( buf, tdat + pes_info.header_len, size ); } - stream->ts[curstream].buf->size = 0; + + b->size = 0; return first; } static void hb_ts_stream_append_pkt(hb_stream_t *stream, int idx, const uint8_t *buf, int len) { - if (stream->ts[idx].buf->size + len > stream->ts[idx].buf->alloc) + if (stream->ts.list[idx].buf->size + len > stream->ts.list[idx].buf->alloc) { int size; - size = MAX(stream->ts[idx].buf->alloc * 2, stream->ts[idx].buf->size + len); - hb_buffer_realloc(stream->ts[idx].buf, size); + 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[idx].buf->data + stream->ts[idx].buf->size, buf, len); - stream->ts[idx].buf->size += len; + memcpy( stream->ts.list[idx].buf->data + stream->ts.list[idx].buf->size, + buf, len); + stream->ts.list[idx].buf->size += len; } int hb_stream_recovery_count( hb_stream_t *stream ) @@ -2899,7 +4488,7 @@ hb_buffer_t * hb_ts_decode_pkt( hb_stream_t *stream, const uint8_t * pkt ) * stash the output buffer pointer in our stream so we don't have to * pass it & its original value to everything we call. */ - int video_index = index_of_video(stream); + int video_index = ts_index_of_video(stream); int curstream; hb_buffer_t *buf; @@ -2950,13 +4539,13 @@ hb_buffer_t * hb_ts_decode_pkt( hb_stream_t *stream, const uint8_t * pkt ) // get the PCR (Program Clock Reference) if ( adapt_len > 7 && ( pkt[5] & 0x10 ) != 0 ) { - stream->ts_pcr = ( (uint64_t)pkt[6] << (33 - 8) ) | + stream->ts.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; - stream->ts_found_pcr = 1; + ++stream->ts.pcr_in; + stream->ts.found_pcr = 1; stream->ts_flags |= TS_HAS_PCR; } } @@ -2966,7 +4555,7 @@ 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; } @@ -2980,9 +4569,9 @@ hb_buffer_t * hb_ts_decode_pkt( hb_stream_t *stream, const uint8_t * pkt ) if ( (adaption & 0x01) != 0 ) { int continuity = (pkt[3] & 0xF); - if ( continuity == stream->ts[curstream].continuity ) + if ( continuity == stream->ts.list[curstream].continuity ) { - // Spliced transport streams can have duplicate + // Spliced transport streams can have duplicate // continuity counts at the splice boundary. // Test to see if the packet is really a duplicate // by comparing packet summaries to see if they @@ -2999,7 +4588,7 @@ 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[curstream].pkt_summary, 8 ) == 0 ) + if ( memcmp( summary, stream->ts.list[curstream].pkt_summary, 8 ) == 0 ) { // we got a duplicate packet (usually used to introduce // a PCR when one is needed). The only thing that can @@ -3008,32 +4597,32 @@ hb_buffer_t * hb_ts_decode_pkt( hb_stream_t *stream, const uint8_t * pkt ) return NULL; } } - if ( !start && (stream->ts[curstream].continuity != -1) && - !stream->ts[curstream].skipbad && - (continuity != ( (stream->ts[curstream].continuity + 1) & 0xf ) ) ) + if ( !start && (stream->ts.list[curstream].continuity != -1) && + !stream->ts.list[curstream].skipbad && + (continuity != ( (stream->ts.list[curstream].continuity + 1) & 0xf ) ) ) { ts_err( stream, curstream, "continuity error: got %d expected %d", (int)continuity, - (stream->ts[curstream].continuity + 1) & 0xf ); - stream->ts[curstream].continuity = continuity; + (stream->ts.list[curstream].continuity + 1) & 0xf ); + stream->ts.list[curstream].continuity = continuity; return NULL; } - stream->ts[curstream].continuity = continuity; + stream->ts.list[curstream].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 + // and payload bytes. Should be enough to detect // non-duplicates. - stream->ts[curstream].pkt_summary[0] = adaption; - stream->ts[curstream].pkt_summary[1] = adapt_len; + stream->ts.list[curstream].pkt_summary[0] = adaption; + stream->ts.list[curstream].pkt_summary[1] = adapt_len; if (adapt_len + 4 + 6 + 9 <= 188) { - memcpy(&stream->ts[curstream].pkt_summary[2], + memcpy(&stream->ts.list[curstream].pkt_summary[2], pkt+4+adapt_len+9, 6); } else { - memset(&stream->ts[curstream].pkt_summary[2], 0, 6); + memset(&stream->ts.list[curstream].pkt_summary[2], 0, 6); } } @@ -3042,28 +4631,29 @@ hb_buffer_t * hb_ts_decode_pkt( hb_stream_t *stream, const uint8_t * pkt ) if ( start ) { - // Found a random access point (now we can start a frame/audio packet..) + // Found a random access point or we have finished generating a PES + // and must start a new one. // 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 ) { ts_err( stream, curstream, "missing start code" ); - stream->ts[curstream].skipbad = 1; + stream->ts.list[curstream].skipbad = 1; return NULL; } - if ( stream->need_keyframe ) + if ( stream->need_keyframe && video_index >= 0 ) { // we're looking for the first video frame because we're // doing random access during 'scan' if ( curstream == video_index ) - stream->recovery_frames = isIframe( stream, pkt, adapt_len ); + stream->recovery_frames = ts_isIframe( stream, pkt, adapt_len ); if ( curstream != video_index || !stream->recovery_frames ) { // not the video stream or didn't find an I frame // but we'll only wait 255 video frames for an I frame. - if ( curstream != video_index || ++stream->need_keyframe ) + if ( curstream != video_index || ++stream->need_keyframe < 512 ) { return NULL; } @@ -3072,9 +4662,9 @@ hb_buffer_t * hb_ts_decode_pkt( hb_stream_t *stream, const uint8_t * pkt ) } // If we were skipping a bad packet, start fresh on this new PES packet.. - if (stream->ts[curstream].skipbad == 1) + if (stream->ts.list[curstream].skipbad == 1) { - stream->ts[curstream].skipbad = 0; + stream->ts.list[curstream].skipbad = 0; } if ( curstream == video_index ) @@ -3082,7 +4672,7 @@ hb_buffer_t * hb_ts_decode_pkt( hb_stream_t *stream, const uint8_t * pkt ) ++stream->frames; // if we don't have a pcr yet use the dts from this frame - if ( !stream->ts_found_pcr ) + if ( !stream->ts.found_pcr ) { // PES must begin with an mpeg start code & contain // a DTS or PTS. @@ -3093,34 +4683,15 @@ hb_buffer_t * hb_ts_decode_pkt( hb_stream_t *stream, const uint8_t * pkt ) return NULL; } // if we have a dts use it otherwise use the pts - stream->ts_pcr = pes_timestamp( pes + ( pes[7] & 0x40?14:9 ) ); - ++stream->ts_pcr_in; - } - } - - // the last byte of the header is the extension id. see if - // this could be a multiplexed stream - int substream = 0; - if ( pes[3] == 0xfd ) - { - int out_substream = 0; - substream = pes[pes[8]+8]; - out_substream = find_substream( &stream->ts[curstream], substream ); - if ( out_substream < 0 ) - { - buf = NULL; - if ( stream->ts[curstream].buf->size ) - buf = generate_output_data(stream, curstream); - - stream->ts[curstream].skipbad = 1; - return buf; + stream->ts.pcr = pes_timestamp( pes + ( pes[7] & 0x40?14:9 ) ); + ++stream->ts.pcr_in; } } // 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[curstream].buf->size ) + 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 @@ -3130,32 +4701,29 @@ hb_buffer_t * hb_ts_decode_pkt( hb_stream_t *stream, const uint8_t * pkt ) if ( buf ) { // Output data is ready. - stream->ts[curstream].current_substream = substream; - // remember the pcr that was in effect when we started // this packet. - stream->ts[curstream].buf->cur = stream->ts_pcr_in; - stream->ts[curstream].buf->pcr = stream->ts_pcr; + stream->ts.list[curstream].buf->cur = stream->ts.pcr_in; + stream->ts.list[curstream].buf->pcr = stream->ts.pcr; hb_ts_stream_append_pkt(stream, curstream, pkt + 4 + adapt_len, 184 - adapt_len); return buf; } } // remember the pcr that was in effect when we started this packet. - stream->ts[curstream].buf->cur = stream->ts_pcr_in; - stream->ts[curstream].buf->pcr = stream->ts_pcr; - stream->ts[curstream].current_substream = substream; + stream->ts.list[curstream].buf->cur = stream->ts.pcr_in; + stream->ts.list[curstream].buf->pcr = stream->ts.pcr; } // Add the payload for this packet to the current buffer - if (!stream->ts[curstream].skipbad && (184 - adapt_len) > 0) + 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[curstream].buf->data; + const uint8_t *pes = stream->ts.list[curstream].buf->data; int len = ( pes[4] << 8 ) + pes[5] + 6; - if ( len > 6 && stream->ts[curstream].buf->size == len && + 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); @@ -3195,23 +4763,23 @@ void hb_ts_stream_reset(hb_stream_t *stream) { int i; - for (i=0; i < kMaxNumberDecodeStreams; i++) + for (i=0; i < stream->ts.count; i++) { - if ( stream->ts[i].buf ) - stream->ts[i].buf->size = 0; - if ( stream->ts[i].extra_buf ) - stream->ts[i].extra_buf->size = 0; - stream->ts[i].skipbad = 1; - stream->ts[i].continuity = -1; + 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; } stream->need_keyframe = 0; - stream->ts_found_pcr = 0; - stream->ts_pcr_out = 0; - stream->ts_pcr_in = 0; - stream->ts_pcr = -1; - stream->ts_pcr_current = -1; + stream->ts.found_pcr = 0; + stream->ts.pcr_out = 0; + stream->ts.pcr_in = 0; + stream->ts.pcr = -1; + stream->ts.pcr_current = -1; stream->frames = 0; stream->errors = 0; @@ -3219,6 +4787,17 @@ void hb_ts_stream_reset(hb_stream_t *stream) stream->last_error_count = 0; } +void hb_ps_stream_reset(hb_stream_t *stream) +{ + stream->need_keyframe = 0; + + stream->pes.found_scr = 0; + stream->pes.scr = -1; + + stream->frames = 0; + stream->errors = 0; +} + // ------------------------------------------------------------------ // Support for reading media files via the ffmpeg libraries. @@ -3334,7 +4913,6 @@ static void add_ffmpeg_audio( hb_title_t *title, hb_stream_t *stream, int id ) } else if ( codec->codec_id == CODEC_ID_DTS && ( codec->profile == FF_PROFILE_DTS || - codec->profile == FF_PROFILE_DTS_ES || codec->profile == FF_PROFILE_DTS_96_24 ) ) { audio->config.in.codec = HB_ACODEC_DCA; @@ -3342,7 +4920,8 @@ static void add_ffmpeg_audio( hb_title_t *title, hb_stream_t *stream, int id ) else { if ( codec->codec_id == CODEC_ID_DTS && - ( codec->profile == FF_PROFILE_DTS_HD_MA || + ( codec->profile == FF_PROFILE_DTS_ES || + codec->profile == FF_PROFILE_DTS_HD_MA || codec->profile == FF_PROFILE_DTS_HD_HRA ) ) { audio->config.in.codec = HB_ACODEC_DCA_HD; @@ -3372,7 +4951,7 @@ static void add_ffmpeg_audio( hb_title_t *title, hb_stream_t *stream, int id ) } tag = av_dict_get( st->metadata, "language", NULL, 0 ); - set_audio_description( stream, audio, + set_audio_description( stream, audio, lang_for_code2( tag ? tag->value : "und" ) ); hb_list_add( title->list_audio, audio ); @@ -3385,10 +4964,10 @@ static void add_ffmpeg_audio( hb_title_t *title, hb_stream_t *stream, int id ) * Line = FieldName ':' ' ' FieldValue '\n' * FieldName = [^:]+ * FieldValue = [^\n]+ - * + * * The line of interest is: * PaletteLine = "palette" ':' ' ' RRGGBB ( ',' ' ' RRGGBB )* - * + * * More information on the format at: * http://www.matroska.org/technical/specs/subtitles/images.html */ @@ -3400,11 +4979,11 @@ static int ffmpeg_parse_vobsub_extradata_mkv( AVCodecContext *codec, hb_subtitle return 1; memcpy( lines, codec->extradata, codec->extradata_size ); lines[codec->extradata_size] = '\0'; - + uint32_t rgb[16]; int gotPalette = 0; int gotDimensions = 0; - + char *curLine, *curLine_parserData; for ( curLine = strtok_r( lines, "\n", &curLine_parserData ); curLine; @@ -3444,9 +5023,9 @@ static int ffmpeg_parse_vobsub_extradata_mkv( AVCodecContext *codec, hb_subtitle subtitle->width = 720; subtitle->height = 480; } - + free( lines ); - + if ( gotPalette ) { int i; @@ -3467,11 +5046,11 @@ static int ffmpeg_parse_vobsub_extradata_mp4( AVCodecContext *codec, hb_subtitle { if ( codec->extradata_size != 4*16 ) return 1; - + int i, j; for ( i=0, j=0; i<16; i++, j+=4 ) { - subtitle->palette[i] = + subtitle->palette[i] = codec->extradata[j+1] << 16 | // Y codec->extradata[j+2] << 8 | // Cb codec->extradata[j+3] << 0; // Cr @@ -3505,11 +5084,11 @@ static void add_ffmpeg_subtitle( hb_title_t *title, hb_stream_t *stream, int id { AVStream *st = stream->ffmpeg_info_ic->streams[id]; AVCodecContext *codec = st->codec; - + hb_subtitle_t *subtitle = calloc( 1, sizeof(*subtitle) ); - + subtitle->id = id; - + switch ( codec->codec_id ) { case CODEC_ID_DVD_SUBTITLE: @@ -3540,7 +5119,7 @@ static void add_ffmpeg_subtitle( hb_title_t *title, hb_stream_t *stream, int id free(subtitle); return; } - + AVDictionaryEntry *tag; iso639_lang_t *language; @@ -3548,12 +5127,12 @@ static void add_ffmpeg_subtitle( hb_title_t *title, hb_stream_t *stream, int id language = lang_for_code2( tag ? tag->value : "und" ); strcpy( subtitle->lang, language->eng_name ); strncpy( subtitle->iso639_2, language->iso639_2, 4 ); - + // Copy the extradata for the subtitle track subtitle->extradata = malloc( codec->extradata_size ); memcpy( subtitle->extradata, codec->extradata, codec->extradata_size ); subtitle->extradata_size = codec->extradata_size; - + hb_list_add(title->list_subtitle, subtitle); } @@ -3574,7 +5153,7 @@ static void add_ffmpeg_attachment( hb_title_t *title, hb_stream_t *stream, int i { AVStream *st = stream->ffmpeg_info_ic->streams[id]; AVCodecContext *codec = st->codec; - + enum attachtype type; switch ( codec->codec_id ) { @@ -3585,16 +5164,16 @@ static void add_ffmpeg_attachment( hb_title_t *title, hb_stream_t *stream, int i // Ignore unrecognized attachment type return; } - + hb_attachment_t *attachment = calloc( 1, sizeof(*attachment) ); - + // Copy the attachment name and data attachment->type = type; attachment->name = strdup( get_ffmpeg_metadata_value( st->metadata, "filename" ) ); attachment->data = malloc( codec->extradata_size ); memcpy( attachment->data, codec->extradata, codec->extradata_size ); attachment->size = codec->extradata_size; - + hb_list_add(title->list_attachment, attachment); } @@ -3854,18 +5433,18 @@ hb_buffer_t * hb_ffmpeg_read( hb_stream_t *stream ) buf->renderOffset = buf->start; } - /* + /* * Fill out buf->stop for subtitle packets - * + * * libavcodec's MKV demuxer stores the duration of UTF-8 subtitles (CODEC_ID_TEXT) * in the 'convergence_duration' field for some reason. - * + * * Other subtitles' durations are stored in the 'duration' field. - * + * * VOB subtitles (CODEC_ID_DVD_SUBTITLE) do not have their duration stored in * either field. This is not a problem because the VOB decoder can extract this * information from the packet payload itself. - * + * * SSA subtitles (CODEC_ID_SSA) do not have their duration stored in * either field. This is not a problem because the SSA decoder can extract this * information from the packet payload itself. |