int codec; /* HB worker object id of codec */
int codec_param; /* param for codec (usually ffmpeg codec id) */
const char* name; /* description of type */
+ int extra_hdr; /* needs a substream header added to PS pack */
} stream2codec_t;
#define st(id, kind, codec, codec_param, name) \
- [id] = { kind, codec, codec_param, name }
+ [id] = { kind, codec, codec_param, name, 0 }
+#define se(id, kind, codec, codec_param, name) \
+ [id] = { kind, codec, codec_param, name, 1 }
static const stream2codec_t st2codec[256] = {
st(0x01, V, WORK_DECMPEG2, 0, "MPEG1"),
st(0x1b, V, WORK_DECAVCODECV, CODEC_ID_H264, "H.264"),
- st(0x80, U, 0, 0, "DigiCipher II Video"),
- st(0x81, A, HB_ACODEC_AC3, 0, "AC-3"),
- st(0x82, A, HB_ACODEC_MPGA, CODEC_ID_DTS, "HDMV DTS"),
+ //st(0x80, U, 0, 0, "DigiCipher II Video"),
+ se(0x81, A, HB_ACODEC_AC3, 0, "AC-3"),
+ se(0x82, A, HB_ACODEC_DCA, 0, "HDMV DTS"),
st(0x83, A, HB_ACODEC_LPCM, 0, "LPCM"),
st(0x84, A, 0, 0, "SDDS"),
st(0x85, U, 0, 0, "ATSC Program ID"),
st(0x86, U, 0, 0, "SCTE 35 splice info"),
st(0x87, A, 0, 0, "E-AC-3"),
- st(0x8a, A, HB_ACODEC_DCA, 0, "DTS"),
+ se(0x8a, A, HB_ACODEC_DCA, 0, "DTS"),
- st(0x91, A, HB_ACODEC_AC3, 0, "AC-3"),
+ se(0x91, A, HB_ACODEC_AC3, 0, "AC-3"),
st(0x92, U, 0, 0, "Subtitle"),
st(0x94, A, 0, 0, "SDDS"),
uint8_t *fwrite_buf; /* PS buffer (set by hb_ts_stream_decode) */
uint8_t *fwrite_buf_orig; /* PS buffer start (set by hb_ts_stream_decode) */
+ uint8_t need_keyframe;
+
/*
* Stuff before this point is dynamic state updated as we read the
* stream. Stuff after this point is stream description state that
uint8_t ts_streamid[kMaxNumberDecodeStreams];
uint8_t ts_stream_type[kMaxNumberDecodeStreams];
+ uint8_t ts_extra_hdr[kMaxNumberDecodeStreams];
char *path;
FILE *file_handle;
AVFormatContext *ffmpeg_ic;
AVPacket *ffmpeg_pkt;
double ffmpeg_tsconv[MAX_STREAMS];
+ uint8_t ffmpeg_video_id;
struct {
int lang_code;
stream->packetsize = psize;
stream->hb_stream_type = transport;
hb_ts_stream_init(stream);
+ if ( !stream->ts_number_video_pids || !stream->ts_number_audio_pids )
+ {
+ return 0;
+ }
return 1;
}
if ( hb_stream_check_for_dvd_ps(buf) != 0 )
d->ts_buf[i] = malloc( HB_DVD_READ_BUFFER_SIZE );
}
hb_stream_seek( d, 0. );
+
+ if ( d->packetsize == 188 )
+ {
+ // Assume that an over-the-air transport stream can lose PCR
+ // packets and try to filter out the timing inconsistencies.
+ title->flaky_clock = 1;
+ }
}
return d;
}
{
stream->ts_audio_pids[indx] = stream->ts_audio_pids[i];
stream->ts_stream_type[1 + indx] = stream->ts_stream_type[1+i];
+ stream->ts_extra_hdr[1 + indx] = stream->ts_extra_hdr[1+i];
stream->ts_streamid[1 + indx] = stream->ts_streamid[1 + i];
++indx;
}
// We need to drop the current decoder output and move
// forwards to the next transport stream packet.
hb_ts_stream_reset(src_stream);
+ src_stream->need_keyframe = ( f != 0 );
}
else if ( src_stream->hb_stream_type == program )
{
if ( st2codec[stype].kind == A && st2codec[stype].codec )
{
stream->ts_streamid[1 + aud_pid_index] = audio->id;
+ stream->ts_extra_hdr[1 + aud_pid_index] = st2codec[stype].extra_hdr;
audio->config.in.codec = st2codec[stype].codec;
audio->config.in.codec_param = st2codec[stype].codec_param;
set_audio_description( audio,
}
+ if ( index_of_pid( elementary_PID, stream ) < 0 )
+ {
+ // already have this pid - do nothing
+ }
if (stream->ts_number_video_pids == 0 && st2codec[stream_type].kind == V )
{
stream->ts_video_pids[0] = elementary_PID;
// Get pointer length - only valid in packets with a start flag
int pointer_len = 0;
- if (start && stream->pmt_info.reading)
- {
- // We just finished a bunch of packets - parse the program map details
- int decode_ok = 0;
- if (stream->pmt_info.tablebuf[0] == 0x02)
- decode_ok = decode_program_map(stream);
- free(stream->pmt_info.tablebuf);
- stream->pmt_info.tablebuf = NULL;
- stream->pmt_info.tablepos = 0;
- stream->pmt_info.reading = 0;
- if (decode_ok)
- return decode_ok;
- }
if (start)
{
memcpy(stream->pmt_info.tablebuf + stream->pmt_info.tablepos, buf + 4 + adapt_len + pointer_len, amount_to_copy);
stream->pmt_info.tablepos += amount_to_copy;
}
+ if (stream->pmt_info.tablepos > 3)
+ {
+ // We have enough to check the section length
+ int length;
+ length = ((stream->pmt_info.tablebuf[1] << 8) +
+ stream->pmt_info.tablebuf[2]) & 0xFFF;
+ if (stream->pmt_info.tablepos > length + 1)
+ {
+ // We just finished a bunch of packets - parse the program map details
+ int decode_ok = 0;
+ if (stream->pmt_info.tablebuf[0] == 0x02)
+ decode_ok = decode_program_map(stream);
+ free(stream->pmt_info.tablebuf);
+ stream->pmt_info.tablebuf = NULL;
+ stream->pmt_info.tablepos = 0;
+ stream->pmt_info.reading = 0;
+ if (decode_ok)
+ return decode_ok;
+ }
+
+ }
return 0;
}
for (;;)
{
const uint8_t *buf = next_packet( stream );
+
if ( buf == NULL )
{
hb_log("hb_ts_stream_find_pids - end of file");
// 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 (pid == stream->pat_info[pat_index].program_map_PID)
+ if (stream->pat_info[pat_index].program_number != 0 &&
+ pid == stream->pat_info[pat_index].program_map_PID)
{
if (build_program_map(buf, stream) > 0)
break;
if ((stream->ts_number_video_pids > 0) && (stream->ts_number_audio_pids > 0))
break;
}
+ // XXX - until we figure out how to handle VC1 just bail when we find it so
+ // that ffmpeg will claim the input stream.
+ if ( stream->ts_stream_type[0] == 0xea )
+ {
+ stream->ts_number_video_pids = 0;
+ stream->ts_number_audio_pids = 0;
+ return;
+ }
hb_log("hb_ts_stream_find_pids - found the following PIDS");
hb_log(" Video PIDS : ");
// we always ship a PACK header plus all the data in our demux buf.
// AC3 audio also always needs its substream header.
len = 14 + stream->ts_pos[curstream];
- if ( stream->ts_stream_type[curstream] == 0x81)
+ if ( stream->ts_extra_hdr[curstream] )
{
len += 4;
}
tdat[3] = stream->ts_streamid[curstream];
uint16_t plen = stream->ts_pos[curstream] - 6;
- if ( stream->ts_stream_type[curstream] == 0x81)
+ if ( stream->ts_extra_hdr[curstream] )
{
// We have to add an AC3 header in front of the data. Add its
// size to the PES packet length.
{
// data without a PES start header needs a simple 'continuation'
// PES header. AC3 audio also needs its substream header.
- if ( stream->ts_stream_type[curstream] != 0x81)
+ if ( stream->ts_extra_hdr[curstream] == 0 )
{
make_pes_header(stream, stream->ts_pos[curstream],
stream->ts_streamid[curstream]);
// h.264 IDR picture start
return 1;
+ if ( stream->packetsize == 192 )
+ {
+ // m2ts files have idr frames so keep looking for one
+ continue;
+ }
+
+ // h226 in ts files (ATSC or DVB video) often seem to be
+ // missing IDR frames so look for at least an I
if ( nal_type == 0x01 )
{
// h.264 slice: has to be start MB 0 & type I (2, 4, 7 or 9)
stream->ts_lastpcr = pcr;
}
- if ( pcr == -1 )
- {
- // don't accumulate data until we get a pcr
- continue;
- }
+ // If we don't have a pcr yet, the right thing to do here would
+ // be a 'continue' so we don't process anything until we have a
+ // clock reference. Unfortunately the HD Home Run appears to null
+ // out the pcr field of some streams so we keep going & substitute
+ // the video stream dts for the pcr when there's no pcr.
// Get continuity
// Continuity only increments for adaption values of 0x3 or 0x01
continue;
}
if ( !start && (stream->ts_streamcont[curstream] != -1) &&
+ stream->ts_foundfirst[curstream] &&
(continuity != ( (stream->ts_streamcont[curstream] + 1) & 0xf ) ) )
{
ts_err( stream, curstream, "continuity error: got %d expected %d",
{
if ( !stream->ts_foundfirst[0] )
{
- if ( !isIframe( stream, buf, adapt_len ) )
+ if ( stream->need_keyframe )
{
- // didn't find an I frame
- continue;
+ if ( !isIframe( stream, buf, adapt_len ) )
+ {
+ // didn't find an I frame
+ continue;
+ }
+ stream->need_keyframe = 0;
}
stream->ts_foundfirst[0] = 1;
}
++stream->frames;
+
+ // if we don't have a pcr yet use the dts from this frame
+ if ( pcr == -1 )
+ {
+ // PES must begin with an mpeg start code & contain
+ // a DTS or PTS.
+ const uint8_t *pes = buf + adapt_len + 4;
+ if ( pes[0] != 0x00 || pes[1] != 0x00 || pes[2] != 0x01 ||
+ ( pes[7] >> 6 ) == 0 )
+ {
+ continue;
+ }
+ // if we have a dts use it otherwise use the pts
+ pes += (pes[7] & 0x40)? 14 : 9;
+
+ pcr = ( (uint64_t)(pes[0] & 0xe ) << 29 );
+ pcr |= ( pes[1] << 22 ) |
+ ( ( pes[2] >> 1 ) << 15 ) |
+ ( pes[3] << 7 ) |
+ ( pes[4] >> 1 );
+ stream->ts_nextpcr = pcr;
+ }
}
- else if ( ! stream->ts_foundfirst[curstream] )
+ else if ( ! stream->ts_foundfirst[curstream] )
{
// start other streams only after first video frame found.
if ( ! stream->ts_foundfirst[0] )
stream->frames = 0;
stream->errors = 0;
+ stream->need_keyframe = 0;
stream->last_error_frame = -10000;
stream->last_error_count = 0;
// read the first packet.
AVCodecContext *context = stream->ffmpeg_ic->streams[stream_index]->codec;
context->workaround_bugs = FF_BUG_AUTODETECT;
- context->error_resilience = 1;
+ context->error_recognition = 1;
context->error_concealment = FF_EC_GUESS_MVS|FF_EC_DEBLOCK;
AVCodec *codec = avcodec_find_decoder( context->codec_id );
avcodec_open( context, codec );
// indexed its stream so we need to remap them so they point
// to this stream.
ffmpeg_remap_stream( stream, title );
- ffmpeg_seek( stream, 0. );
av_log_set_level( AV_LOG_ERROR );
}
else
{
audio->config.in.codec = HB_ACODEC_AC3;
}
+ else if ( codec->codec_id == CODEC_ID_DTS )
+ {
+ audio->config.in.codec = HB_ACODEC_DCA;
+ }
else
{
audio->config.in.codec = HB_ACODEC_FFMPEG;
title->video_codec == 0 )
{
title->video_id = i;
+ stream->ffmpeg_video_id = i;
// We have to use the 'internal' avcodec decoder because
// it needs to share the codec context from this video
static int ffmpeg_read( hb_stream_t *stream, hb_buffer_t *buf )
{
int err;
+ again:
if ( ( err = av_read_frame( stream->ffmpeg_ic, stream->ffmpeg_pkt )) < 0 )
{
// XXX the following conditional is to handle avi files that
{
if ( stream->ffmpeg_pkt->size > buf->alloc )
{
+ // sometimes we get absurd sizes from ffmpeg
+ if ( stream->ffmpeg_pkt->size >= (1 << 25) )
+ {
+ hb_log( "ffmpeg_read: pkt too big: %d bytes", stream->ffmpeg_pkt->size );
+ av_free_packet( stream->ffmpeg_pkt );
+ return ffmpeg_read( stream, buf );
+ }
// need to expand buffer
hb_buffer_realloc( buf, stream->ffmpeg_pkt->size );
}
buf->size = stream->ffmpeg_pkt->size;
}
buf->id = stream->ffmpeg_pkt->stream_index;
+ if ( buf->id == stream->ffmpeg_video_id )
+ {
+ if ( stream->need_keyframe &&
+ stream->ffmpeg_ic->streams[stream->ffmpeg_video_id]->codec->codec_id ==
+ CODEC_ID_VC1 )
+ {
+ // XXX the VC1 codec doesn't seek to key frames so to get previews
+ // we do it ourselves here. The decoder gets messed up if it
+ // doesn't get a SEQ header first so we consider that to be a key frame.
+ uint8_t *pkt = stream->ffmpeg_pkt->data;
+ if ( pkt[0] || pkt[1] || pkt[2] != 1 || pkt[3] != 0x0f )
+ {
+ goto again;
+ }
+ stream->need_keyframe = 0;
+ }
+ ++stream->frames;
+ }
// if we haven't done it already, compute a conversion factor to go
// from the ffmpeg timebase for the stream to HB's 90KHz timebase.
{
AVFormatContext *ic = stream->ffmpeg_ic;
int64_t pos = (double)ic->duration * (double)frac;
- av_seek_frame( ic, -1, pos, pos? 0 : AVSEEK_FLAG_BACKWARD );
+ if ( pos )
+ {
+ av_seek_frame( ic, -1, pos, 0 );
+ stream->need_keyframe = 1;
+ }
+ else
+ {
+ av_seek_frame( ic, -1, pos, AVSEEK_FLAG_BACKWARD );
+ }
return 1;
}