diff options
Diffstat (limited to 'libavformat/mpegts.c')
-rw-r--r-- | libavformat/mpegts.c | 455 |
1 files changed, 346 insertions, 109 deletions
diff --git a/libavformat/mpegts.c b/libavformat/mpegts.c index d623452130..67e94fb39e 100644 --- a/libavformat/mpegts.c +++ b/libavformat/mpegts.c @@ -2,20 +2,20 @@ * MPEG2 transport stream (aka DVB) demuxer * Copyright (c) 2002-2003 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -26,6 +26,7 @@ #include "libavutil/dict.h" #include "libavutil/mathematics.h" #include "libavutil/opt.h" +#include "libavutil/avassert.h" #include "libavcodec/bytestream.h" #include "libavcodec/get_bits.h" #include "libavcodec/mathops.h" @@ -52,7 +53,7 @@ enum MpegTSFilterType { typedef struct MpegTSFilter MpegTSFilter; -typedef int PESCallback(MpegTSFilter *f, const uint8_t *buf, int len, int is_start, int64_t pos); +typedef int PESCallback(MpegTSFilter *f, const uint8_t *buf, int len, int is_start, int64_t pos, int64_t cur_pcr); typedef struct MpegTSPESFilter { PESCallback *pes_cb; @@ -98,9 +99,11 @@ struct MpegTSContext { /** raw packet size, including FEC if present */ int raw_packet_size; - int pos47; - /** position corresponding to pos47, or 0 if pos47 invalid */ - int64_t pos; + int size_stat[3]; + int size_stat_count; +#define SIZE_STAT_THRESHOLD 10 + + int64_t pos47_full; /** if true, all pids are analyzed to find streams */ int auto_guess; @@ -108,6 +111,9 @@ struct MpegTSContext { /** compute exact PCR for each transport stream packet */ int mpeg2ts_compute_pcr; + /** fix dvb teletext pts */ + int fix_teletext_pts; + int64_t cur_pcr; /**< used to estimate the exact PCR */ int pcr_incr; /**< used to estimate the exact PCR */ @@ -126,12 +132,14 @@ struct MpegTSContext { unsigned int nb_prg; struct Program *prg; + int8_t crc_validity[NB_PID_MAX]; /** filters for various streams specified by PMT + for the PAT and PMT */ MpegTSFilter *pids[NB_PID_MAX]; + int current_pid; }; -static const AVOption options[] = { +static const AVOption mpegtsraw_options[] = { {"compute_pcr", "Compute exact PCR for each transport stream packet.", offsetof(MpegTSContext, mpeg2ts_compute_pcr), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, AV_OPT_FLAG_DECODING_PARAM }, { NULL }, @@ -140,7 +148,20 @@ static const AVOption options[] = { static const AVClass mpegtsraw_class = { .class_name = "mpegtsraw demuxer", .item_name = av_default_item_name, - .option = options, + .option = mpegtsraw_options, + .version = LIBAVUTIL_VERSION_INT, +}; + +static const AVOption mpegts_options[] = { + {"fix_teletext_pts", "Try to fix pts values of dvb teletext streams.", offsetof(MpegTSContext, fix_teletext_pts), AV_OPT_TYPE_INT, + {.i64 = 1}, 0, 1, AV_OPT_FLAG_DECODING_PARAM }, + { NULL }, +}; + +static const AVClass mpegts_class = { + .class_name = "mpegts demuxer", + .item_name = av_default_item_name, + .option = mpegts_options, .version = LIBAVUTIL_VERSION_INT, }; @@ -179,14 +200,30 @@ typedef struct PESContext { uint8_t header[MAX_PES_HEADER_SIZE]; AVBufferRef *buffer; SLConfigDescr sl; + int64_t last_pcr; } PESContext; extern AVInputFormat ff_mpegts_demuxer; +static void clear_avprogram(MpegTSContext *ts, unsigned int programid) +{ + AVProgram *prg = NULL; + int i; + for(i=0; i<ts->stream->nb_programs; i++) + if(ts->stream->programs[i]->id == programid){ + prg = ts->stream->programs[i]; + break; + } + if (!prg) + return; + prg->nb_stream_indexes = 0; +} + static void clear_program(MpegTSContext *ts, unsigned int programid) { int i; + clear_avprogram(ts, programid); for(i=0; i<ts->nb_prg; i++) if(ts->prg[i].id == programid) ts->prg[i].nb_pids = 0; @@ -229,6 +266,17 @@ static void add_pid_to_pmt(MpegTSContext *ts, unsigned int programid, unsigned i p->pids[p->nb_pids++] = pid; } +static void set_pcr_pid(AVFormatContext *s, unsigned int programid, unsigned int pid) +{ + int i; + for(i=0; i<s->nb_programs; i++) { + if(s->programs[i]->id == programid) { + s->programs[i]->pcr_pid = pid; + break; + } + } +} + /** * @brief discard_pid() decides if the pid is to be discarded according * to caller's programs selection @@ -280,6 +328,7 @@ static int discard_pid(MpegTSContext *ts, unsigned int pid) static void write_section_data(AVFormatContext *s, MpegTSFilter *tss1, const uint8_t *buf, int buf_size, int is_start) { + MpegTSContext *ts = s->priv_data; MpegTSSectionFilter *tss = &tss1->u.section_filter; int len; @@ -307,10 +356,19 @@ static void write_section_data(AVFormatContext *s, MpegTSFilter *tss1, } if (tss->section_h_size != -1 && tss->section_index >= tss->section_h_size) { + int crc_valid = 1; tss->end_of_section_reached = 1; - if (!tss->check_crc || - av_crc(av_crc_get_table(AV_CRC_32_IEEE), -1, - tss->section_buf, tss->section_h_size) == 0) + + if (tss->check_crc){ + crc_valid = !av_crc(av_crc_get_table(AV_CRC_32_IEEE), -1, tss->section_buf, tss->section_h_size); + if (crc_valid){ + ts->crc_validity[ tss1->pid ] = 100; + }else if(ts->crc_validity[ tss1->pid ] > -10){ + ts->crc_validity[ tss1->pid ]--; + }else + crc_valid = 2; + } + if (crc_valid) tss->section_cb(tss1, tss->section_buf, tss->section_h_size); } } @@ -400,7 +458,7 @@ static int analyze(const uint8_t *buf, int size, int packet_size, int *index){ memset(stat, 0, packet_size*sizeof(int)); for(x=i=0; i<size-3; i++){ - if(buf[i] == 0x47 && !(buf[i+1] & 0x80) && (buf[i+3] & 0x30)){ + if(buf[i] == 0x47 && !(buf[i+1] & 0x80) && buf[i+3] != 0x47){ stat[x]++; if(stat[x] > best_score){ best_score= stat[x]; @@ -535,7 +593,11 @@ static const StreamType ISO_types[] = { { 0x04, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_MP3 }, { 0x0f, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AAC }, { 0x10, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MPEG4 }, + /* Makito encoder sets stream type 0x11 for AAC, + * so auto-detect LOAS/LATM instead of hardcoding it. */ +#if !CONFIG_LOAS_DEMUXER { 0x11, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AAC_LATM }, /* LATM syntax */ +#endif { 0x1b, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_H264 }, { 0x42, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_CAVS }, { 0xd1, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_DIRAC }, @@ -551,6 +613,8 @@ static const StreamType HDMV_types[] = { { 0x84, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_EAC3 }, { 0x85, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_DTS }, /* DTS HD */ { 0x86, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_DTS }, /* DTS HD MASTER*/ + { 0xa1, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_EAC3 }, /* E-AC3 Secondary Audio */ + { 0xa2, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_DTS }, /* DTS Express Secondary Audio */ { 0x90, AVMEDIA_TYPE_SUBTITLE, AV_CODEC_ID_HDMV_PGS_SUBTITLE }, { 0 }, }; @@ -569,6 +633,7 @@ static const StreamType REGD_types[] = { { MKTAG('D','T','S','1'), AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_DTS }, { MKTAG('D','T','S','2'), AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_DTS }, { MKTAG('D','T','S','3'), AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_DTS }, + { MKTAG('K','L','V','A'), AVMEDIA_TYPE_DATA, AV_CODEC_ID_SMPTE_KLV }, { MKTAG('V','C','-','1'), AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_VC1 }, { 0 }, }; @@ -586,10 +651,16 @@ static const StreamType DESC_types[] = { static void mpegts_find_stream_type(AVStream *st, uint32_t stream_type, const StreamType *types) { + if (avcodec_is_open(st->codec)) { + av_log(NULL, AV_LOG_DEBUG, "cannot set stream info, codec is open\n"); + return; + } + for (; types->stream_type; types++) { if (stream_type == types->stream_type) { st->codec->codec_type = types->codec_type; st->codec->codec_id = types->codec_id; + st->request_probe = 0; return; } } @@ -598,6 +669,14 @@ static void mpegts_find_stream_type(AVStream *st, static int mpegts_set_stream_info(AVStream *st, PESContext *pes, uint32_t stream_type, uint32_t prog_reg_desc) { + int old_codec_type= st->codec->codec_type; + int old_codec_id = st->codec->codec_id; + + if (avcodec_is_open(st->codec)) { + av_log(pes->stream, AV_LOG_DEBUG, "cannot set stream info, codec is open\n"); + return 0; + } + avpriv_set_pts_info(st, 33, 1, 90000); st->priv_data = pes; st->codec->codec_type = AVMEDIA_TYPE_DATA; @@ -613,7 +692,8 @@ static int mpegts_set_stream_info(AVStream *st, PESContext *pes, st->codec->codec_tag = pes->stream_type; mpegts_find_stream_type(st, pes->stream_type, ISO_types); - if (prog_reg_desc == AV_RL32("HDMV") && + if ((prog_reg_desc == AV_RL32("HDMV") || + prog_reg_desc == AV_RL32("HDPR")) && st->codec->codec_id == AV_CODEC_ID_NONE) { mpegts_find_stream_type(st, pes->stream_type, HDMV_types); if (pes->stream_type == 0x83) { @@ -643,6 +723,10 @@ static int mpegts_set_stream_info(AVStream *st, PESContext *pes, } if (st->codec->codec_id == AV_CODEC_ID_NONE) mpegts_find_stream_type(st, pes->stream_type, MISC_types); + if (st->codec->codec_id == AV_CODEC_ID_NONE){ + st->codec->codec_id = old_codec_id; + st->codec->codec_type= old_codec_type; + } return 0; } @@ -681,6 +765,13 @@ static void new_pes_packet(PESContext *pes, AVPacket *pkt) pes->flags = 0; } +static uint64_t get_ts64(GetBitContext *gb, int bits) +{ + if (get_bits_left(gb) < bits) + return AV_NOPTS_VALUE; + return get_bits64(gb, bits); +} + static int read_sl_header(PESContext *pes, SLConfigDescr *sl, const uint8_t *buf, int buf_size) { GetBitContext gb; @@ -688,6 +779,7 @@ static int read_sl_header(PESContext *pes, SLConfigDescr *sl, const uint8_t *buf int padding_flag = 0, padding_bits = 0, inst_bitrate_flag = 0; int dts_flag = -1, cts_flag = -1; int64_t dts = AV_NOPTS_VALUE, cts = AV_NOPTS_VALUE; + init_get_bits(&gb, buf, buf_size*8); if (sl->use_au_start) @@ -726,9 +818,9 @@ static int read_sl_header(PESContext *pes, SLConfigDescr *sl, const uint8_t *buf if (sl->inst_bitrate_len) inst_bitrate_flag = get_bits1(&gb); if (dts_flag == 1) - dts = get_bits64(&gb, sl->timestamp_len); + dts = get_ts64(&gb, sl->timestamp_len); if (cts_flag == 1) - cts = get_bits64(&gb, sl->timestamp_len); + cts = get_ts64(&gb, sl->timestamp_len); if (sl->au_len > 0) skip_bits_long(&gb, sl->au_len); if (inst_bitrate_flag) @@ -749,7 +841,7 @@ static int read_sl_header(PESContext *pes, SLConfigDescr *sl, const uint8_t *buf /* return non zero if a packet could be constructed */ static int mpegts_push_data(MpegTSFilter *filter, const uint8_t *buf, int buf_size, int is_start, - int64_t pos) + int64_t pos, int64_t pcr) { PESContext *pes = filter->u.pes_filter.opaque; MpegTSContext *ts = pes->ts; @@ -759,6 +851,9 @@ static int mpegts_push_data(MpegTSFilter *filter, if(!ts->pkt) return 0; + if (pcr != -1) + pes->last_pcr = pcr; + if (is_start) { if (pes->state == MPEGTS_PAYLOAD && pes->data_index > 0) { new_pes_packet(pes, ts->pkt); @@ -819,10 +914,10 @@ static int mpegts_push_data(MpegTSFilter *filter, code != 0x1ff && code != 0x1f2 && /* program_stream_directory, DSMCC_stream */ code != 0x1f8) { /* ITU-T Rec. H.222.1 type E stream */ pes->state = MPEGTS_PESHEADER; - if (pes->st->codec->codec_id == AV_CODEC_ID_NONE) { + if (pes->st->codec->codec_id == AV_CODEC_ID_NONE && !pes->st->request_probe) { av_dlog(pes->stream, "pid=%x stream_type=%x probing\n", pes->pid, pes->stream_type); - pes->st->codec->codec_id = AV_CODEC_ID_PROBE; + pes->st->request_probe= 1; } } else { pes->state = MPEGTS_PAYLOAD; @@ -905,6 +1000,32 @@ static int mpegts_push_data(MpegTSFilter *filter, p += sl_header_bytes; buf_size -= sl_header_bytes; } + if (pes->ts->fix_teletext_pts && pes->st->codec->codec_id == AV_CODEC_ID_DVB_TELETEXT) { + AVProgram *p = NULL; + while ((p = av_find_program_from_stream(pes->stream, p, pes->st->index))) { + if (p->pcr_pid != -1 && p->discard != AVDISCARD_ALL) { + MpegTSFilter *f = pes->ts->pids[p->pcr_pid]; + if (f && f->type == MPEGTS_PES) { + PESContext *pcrpes = f->u.pes_filter.opaque; + if (pcrpes && pcrpes->last_pcr != -1 && pcrpes->st && pcrpes->st->discard != AVDISCARD_ALL) { + // teletext packets do not always have correct timestamps, + // the standard says they should be handled after 40.6 ms at most, + // and the pcr error to this packet should be no more than 100 ms. + // TODO: we should interpolate the PCR, not just use the last one + int64_t pcr = pcrpes->last_pcr / 300; + pes->st->pts_wrap_reference = pcrpes->st->pts_wrap_reference; + pes->st->pts_wrap_behavior = pcrpes->st->pts_wrap_behavior; + if (pes->dts == AV_NOPTS_VALUE || pes->dts < pcr) { + pes->pts = pes->dts = pcr; + } else if (pes->dts > pcr + 3654 + 9000) { + pes->pts = pes->dts = pcr + 3654 + 9000; + } + break; + } + } + } + } + } } break; case MPEGTS_PAYLOAD: @@ -961,6 +1082,7 @@ static PESContext *add_pes_stream(MpegTSContext *ts, int pid, int pcr_pid) pes->state = MPEGTS_SKIP; pes->pts = AV_NOPTS_VALUE; pes->dts = AV_NOPTS_VALUE; + pes->last_pcr = -1; tss = mpegts_open_pes_filter(ts, pid, mpegts_push_data, pes); if (!tss) { av_free(pes); @@ -1292,15 +1414,17 @@ int ff_parse_mpeg2_descriptor(AVFormatContext *fc, AVStream *st, int stream_type break; case 0x1F: /* FMC descriptor */ get16(pp, desc_end); - if (mp4_descr_count > 0 && st->codec->codec_id == AV_CODEC_ID_AAC_LATM && + if (mp4_descr_count > 0 && (st->codec->codec_id == AV_CODEC_ID_AAC_LATM || st->request_probe>0) && mp4_descr->dec_config_descr_len && mp4_descr->es_id == pid) { AVIOContext pb; ffio_init_context(&pb, mp4_descr->dec_config_descr, mp4_descr->dec_config_descr_len, 0, NULL, NULL, NULL, NULL); ff_mp4_read_dec_config_descr(fc, st, &pb); if (st->codec->codec_id == AV_CODEC_ID_AAC && - st->codec->extradata_size > 0) - st->need_parsing = 0; + st->codec->extradata_size > 0){ + st->request_probe= st->need_parsing = 0; + st->codec->codec_type= AVMEDIA_TYPE_AUDIO; + } } break; case 0x56: /* DVB teletext descriptor */ @@ -1362,6 +1486,9 @@ int ff_parse_mpeg2_descriptor(AVFormatContext *fc, AVStream *st, int stream_type if (st->codec->codec_id == AV_CODEC_ID_NONE) mpegts_find_stream_type(st, st->codec->codec_tag, REGD_types); break; + case 0x52: /* stream identifier descriptor */ + st->stream_identifier = 1 + get8(pp, desc_end); + break; default: break; } @@ -1404,6 +1531,7 @@ static void pmt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len return; pcr_pid &= 0x1fff; add_pid_to_pmt(ts, h->id, pcr_pid); + set_pcr_pid(ts->stream, h->id, pcr_pid); av_dlog(ts->stream, "pcr_pid=0x%x\n", pcr_pid); @@ -1440,7 +1568,7 @@ static void pmt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len // stop parsing after pmt, we found header if (!ts->stream->nb_streams) - ts->stop_parse = 1; + ts->stop_parse = 2; for(;;) { st = 0; @@ -1452,12 +1580,16 @@ static void pmt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len if (pid < 0) break; pid &= 0x1fff; + if (pid == ts->current_pid) + break; /* now create stream */ if (ts->pids[pid] && ts->pids[pid]->type == MPEGTS_PES) { pes = ts->pids[pid]->u.pes_filter.opaque; if (!pes->st) { pes->st = avformat_new_stream(pes->stream, NULL); + if (!pes->st) + goto out; pes->st->id = pes->pid; } st = pes->st; @@ -1466,6 +1598,8 @@ static void pmt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len pes = add_pes_stream(ts, pid, pcr_pid); if (pes) { st = avformat_new_stream(pes->stream, NULL); + if (!st) + goto out; st->id = pes->pid; } } else { @@ -1474,6 +1608,8 @@ static void pmt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len st = ts->stream->streams[idx]; } else { st = avformat_new_stream(ts->stream, NULL); + if (!st) + goto out; st->id = pid; st->codec->codec_type = AVMEDIA_TYPE_DATA; } @@ -1520,6 +1656,7 @@ static void pat_cb(MpegTSFilter *filter, const uint8_t *section, int section_len SectionHeader h1, *h = &h1; const uint8_t *p, *p_end; int sid, pmt_pid; + AVProgram *program; av_dlog(ts->stream, "PAT:\n"); hex_dump_debug(ts->stream, section, section_len); @@ -1531,6 +1668,8 @@ static void pat_cb(MpegTSFilter *filter, const uint8_t *section, int section_len if (h->tid != PAT_TID) return; + ts->stream->ts_id = h->id; + clear_programs(ts); for(;;) { sid = get16(&p, p_end); @@ -1541,20 +1680,42 @@ static void pat_cb(MpegTSFilter *filter, const uint8_t *section, int section_len break; pmt_pid &= 0x1fff; + if (pmt_pid == ts->current_pid) + break; + av_dlog(ts->stream, "sid=0x%x pid=0x%x\n", sid, pmt_pid); if (sid == 0x0000) { /* NIT info */ } else { - av_new_program(ts->stream, sid); - if (ts->pids[pmt_pid]) - mpegts_close_filter(ts, ts->pids[pmt_pid]); - mpegts_open_section_filter(ts, pmt_pid, pmt_cb, ts, 1); + MpegTSFilter *fil = ts->pids[pmt_pid]; + program = av_new_program(ts->stream, sid); + program->program_num = sid; + program->pmt_pid = pmt_pid; + if (fil) + if ( fil->type != MPEGTS_SECTION + || fil->pid != pmt_pid + || fil->u.section_filter.section_cb != pmt_cb) + mpegts_close_filter(ts, ts->pids[pmt_pid]); + + if (!ts->pids[pmt_pid]) + mpegts_open_section_filter(ts, pmt_pid, pmt_cb, ts, 1); add_pat_entry(ts, sid); add_pid_to_pmt(ts, sid, 0); //add pat pid to program add_pid_to_pmt(ts, sid, pmt_pid); } } + + if (sid < 0) { + int i,j; + for (j=0; j<ts->stream->nb_programs; j++) { + for (i=0; i<ts->nb_prg; i++) + if (ts->prg[i].id == ts->stream->programs[j]->id) + break; + if (i==ts->nb_prg) + clear_avprogram(ts, ts->stream->programs[j]->id); + } + } } static void sdt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len) @@ -1634,6 +1795,9 @@ static void sdt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len } } +static int parse_pcr(int64_t *ppcr_high, int *ppcr_low, + const uint8_t *packet); + /* handle one TS packet */ static int handle_packet(MpegTSContext *ts, const uint8_t *packet) { @@ -1655,6 +1819,7 @@ static int handle_packet(MpegTSContext *ts, const uint8_t *packet) } if (!tss) return 0; + ts->current_pid = pid; afc = (packet[3] >> 4) & 3; if (afc == 0) /* reserved value */ @@ -1675,7 +1840,7 @@ static int handle_packet(MpegTSContext *ts, const uint8_t *packet) tss->last_cc = cc; if (!cc_ok) { - av_log(ts->stream, AV_LOG_WARNING, + av_log(ts->stream, AV_LOG_DEBUG, "Continuity check failed for pid %d expected %d got %d\n", pid, expected_cc, cc); if(tss->type == MPEGTS_PES) { @@ -1697,7 +1862,10 @@ static int handle_packet(MpegTSContext *ts, const uint8_t *packet) return 0; pos = avio_tell(ts->stream->pb); - MOD_UNLIKELY(ts->pos47, pos, ts->raw_packet_size, ts->pos); + if (pos >= 0) { + av_assert0(pos >= TS_PACKET_SIZE); + ts->pos47_full = pos - TS_PACKET_SIZE; + } if (tss->type == MPEGTS_SECTION) { if (is_start) { @@ -1726,15 +1894,53 @@ static int handle_packet(MpegTSContext *ts, const uint8_t *packet) } } else { int ret; + int64_t pcr = -1; + int64_t pcr_h; + int pcr_l; + if (parse_pcr(&pcr_h, &pcr_l, packet) == 0) + pcr = pcr_h * 300 + pcr_l; // Note: The position here points actually behind the current packet. if ((ret = tss->u.pes_filter.pes_cb(tss, p, p_end - p, is_start, - pos - ts->raw_packet_size)) < 0) + pos - ts->raw_packet_size, pcr)) < 0) return ret; } return 0; } +static void reanalyze(MpegTSContext *ts) { + AVIOContext *pb = ts->stream->pb; + int64_t pos = avio_tell(pb); + if(pos < 0) + return; + pos -= ts->pos47_full; + if (pos == TS_PACKET_SIZE) { + ts->size_stat[0] ++; + } else if (pos == TS_DVHS_PACKET_SIZE) { + ts->size_stat[1] ++; + } else if (pos == TS_FEC_PACKET_SIZE) { + ts->size_stat[2] ++; + } + + ts->size_stat_count ++; + if(ts->size_stat_count > SIZE_STAT_THRESHOLD) { + int newsize = 0; + if (ts->size_stat[0] > SIZE_STAT_THRESHOLD) { + newsize = TS_PACKET_SIZE; + } else if (ts->size_stat[1] > SIZE_STAT_THRESHOLD) { + newsize = TS_DVHS_PACKET_SIZE; + } else if (ts->size_stat[2] > SIZE_STAT_THRESHOLD) { + newsize = TS_FEC_PACKET_SIZE; + } + if (newsize && newsize != ts->raw_packet_size) { + av_log(ts->stream, AV_LOG_WARNING, "changing packet size to %d\n", newsize); + ts->raw_packet_size = newsize; + } + ts->size_stat_count = 0; + memset(ts->size_stat, 0, sizeof(ts->size_stat)); + } +} + /* XXX: try to find a better synchro over several packets (use get_packet_size() ?) */ static int mpegts_resync(AVFormatContext *s) @@ -1744,10 +1950,11 @@ static int mpegts_resync(AVFormatContext *s) for(i = 0;i < MAX_RESYNC_SIZE; i++) { c = avio_r8(pb); - if (pb->eof_reached) + if (url_feof(pb)) return -1; if (c == 0x47) { avio_seek(pb, -1, SEEK_CUR); + reanalyze(s->priv_data); return 0; } } @@ -1769,7 +1976,7 @@ static int read_packet(AVFormatContext *s, uint8_t *buf, int raw_packet_size, co /* check packet sync byte */ if ((*data)[0] != 0x47) { /* find a new packet start */ - avio_seek(pb, -TS_PACKET_SIZE, SEEK_CUR); + avio_seek(pb, -raw_packet_size, SEEK_CUR); if (mpegts_resync(s) < 0) return AVERROR(EAGAIN); else @@ -1792,7 +1999,7 @@ static void finished_reading_packet(AVFormatContext *s, int raw_packet_size) static int handle_packets(MpegTSContext *ts, int nb_packets) { AVFormatContext *s = ts->stream; - uint8_t packet[TS_PACKET_SIZE+FF_INPUT_BUFFER_PADDING_SIZE]; + uint8_t packet[TS_PACKET_SIZE + FF_INPUT_BUFFER_PADDING_SIZE]; const uint8_t *data; int packet_num, ret = 0; @@ -1807,6 +2014,7 @@ static int handle_packets(MpegTSContext *ts, int nb_packets) av_buffer_unref(&pes->buffer); pes->data_index = 0; pes->state = MPEGTS_SKIP; /* skip until pes header */ + pes->last_pcr = -1; } ts->pids[i]->last_cc = -1; } @@ -1817,11 +2025,15 @@ static int handle_packets(MpegTSContext *ts, int nb_packets) packet_num = 0; memset(packet + TS_PACKET_SIZE, 0, FF_INPUT_BUFFER_PADDING_SIZE); for(;;) { - if (ts->stop_parse>0) - break; packet_num++; - if (nb_packets != 0 && packet_num >= nb_packets) + if (nb_packets != 0 && packet_num >= nb_packets || + ts->stop_parse > 1) { + ret = AVERROR(EAGAIN); break; + } + if (ts->stop_parse > 0) + break; + ret = read_packet(s, packet, ts->raw_packet_size, &data); if (ret != 0) break; @@ -1837,24 +2049,34 @@ static int handle_packets(MpegTSContext *ts, int nb_packets) static int mpegts_probe(AVProbeData *p) { const int size= p->buf_size; - int score, fec_score, dvhs_score; + int maxscore=0; + int sumscore=0; + int i; int check_count= size / TS_FEC_PACKET_SIZE; #define CHECK_COUNT 10 +#define CHECK_BLOCK 100 if (check_count < CHECK_COUNT) return -1; - score = analyze(p->buf, TS_PACKET_SIZE *check_count, TS_PACKET_SIZE , NULL)*CHECK_COUNT/check_count; - dvhs_score= analyze(p->buf, TS_DVHS_PACKET_SIZE*check_count, TS_DVHS_PACKET_SIZE, NULL)*CHECK_COUNT/check_count; - fec_score = analyze(p->buf, TS_FEC_PACKET_SIZE *check_count, TS_FEC_PACKET_SIZE , NULL)*CHECK_COUNT/check_count; - av_dlog(NULL, "score: %d, dvhs_score: %d, fec_score: %d \n", - score, dvhs_score, fec_score); + for (i=0; i<check_count; i+=CHECK_BLOCK){ + int left = FFMIN(check_count - i, CHECK_BLOCK); + int score = analyze(p->buf + TS_PACKET_SIZE *i, TS_PACKET_SIZE *left, TS_PACKET_SIZE , NULL); + int dvhs_score= analyze(p->buf + TS_DVHS_PACKET_SIZE*i, TS_DVHS_PACKET_SIZE*left, TS_DVHS_PACKET_SIZE, NULL); + int fec_score = analyze(p->buf + TS_FEC_PACKET_SIZE *i, TS_FEC_PACKET_SIZE *left, TS_FEC_PACKET_SIZE , NULL); + score = FFMAX3(score, dvhs_score, fec_score); + sumscore += score; + maxscore = FFMAX(maxscore, score); + } + + sumscore = sumscore*CHECK_COUNT/check_count; + maxscore = maxscore*CHECK_COUNT/CHECK_BLOCK; -// we need a clear definition for the returned score otherwise things will become messy sooner or later - if (score > fec_score && score > dvhs_score && score > 6) return AVPROBE_SCORE_MAX + score - CHECK_COUNT; - else if(dvhs_score > score && dvhs_score > fec_score && dvhs_score > 6) return AVPROBE_SCORE_MAX + dvhs_score - CHECK_COUNT; - else if( fec_score > 6) return AVPROBE_SCORE_MAX + fec_score - CHECK_COUNT; - else return -1; + av_dlog(0, "TS score: %d %d\n", sumscore, maxscore); + + if (sumscore > 6) return AVPROBE_SCORE_MAX + sumscore - CHECK_COUNT; + else if (maxscore > 6) return AVPROBE_SCORE_MAX/2 + sumscore - CHECK_COUNT; + else return -1; } /* return the 90kHz PCR and the extension for the 27MHz PCR. return @@ -1886,22 +2108,33 @@ static int parse_pcr(int64_t *ppcr_high, int *ppcr_low, return 0; } +static void seek_back(AVFormatContext *s, AVIOContext *pb, int64_t pos) { + + /* NOTE: We attempt to seek on non-seekable files as well, as the + * probe buffer usually is big enough. Only warn if the seek failed + * on files where the seek should work. */ + if (avio_seek(pb, pos, SEEK_SET) < 0) + av_log(s, pb->seekable ? AV_LOG_ERROR : AV_LOG_INFO, "Unable to seek back to the start\n"); +} + static int mpegts_read_header(AVFormatContext *s) { MpegTSContext *ts = s->priv_data; AVIOContext *pb = s->pb; - uint8_t buf[5*1024]; + uint8_t buf[8*1024]={0}; int len; int64_t pos; - /* read the first 1024 bytes to get packet size */ + ffio_ensure_seekback(pb, s->probesize); + + /* read the first 8192 bytes to get packet size */ pos = avio_tell(pb); len = avio_read(pb, buf, sizeof(buf)); - if (len != sizeof(buf)) - goto fail; - ts->raw_packet_size = get_packet_size(buf, sizeof(buf)); - if (ts->raw_packet_size <= 0) - goto fail; + ts->raw_packet_size = get_packet_size(buf, len); + if (ts->raw_packet_size <= 0) { + av_log(s, AV_LOG_WARNING, "Could not detect TS packet size, defaulting to non-FEC/DVHS\n"); + ts->raw_packet_size = TS_PACKET_SIZE; + } ts->stream = s; ts->auto_guess = 0; @@ -1909,8 +2142,7 @@ static int mpegts_read_header(AVFormatContext *s) /* normal demux */ /* first do a scan to get all the services */ - if (avio_seek(pb, pos, SEEK_SET) < 0 && pb->seekable) - av_log(s, AV_LOG_ERROR, "Unable to seek back to the start\n"); + seek_back(s, pb, pos); mpegts_open_section_filter(ts, SDT_PID, sdt_cb, ts, 1); @@ -1948,7 +2180,7 @@ static int mpegts_read_header(AVFormatContext *s) for(;;) { ret = read_packet(s, packet, ts->raw_packet_size, &data); if (ret < 0) - return -1; + goto fail; pid = AV_RB16(data + 1) & 0x1fff; if ((pcr_pid == -1 || pcr_pid == pid) && parse_pcr(&pcr_h, &pcr_l, data) == 0) { @@ -1976,7 +2208,7 @@ static int mpegts_read_header(AVFormatContext *s) st->start_time / 1000000.0, pcrs[0] / 27e6, ts->pcr_incr); } - avio_seek(pb, pos, SEEK_SET); + seek_back(s, pb, pos); return 0; fail: return -1; @@ -2042,6 +2274,7 @@ static int mpegts_read_packet(AVFormatContext *s, ts->pkt = pkt; ret = handle_packets(ts, 0); if (ret < 0) { + av_free_packet(ts->pkt); /* flush pes data left */ for (i = 0; i < NB_PID_MAX; i++) { if (ts->pids[i] && ts->pids[i]->type == MPEGTS_PES) { @@ -2078,66 +2311,68 @@ static int mpegts_read_close(AVFormatContext *s) return 0; } -static int64_t mpegts_get_pcr(AVFormatContext *s, int stream_index, +static av_unused int64_t mpegts_get_pcr(AVFormatContext *s, int stream_index, int64_t *ppos, int64_t pos_limit) { MpegTSContext *ts = s->priv_data; int64_t pos, timestamp; uint8_t buf[TS_PACKET_SIZE]; int pcr_l, pcr_pid = ((PESContext*)s->streams[stream_index]->priv_data)->pcr_pid; - const int find_next= 1; - pos = ((*ppos + ts->raw_packet_size - 1 - ts->pos47) / ts->raw_packet_size) * ts->raw_packet_size + ts->pos47; - if (find_next) { - for(;;) { - avio_seek(s->pb, pos, SEEK_SET); - if (avio_read(s->pb, buf, TS_PACKET_SIZE) != TS_PACKET_SIZE) + int pos47 = ts->pos47_full % ts->raw_packet_size; + pos = ((*ppos + ts->raw_packet_size - 1 - pos47) / ts->raw_packet_size) * ts->raw_packet_size + pos47; + while(pos < pos_limit) { + if (avio_seek(s->pb, pos, SEEK_SET) < 0) + return AV_NOPTS_VALUE; + if (avio_read(s->pb, buf, TS_PACKET_SIZE) != TS_PACKET_SIZE) + return AV_NOPTS_VALUE; + if (buf[0] != 0x47) { + avio_seek(s->pb, -TS_PACKET_SIZE, SEEK_CUR); + if (mpegts_resync(s) < 0) return AV_NOPTS_VALUE; - if ((pcr_pid < 0 || (AV_RB16(buf + 1) & 0x1fff) == pcr_pid) && - parse_pcr(×tamp, &pcr_l, buf) == 0) { - break; - } - pos += ts->raw_packet_size; + pos = avio_tell(s->pb); + continue; } - } else { - for(;;) { - pos -= ts->raw_packet_size; - if (pos < 0) - return AV_NOPTS_VALUE; - avio_seek(s->pb, pos, SEEK_SET); - if (avio_read(s->pb, buf, TS_PACKET_SIZE) != TS_PACKET_SIZE) - return AV_NOPTS_VALUE; - if ((pcr_pid < 0 || (AV_RB16(buf + 1) & 0x1fff) == pcr_pid) && - parse_pcr(×tamp, &pcr_l, buf) == 0) { - break; - } + if ((pcr_pid < 0 || (AV_RB16(buf + 1) & 0x1fff) == pcr_pid) && + parse_pcr(×tamp, &pcr_l, buf) == 0) { + *ppos = pos; + return timestamp; } + pos += ts->raw_packet_size; } - *ppos = pos; - return timestamp; + return AV_NOPTS_VALUE; } -static int read_seek(AVFormatContext *s, int stream_index, int64_t target_ts, int flags){ +static int64_t mpegts_get_dts(AVFormatContext *s, int stream_index, + int64_t *ppos, int64_t pos_limit) +{ MpegTSContext *ts = s->priv_data; - uint8_t buf[TS_PACKET_SIZE]; int64_t pos; - - if (ff_seek_frame_binary(s, stream_index, target_ts, flags) < 0) - return -1; - - pos= avio_tell(s->pb); - - for(;;) { - avio_seek(s->pb, pos, SEEK_SET); - if (avio_read(s->pb, buf, TS_PACKET_SIZE) != TS_PACKET_SIZE) - return -1; -// pid = AV_RB16(buf + 1) & 0x1fff; - if(buf[1] & 0x40) break; - pos += ts->raw_packet_size; + int pos47 = ts->pos47_full % ts->raw_packet_size; + pos = ((*ppos + ts->raw_packet_size - 1 - pos47) / ts->raw_packet_size) * ts->raw_packet_size + pos47; + ff_read_frame_flush(s); + if (avio_seek(s->pb, pos, SEEK_SET) < 0) + return AV_NOPTS_VALUE; + while(pos < pos_limit) { + int ret; + AVPacket pkt; + av_init_packet(&pkt); + ret= av_read_frame(s, &pkt); + if(ret < 0) + return AV_NOPTS_VALUE; + av_free_packet(&pkt); + if(pkt.dts != AV_NOPTS_VALUE && pkt.pos >= 0){ + ff_reduce_index(s, pkt.stream_index); + av_add_index_entry(s->streams[pkt.stream_index], pkt.pos, pkt.dts, 0, 0, AVINDEX_KEYFRAME /* FIXME keyframe? */); + if(pkt.stream_index == stream_index && pkt.pos >= *ppos){ + *ppos= pkt.pos; + return pkt.dts; + } + } + pos = pkt.pos; } - avio_seek(s->pb, pos, SEEK_SET); - return 0; + return AV_NOPTS_VALUE; } /**************************************************************/ @@ -2154,6 +2389,9 @@ MpegTSContext *ff_mpegts_parse_open(AVFormatContext *s) ts->raw_packet_size = TS_PACKET_SIZE; ts->stream = s; ts->auto_guess = 1; + mpegts_open_section_filter(ts, SDT_PID, sdt_cb, ts, 1); + mpegts_open_section_filter(ts, PAT_PID, pat_cb, ts, 1); + return ts; } @@ -2166,10 +2404,8 @@ int ff_mpegts_parse_packet(MpegTSContext *ts, AVPacket *pkt, len1 = len; ts->pkt = pkt; - ts->stop_parse = 0; for(;;) { - if (ts->stop_parse>0) - break; + ts->stop_parse = 0; if (len < TS_PACKET_SIZE) return -1; if (buf[0] != 0x47) { @@ -2179,6 +2415,8 @@ int ff_mpegts_parse_packet(MpegTSContext *ts, AVPacket *pkt, handle_packet(ts, buf); buf += TS_PACKET_SIZE; len -= TS_PACKET_SIZE; + if (ts->stop_parse == 1) + break; } } return len1 - len; @@ -2198,9 +2436,9 @@ AVInputFormat ff_mpegts_demuxer = { .read_header = mpegts_read_header, .read_packet = mpegts_read_packet, .read_close = mpegts_read_close, - .read_seek = read_seek, - .read_timestamp = mpegts_get_pcr, + .read_timestamp = mpegts_get_dts, .flags = AVFMT_SHOW_IDS | AVFMT_TS_DISCONT, + .priv_class = &mpegts_class, }; AVInputFormat ff_mpegtsraw_demuxer = { @@ -2210,8 +2448,7 @@ AVInputFormat ff_mpegtsraw_demuxer = { .read_header = mpegts_read_header, .read_packet = mpegts_raw_read_packet, .read_close = mpegts_read_close, - .read_seek = read_seek, - .read_timestamp = mpegts_get_pcr, + .read_timestamp = mpegts_get_dts, .flags = AVFMT_SHOW_IDS | AVFMT_TS_DISCONT, .priv_class = &mpegtsraw_class, }; |