X-Git-Url: http://git.osdn.jp/view?a=blobdiff_plain;f=libhb%2Fstream.c;h=538353e523023fc41479fbbeceb2b37dcd720c45;hb=033e32de9c380f54c7d1362a3979da205ebc3a29;hp=8e66c850aa4d843588bcbc3c7f5359b4797f4b02;hpb=63d771700f06b65756bb7d0f9ab637df8d518dea;p=handbrake-jp%2Fhandbrake-jp-git.git diff --git a/libhb/stream.c b/libhb/stream.c old mode 100755 new mode 100644 index 8e66c850..538353e5 --- a/libhb/stream.c +++ b/libhb/stream.c @@ -9,10 +9,9 @@ #include #include "hb.h" +#include "hbffmpeg.h" #include "lang.h" #include "a52dec/a52.h" -#include "libavcodec/avcodec.h" -#include "libavformat/avformat.h" #include "mp4v2/mp4v2.h" #define min(a, b) a < b ? a : b @@ -90,7 +89,7 @@ typedef enum { } hb_stream_type_t; #define kMaxNumberVideoPIDS 1 -#define kMaxNumberAudioPIDS 15 +#define kMaxNumberAudioPIDS 31 #define kMaxNumberDecodeStreams (kMaxNumberVideoPIDS+kMaxNumberAudioPIDS) #define kMaxNumberPMTStreams 32 @@ -103,10 +102,8 @@ struct hb_stream_s int last_error_count; /* # errors at last error message */ int packetsize; /* Transport Stream packet size */ - int8_t need_keyframe; // non-zero if want to start at a keyframe - int8_t ts_no_RAP; // non-zero if there are no random access points - - int8_t ts_found_pcr; // non-zero if we've found at least one input pcr + uint8_t need_keyframe; // non-zero if want to start at a keyframe + 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 @@ -117,11 +114,12 @@ struct hb_stream_s int ts_pos[kMaxNumberDecodeStreams]; int8_t ts_skipbad[kMaxNumberDecodeStreams]; int8_t ts_streamcont[kMaxNumberDecodeStreams]; + uint8_t ts_pkt_summary[kMaxNumberDecodeStreams][8]; hb_buffer_t *fwrite_buf; /* PS buffer (set by hb_ts_stream_decode) */ int chapter; /* Chapter that we are currently in */ - uint64_t chapter_end; /* HB time that the current chapter ends */ + int64_t chapter_end; /* HB time that the current chapter ends */ /* * Stuff before this point is dynamic state updated as we read the @@ -131,6 +129,11 @@ struct hb_stream_s */ uint8_t ts_number_video_pids; uint8_t ts_number_audio_pids; + 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 int16_t ts_video_pids[kMaxNumberVideoPIDS]; int16_t ts_audio_pids[kMaxNumberAudioPIDS]; @@ -273,9 +276,12 @@ static void ts_warn_helper( hb_stream_t *stream, char *log, va_list args ) } } +static void ts_warn( hb_stream_t*, char*, ... ) HB_WPRINTF(2,3); +static void ts_err( hb_stream_t*, int, char*, ... ) HB_WPRINTF(3,4); + static void ts_warn( hb_stream_t *stream, char *log, ... ) { - va_list args; + va_list args; va_start( args, log ); ts_warn_helper( stream, log, args ); va_end( args ); @@ -283,7 +289,7 @@ static void ts_warn( hb_stream_t *stream, char *log, ... ) static void ts_err( hb_stream_t *stream, int curstream, char *log, ... ) { - va_list args; + va_list args; va_start( args, log ); ts_warn_helper( stream, log, args ); va_end( args ); @@ -301,14 +307,13 @@ static int check_ps_sync(const uint8_t *buf) (buf[2] == 0x01) && (buf[3] == 0xba); } -static int check_ps_sys(const uint8_t *buf) +static int check_ps_sc(const uint8_t *buf) { // a legal MPEG program stream must start with a Pack followed by a - // SYS. If we've already verified the pack, this skips over it and checks - // for the sys header. + // 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 - return (buf[pos+0] == 0x00) && (buf[pos+1] == 0x00) && - (buf[pos+2] == 0x01) && (buf[pos+3] == 0xbb); + return (buf[pos+0] == 0x00) && (buf[pos+1] == 0x00) && (buf[pos+2] == 0x01); } static int check_ts_sync(const uint8_t *buf) @@ -348,8 +353,9 @@ static int hb_stream_check_for_ts(const uint8_t *buf) static int hb_stream_check_for_ps(const uint8_t *buf) { - // program streams should start with a PACK then a SYS header. - return check_ps_sync(buf) && check_ps_sys(buf); + // 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). + return check_ps_sync(buf) && check_ps_sc(buf); } static int hb_stream_check_for_dvd_ps(const uint8_t *buf) @@ -468,7 +474,7 @@ static int audio_inactive( hb_stream_t *stream, int indx ) **********************************************************************/ hb_stream_t * hb_stream_open( char *path, hb_title_t *title ) { - FILE *f = fopen( path, "r" ); + FILE *f = fopen( path, "rb" ); if ( f == NULL ) { hb_log( "hb_stream_open: open %s failed", path ); @@ -705,6 +711,17 @@ hb_title_t * hb_stream_title_scan(hb_stream_t *stream) aTitle->video_codec = st2codec[stream->ts_stream_type[0]].codec; aTitle->video_codec_param = st2codec[stream->ts_stream_type[0]].codec_param; aTitle->demuxer = HB_MPEG2_TS_DEMUXER; + + 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; + } } else { @@ -738,10 +755,10 @@ static const uint8_t *next_packet( hb_stream_t *stream ) off_t pos2 = align_to_next_packet(stream); if ( pos2 == 0 ) { - hb_log( "next_packet: eof while re-establishing sync @ %lld", pos ); + hb_log( "next_packet: eof while re-establishing sync @ %"PRId64, pos ); return NULL; } - ts_warn( stream, "next_packet: sync lost @ %lld, regained after %lld bytes", + ts_warn( stream, "next_packet: sync lost @ %"PRId64", regained after %"PRId64" bytes", pos, pos2 ); } } @@ -773,42 +790,87 @@ static void skip_to_next_pack( hb_stream_t *src_stream ) } } -/* - * scan the next MB of 'stream' to try to find a random access point - */ -static void hb_ts_stream_find_RAP( hb_stream_t *stream ) +static int isIframe( hb_stream_t *stream, const uint8_t *buf, int adapt_len ) { - off_t starting_point = ftello(stream->file_handle); - int npack = 300000; // max packets to read + // 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; + uint32_t strid = 0; - while (--npack >= 0) + + if ( stream->ts_stream_type[0] <= 2 ) { - off_t cur = ftello(stream->file_handle); - const uint8_t *buf = next_packet( stream ); - if ( buf == NULL ) - { - break; - } - switch (buf[3] & 0x30) + // This section of the code handles MPEG-1 and MPEG-2 video streams + for (i = 13 + adapt_len; i < 188; i++) { - case 0x00: // illegal - continue; - - case 0x20: // fill packet - case 0x30: // adaptation - if ( buf[5] & 0x40 ) + strid = (strid << 8) | buf[i]; + if ( ( strid >> 8 ) == 1 ) + { + // we found a start code + uint8_t id = strid; + switch ( id ) { - // found a random access point - fseeko( stream->file_handle, cur, SEEK_SET ); - return; + case 0xB8: // group_start_code (GOP header) + case 0xB3: // sequence_header code + return 1; + + case 0x00: // picture_start_code + // picture_header, let's see if it's an I-frame + if (i<185) + { + // check if picture_coding_type == 1 + if ((buf[i+2] & (0x7 << 3)) == (1 << 3)) + { + // found an I-frame picture + return 1; + } + } + break; } - continue; + } + } + // didn't find an I-frame + return 0; + } + if ( stream->ts_stream_type[0] == 0x1b ) + { + // we have an h.264 stream + for (i = 13 + adapt_len; i < 188; i++) + { + strid = (strid << 8) | buf[i]; + if ( ( strid >> 8 ) == 1 ) + { + // we found a start code - remove the ref_idc from the nal type + uint8_t nal_type = strid & 0x1f; + if ( nal_type == 0x05 ) + // h.264 IDR picture start + return 1; + } + } + // didn't find an I-frame + return 0; + } + if ( stream->ts_stream_type[0] == 0xea ) + { + // we have an vc1 stream + for (i = 13 + adapt_len; i < 188; i++) + { + strid = (strid << 8) | buf[i]; + if ( strid == 0x10f ) + { + // the ffmpeg vc1 decoder requires a seq hdr code in the first + // frame. + return 1; + } } + // didn't find an I-frame + return 0; } - /* didn't find it */ - fseeko( stream->file_handle, starting_point, SEEK_SET ); - stream->ts_no_RAP = 1; + // we don't understand the stream type so just say "yes" otherwise + // we'll discard all the video. + return 1; } /* @@ -828,12 +890,28 @@ static const uint8_t *hb_ts_stream_getPEStype(hb_stream_t *stream, uint32_t pid) return 0; } + // while we're reading the stream, check if it has valid PCRs + // and/or random access points. + uint32_t pack_pid = ( (buf[1] & 0x1f) << 8 ) | buf[2]; + if ( pack_pid == stream->pmt_info.PCR_PID ) + { + if ( ( buf[5] & 0x10 ) && + ( ( ( buf[3] & 0x30 ) == 0x20 ) || + ( ( buf[3] & 0x30 ) == 0x30 && buf[4] > 6 ) ) ) + { + stream->ts_flags |= TS_HAS_PCR; + } + } + if ( buf[5] & 0x40 ) + { + stream->ts_flags |= TS_HAS_RAP; + } + /* * The PES header is only in TS packets with 'start' set so we check * that first then check for the right PID. */ - if ((buf[1] & 0x40) == 0 || (buf[1] & 0x1f) != (pid >> 8) || - buf[2] != (pid & 0xff)) + if ((buf[1] & 0x40) == 0 || pack_pid != pid ) { // not a start packet or not the pid we want continue; @@ -935,7 +1013,7 @@ struct pts_pos { uint64_t pts; /* PTS from video stream */ }; -#define NDURSAMPLES 64 +#define NDURSAMPLES 128 // get one (position, timestamp) sampple from a transport or program // stream. @@ -951,12 +1029,12 @@ static struct pts_pos hb_sample_pts(hb_stream_t *stream, uint64_t fpos) buf = hb_ts_stream_getPEStype( stream, stream->ts_video_pids[0] ); if ( buf == NULL ) { - hb_log("hb_sample_pts: couldn't find video packet near %llu", fpos); + hb_log("hb_sample_pts: couldn't find video packet near %"PRIu64, fpos); return pp; } if ( ( buf[7] >> 7 ) != 1 ) { - hb_log("hb_sample_pts: no PTS in video packet near %llu", fpos); + hb_log("hb_sample_pts: no PTS in video packet near %"PRIu64, fpos); return pp; } pp.pts = ( ( (uint64_t)buf[9] >> 1 ) & 7 << 30 ) | @@ -964,6 +1042,14 @@ static struct pts_pos hb_sample_pts(hb_stream_t *stream, uint64_t fpos) ( ( (uint64_t)buf[11] >> 1 ) << 15 ) | ( (uint64_t)buf[12] << 7 ) | ( (uint64_t)buf[13] >> 1 ); + + if ( isIframe( stream, buf, -4 ) ) + { + if ( stream->ts_IDRs < 255 ) + { + ++stream->ts_IDRs; + } + } } else { @@ -1120,61 +1206,40 @@ int hb_stream_read( hb_stream_t * src_stream, hb_buffer_t * b ) return hb_ts_stream_decode( src_stream, b ); } -/*********************************************************************** - * hb_stream_seek_chapter - *********************************************************************** - * - **********************************************************************/ int hb_stream_seek_chapter( hb_stream_t * stream, int chapter_num ) { - AVFormatContext *ic = stream->ffmpeg_ic; - uint64_t end_offset = 0; - uint64_t start_offset = 0; - uint64_t pos = 0; - hb_chapter_t *chapter = NULL; - int i; - if( !stream || !stream->title ) - { - return 0; - } if ( stream->hb_stream_type != ffmpeg ) { // currently meaningliess for transport and program streams return 1; } - - for( i = 0; i < chapter_num; i++) + if ( !stream || !stream->title || + chapter_num > hb_list_count( stream->title->list_chapter ) ) { - chapter = hb_list_item( stream->title->list_chapter, - i ); - - if( chapter ) - { - /* - * Seeking to a chapter means that we are in that chapter, - * so track which chapter we are in so that we can output - * the correct chapter numbers in buf->new_chap - */ - start_offset = end_offset; - end_offset += chapter->duration; - stream->chapter = i; - stream->chapter_end = end_offset; - } else { - return 0; - } + return 0; } - /* - * Is the the correct way to convert timebases? It seems to get it pretty - * much right - plus a few seconds, which is odd. - */ - pos = ((start_offset * AV_TIME_BASE) / 90000); + int64_t sum_dur = 0; + hb_chapter_t *chapter = NULL; + int i; + for ( i = 0; i < chapter_num; ++i) + { + chapter = hb_list_item( stream->title->list_chapter, i ); + sum_dur += chapter->duration; + } + stream->chapter = chapter_num - 1; + stream->chapter_end = sum_dur; - hb_deep_log( 2, "Seeking to chapter %d time (starts: %lld ends %lld) AV pos %lld", chapter_num-1, start_offset, end_offset, pos); + int64_t pos = ( ( ( sum_dur - chapter->duration ) * AV_TIME_BASE ) / 90000 ); - av_seek_frame( ic, -1, pos, 0); + hb_deep_log( 2, "Seeking to chapter %d: starts %"PRId64", ends %"PRId64", AV pos %"PRId64, + chapter_num, sum_dur - chapter->duration, sum_dur, pos); + if ( chapter_num > 1 && pos > 0 ) + { + av_seek_frame( stream->ffmpeg_ic, -1, pos, 0); + } return 1; } @@ -1222,12 +1287,11 @@ int hb_stream_seek( hb_stream_t * stream, float f ) hb_ts_stream_reset(stream); if ( f > 0 ) { - if ( !stream->ts_no_RAP ) + if ( stream->ts_IDRs ) { - // we're not at the beginning - try to find a random access point - hb_ts_stream_find_RAP( stream ); + // the stream has IDRs so look for one. + stream->need_keyframe = 1; } - stream->need_keyframe = 1; } else { @@ -1631,7 +1695,7 @@ static void decode_element_descriptors(hb_stream_t* stream, int esindx, switch (dp[0]) { case 5: // Registration descriptor - stream->ts_format_id[esindx] = (dp[2] << 24) | (dp[3] << 16) | + stream->ts_format_id[esindx+1] = (dp[2] << 24) | (dp[3] << 16) | (dp[4] << 8) | dp[5]; break; @@ -1639,6 +1703,10 @@ static void decode_element_descriptors(hb_stream_t* stream, int esindx, stream->a52_info[esindx].lang_code = lang_to_code(lang_for_code2((const char *)&dp[2])); break; + case 0x6a: // DVB AC-3 descriptor + stream->ts_stream_type[esindx+1] = 0x81; + break; + default: break; } @@ -1702,33 +1770,35 @@ int decode_program_map(hb_stream_t* stream) 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; - stream->ts_stream_type[0] = stream_type; - stream->ts_number_video_pids = 1; - } - else - { - // Defined audio stream types are 0x81 for AC-3/A52 audio and 0x03 - // for mpeg audio. But content producers seem to use other - // values (0x04 and 0x06 have both been observed) so at this point - // we say everything that isn't a video pid is audio then at the end - // of hb_stream_title_scan we'll figure out which are really audio - // by looking at the PES headers. - i = stream->ts_number_audio_pids; - if (i < kMaxNumberAudioPIDS) + // don't have this pid yet + if (stream->ts_number_video_pids == 0 && + st2codec[stream_type].kind == V ) + { + stream->ts_video_pids[0] = elementary_PID; + stream->ts_stream_type[0] = stream_type; + stream->ts_number_video_pids = 1; + } + else { - stream->ts_audio_pids[i] = elementary_PID; - stream->ts_stream_type[1 + i] = stream_type; - if (ES_info_length > 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_audio_pids; + if (i < kMaxNumberAudioPIDS) { - decode_element_descriptors(stream, i, ES_info_buf, - ES_info_length); + stream->ts_audio_pids[i] = elementary_PID; + stream->ts_stream_type[1 + i] = stream_type; + if (ES_info_length > 0) + { + decode_element_descriptors(stream, i, ES_info_buf, + ES_info_length); + } + ++stream->ts_number_audio_pids; } - ++stream->ts_number_audio_pids; } } @@ -1737,7 +1807,7 @@ int decode_program_map(hb_stream_t* stream) free(ES_info_buf); if (cur_pos >= section_length - 4 /* stop before the CRC */) - done_reading_stream_types = 1; + done_reading_stream_types = 1; } free(descriptor_buf); @@ -1926,7 +1996,13 @@ static int decode_PAT(const uint8_t *buf, hb_stream_t *stream) static void hb_ts_stream_find_pids(hb_stream_t *stream) { - // align to first packet + // To be different from every other broadcaster in the world, New Zealand TV + // changes PMTs (and thus video & audio PIDs) when 'programs' change. Since + // we may have the tail of the previous program at the beginning of this + // file, take our PMT from the middle of the file. + fseeko(stream->file_handle, 0, SEEK_END); + uint64_t fsize = ftello(stream->file_handle); + fseeko(stream->file_handle, fsize >> 1, SEEK_SET); align_to_next_packet(stream); // Read the Transport Stream Packets (188 bytes each) looking at first for PID 0 (the PAT PID), then decode that @@ -2000,16 +2076,17 @@ static void hb_ts_stream_find_pids(hb_stream_t *stream) static void fwrite64( hb_stream_t *stream, void *buf, int len ) { - int pos; - - pos = stream->fwrite_buf->size; - if ( pos + len > stream->fwrite_buf->alloc ) + if ( len > 0 ) { - int size = MAX(stream->fwrite_buf->alloc * 2, pos + len); - hb_buffer_realloc(stream->fwrite_buf, size); + int pos = stream->fwrite_buf->size; + if ( pos + len > stream->fwrite_buf->alloc ) + { + int size = MAX(stream->fwrite_buf->alloc * 2, pos + len); + hb_buffer_realloc(stream->fwrite_buf, size); + } + memcpy( &(stream->fwrite_buf->data[pos]), buf, len ); + stream->fwrite_buf->size += len; } - memcpy( &(stream->fwrite_buf->data[pos]), buf, len ); - stream->fwrite_buf->size += len; } // convert a PES PTS or DTS to an int64 @@ -2081,108 +2158,6 @@ static void generate_output_data(hb_stream_t *stream, int curstream) stream->ts_buf[curstream]->size = 0; } -static int isIframe( hb_stream_t *stream, const uint8_t *buf, int adapt_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: ??? - int i; - uint32_t strid = 0; - - - if ( stream->ts_stream_type[0] <= 2 ) - { - // This section of the code handles MPEG-1 and MPEG-2 video streams - for (i = 13 + adapt_len; i < 188; i++) - { - strid = (strid << 8) | buf[i]; - if ( ( strid >> 8 ) == 1 ) - { - // we found a start code - uint8_t id = strid; - switch ( id ) - { - case 0xB8: // group_start_code (GOP header) - case 0xB3: // sequence_header code - return 1; - - case 0x00: // picture_start_code - // picture_header, let's see if it's an I-frame - if (i<185) - { - // check if picture_coding_type == 1 - if ((buf[i+2] & (0x7 << 3)) == (1 << 3)) - { - // found an I-frame picture - return 1; - } - } - break; - } - } - } - // didn't find an I-frame - return 0; - } - if ( stream->ts_stream_type[0] == 0x1b ) - { - // we have an h.264 stream - for (i = 13 + adapt_len; i < 188; i++) - { - strid = (strid << 8) | buf[i]; - if ( ( strid >> 8 ) == 1 ) - { - // we found a start code - remove the ref_idc from the nal type - uint8_t nal_type = strid & 0x1f; - if ( nal_type == 0x05 ) - // h.264 IDR picture start - return 1; - - if ( stream->packetsize == 192 ) - { - // m2ts files have idr frames so keep looking for one - continue; - } - - // h.264 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) - uint8_t id = buf[i+1]; - if ( ( id >> 4 ) == 0x0b || ( id >> 2 ) == 0x25 || - id == 0x88 || id == 0x8a ) - { - return 1; - } - } - } - } - // didn't find an I-frame - return 0; - } - if ( stream->ts_stream_type[0] == 0xea ) - { - // we have an vc1 stream - for (i = 13 + adapt_len; i < 188; i++) - { - strid = (strid << 8) | buf[i]; - if ( strid == 0x10f ) - { - // the ffmpeg vc1 decoder requires a seq hdr code in the first - // frame. - return 1; - } - } - // didn't find an I-frame - return 0; - } - - // we don't understand the stream type so just say "yes" otherwise - // we'll discard all the video. - return 1; -} - static void hb_ts_stream_append_pkt(hb_stream_t *stream, int idx, const uint8_t *buf, int len) { if (stream->ts_pos[idx] + len > stream->ts_buf[idx]->alloc) @@ -2280,11 +2255,16 @@ static int hb_ts_stream_decode( hb_stream_t *stream, hb_buffer_t *obuf ) } } - // 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. + // If we don't have a PCR yet but the stream has PCRs just loop + // so we don't process anything until we have a clock reference. + // 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 ) ) + { + continue; + } // Get continuity // Continuity only increments for adaption values of 0x3 or 0x01 @@ -2297,11 +2277,31 @@ static int hb_ts_stream_decode( hb_stream_t *stream, hb_buffer_t *obuf ) int continuity = (buf[3] & 0xF); if ( continuity == stream->ts_streamcont[curstream] ) { - // we got a duplicate packet (usually used to introduce - // a PCR when one is needed). The only thing that can - // change in the dup is the PCR which we grabbed above - // so ignore the rest. - continue; + // 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 + // match. + uint8_t summary[8]; + + summary[0] = adaption; + summary[1] = adapt_len; + if (adapt_len + 4 + 6 + 9 <= 188) + { + memcpy(&summary[2], buf+4+adapt_len+9, 6); + } + else + { + memset(&summary[2], 0, 6); + } + if ( memcmp( summary, stream->ts_pkt_summary[curstream], 8 ) == 0 ) + { + // we got a duplicate packet (usually used to introduce + // a PCR when one is needed). The only thing that can + // change in the dup is the PCR which we grabbed above + // so ignore the rest. + continue; + } } if ( !start && (stream->ts_streamcont[curstream] != -1) && !stream->ts_skipbad[curstream] && @@ -2311,10 +2311,26 @@ static int hb_ts_stream_decode( hb_stream_t *stream, hb_buffer_t *obuf ) (int)continuity, (stream->ts_streamcont[curstream] + 1) & 0xf ); stream->ts_streamcont[curstream] = continuity; - continue; - } - stream->ts_streamcont[curstream] = continuity; - } + continue; + } + stream->ts_streamcont[curstream] = continuity; + + // Save a summary of this packet for later duplicate + // testing. The summary includes some header information + // and payload bytes. Should be enough to detect + // non-duplicates. + stream->ts_pkt_summary[curstream][0] = adaption; + stream->ts_pkt_summary[curstream][1] = adapt_len; + if (adapt_len + 4 + 6 + 9 <= 188) + { + memcpy(&stream->ts_pkt_summary[curstream][2], + buf+4+adapt_len+9, 6); + } + else + { + memset(&stream->ts_pkt_summary[curstream][2], 0, 6); + } + } /* If we get here the packet is valid - process its data */ @@ -2326,10 +2342,14 @@ static int hb_ts_stream_decode( hb_stream_t *stream, hb_buffer_t *obuf ) { // we're looking for the first video frame because we're // doing random access during 'scan' - if (curstream != 0 || !isIframe( stream, buf, adapt_len ) ) + if ( curstream != 0 || !isIframe( stream, buf, adapt_len ) ) { // not the video stream or didn't find an I frame - continue; + // but we'll only wait 255 video frames for an I frame. + if ( curstream != 0 || ++stream->need_keyframe ) + { + continue; + } } stream->need_keyframe = 0; } @@ -2460,7 +2480,7 @@ static void ffmpeg_add_codec( hb_stream_t *stream, int stream_index ) 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 ); + hb_avcodec_open( context, codec ); } // The ffmpeg stream reader / parser shares a lot of state with the @@ -2515,9 +2535,6 @@ static int ffmpeg_codec_param( hb_stream_t *stream, int stream_index ) // (the original scan stream was closed and no longer exists). static void ffmpeg_remap_stream( hb_stream_t *stream, hb_title_t *title ) { - // tell ffmpeg we want a pts on every frame it returns - stream->ffmpeg_ic->flags |= AVFMT_FLAG_GENPTS; - // all the video & audio came from the same stream so remapping // the video's stream slot takes care of everything. int slot = title->video_codec_param & (ffmpeg_sl_size - 1); @@ -2575,6 +2592,7 @@ static int ffmpeg_open( hb_stream_t *stream, hb_title_t *title ) stream->hb_stream_type = ffmpeg; stream->ffmpeg_pkt = malloc(sizeof(*stream->ffmpeg_pkt)); av_init_packet( stream->ffmpeg_pkt ); + stream->chapter_end = INT64_MAX; if ( title ) { @@ -2731,7 +2749,31 @@ static hb_title_t *ffmpeg_title_scan( hb_stream_t *stream ) title->container_name = strdup( ic->iformat->name ); title->data_rate = ic->bit_rate; - hb_deep_log( 2, "Found ffmpeg %d chapters, container=%s", ic->nb_chapters, ic->iformat->name); + hb_deep_log( 2, "Found ffmpeg %d chapters, container=%s", ic->nb_chapters, ic->iformat->name ); + + if( ic->nb_chapters != 0 ) + { + AVChapter *m; + uint64_t duration_sum = 0; + for( i = 0; i < ic->nb_chapters; i++ ) + if( ( m = ic->chapters[i] ) != NULL ) + { + hb_chapter_t * chapter; + chapter = calloc( sizeof( hb_chapter_t ), 1 ); + chapter->index = i+1; + chapter->duration = ( m->end / ( (double) m->time_base.num * m->time_base.den ) ) * 90000 - duration_sum; + duration_sum += chapter->duration; + chapter->hours = chapter->duration / 90000 / 3600; + chapter->minutes = ( ( chapter->duration / 90000 ) % 3600 ) / 60; + chapter->seconds = ( chapter->duration / 90000 ) % 60; + strcpy( chapter->title, m->title ); + hb_deep_log( 2, "Added chapter %i, name='%s', dur=%"PRIu64", (%02i:%02i:%02i)", + chapter->index, chapter->title, + chapter->duration, chapter->hours, + chapter->minutes, chapter->seconds ); + hb_list_add( title->list_chapter, chapter ); + } + } /* * Fill the metadata. @@ -2761,6 +2803,52 @@ static int64_t av_to_hb_pts( int64_t pts, double conv_factor ) return (int64_t)( (double)pts * conv_factor ); } +static int ffmpeg_is_keyframe( hb_stream_t *stream ) +{ + uint8_t *pkt; + + switch ( stream->ffmpeg_ic->streams[stream->ffmpeg_video_id]->codec->codec_id ) + { + case CODEC_ID_VC1: + // XXX the VC1 codec doesn't mark 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. + pkt = stream->ffmpeg_pkt->data; + if ( !pkt[0] && !pkt[1] && pkt[2] == 1 && pkt[3] == 0x0f ) + return 1; + + return 0; + + case CODEC_ID_WMV3: + // XXX the ffmpeg WMV3 codec doesn't mark key frames. + // Only M$ could make I-frame detection this complicated: there + // are two to four bits of unused junk ahead of the frame type + // so we have to look at the sequence header to find out how much + // to skip. Then there are three different ways of coding the type + // depending on whether it's main or advanced profile then whether + // there are bframes or not so we have to look at the sequence + // header to get that. + pkt = stream->ffmpeg_pkt->data; + uint8_t *seqhdr = stream->ffmpeg_ic->streams[stream->ffmpeg_video_id]->codec->extradata; + int pshift = 2; + if ( ( seqhdr[3] & 0x02 ) == 0 ) + // no FINTERPFLAG + ++pshift; + if ( ( seqhdr[3] & 0x80 ) == 0 ) + // no RANGEREDUCTION + ++pshift; + if ( seqhdr[3] & 0x70 ) + // stream has b-frames + return ( ( pkt[0] >> pshift ) & 0x3 ) == 0x01; + + return ( ( pkt[0] >> pshift ) & 0x2 ) == 0; + + default: + break; + } + return ( stream->ffmpeg_pkt->flags & PKT_FLAG_KEY ); +} + static int ffmpeg_read( hb_stream_t *stream, hb_buffer_t *buf ) { int err; @@ -2807,15 +2895,13 @@ static int ffmpeg_read( hb_stream_t *stream, hb_buffer_t *buf ) 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 ) + if ( stream->need_keyframe ) { - // 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 ) + // we've just done a seek (generally for scan or live preview) and + // want to start at a keyframe. Some ffmpeg codecs seek to a key + // frame but most don't. So we spin until we either get a keyframe + // or we've looked through 50 video frames without finding one. + if ( ! ffmpeg_is_keyframe( stream ) && ++stream->need_keyframe < 50 ) { av_free_packet( stream->ffmpeg_pkt ); goto again; @@ -2844,47 +2930,26 @@ static int ffmpeg_read( hb_stream_t *stream, hb_buffer_t *buf ) /* * Check to see whether this video buffer is on a chapter - * boundary, if so mark it as such in the buffer. The chapters for - * a stream have a simple duration for each chapter. So we keep - * track of what chapter we are in currently, and when it is due - * to end. + * boundary, if so mark it as such in the buffer then advance + * chapter_end to the end of the next chapter. + * If there are no chapters, chapter_end is always initialized to INT64_MAX + * (roughly 3 million years at our 90KHz clock rate) so the test + * below handles both the chapters & no chapters case. */ - hb_deep_log( 3, "title=0x%x, job=0x%x, chapter_markers=%d, time=%lld, chapter=%d, end_chapter=%lld", - stream->title, - stream->title ? (stream->title->job ? stream->title->job : 0x0) : 0x0, - stream->title ? (stream->title->job ? stream->title->job->chapter_markers : 2) : 0x0, - buf->start, stream->chapter, stream->chapter_end); - - if( stream->title && - stream->title->job && - stream->title->job->chapter_markers && - buf->id == stream->ffmpeg_video_id && - buf->start >= stream->chapter_end ) + if ( buf->id == stream->ffmpeg_video_id && buf->start >= stream->chapter_end ) { - hb_chapter_t *chapter = NULL; - - /* - * Store when this chapter ends using HB time. - */ - chapter = hb_list_item( stream->title->list_chapter, - stream->chapter ); - + hb_chapter_t *chapter = hb_list_item( stream->title->list_chapter, + stream->chapter+1 ); if( chapter ) { - if( stream->chapter != 0 ) - { - buf->new_chap = stream->chapter + 2; - } - - hb_deep_log( 2, "Starting chapter %i at %lld", buf->new_chap, buf->start); - stream->chapter_end += chapter->duration; stream->chapter++; - hb_deep_log( 2, "Looking for chapter %i at %lld", stream->chapter+1, stream->chapter_end ); + stream->chapter_end += chapter->duration; + buf->new_chap = stream->chapter + 1; + hb_deep_log( 2, "ffmpeg_read starting chapter %i at %"PRId64, + buf->new_chap, buf->start); } else { - /* - * Must have run out of chapters, stop looking. - */ - stream->chapter_end = -1; + // Must have run out of chapters, stop looking. + stream->chapter_end = INT64_MAX; } } else { buf->new_chap = 0; @@ -2896,15 +2961,19 @@ static int ffmpeg_read( hb_stream_t *stream, hb_buffer_t *buf ) static int ffmpeg_seek( hb_stream_t *stream, float frac ) { AVFormatContext *ic = stream->ffmpeg_ic; - int64_t pos = (double)ic->duration * (double)frac; - if ( pos ) + if ( frac > 0. ) { + int64_t pos = (double)ic->duration * (double)frac; + if ( ic->start_time != AV_NOPTS_VALUE && ic->start_time > 0 ) + { + pos += ic->start_time; + } av_seek_frame( ic, -1, pos, 0 ); stream->need_keyframe = 1; } else { - av_seek_frame( ic, -1, pos, AVSEEK_FLAG_BACKWARD ); + av_seek_frame( ic, -1, 0LL, AVSEEK_FLAG_BACKWARD ); } return 1; }