X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=libavformat%2Fmpegts.c;h=2ef80c088c5a94a3e288a5cf83b1096fad1269c3;hb=2cface71ca58b1ab811efae7d22f3264f362f672;hp=0d38e6f92a9bb7994846c8d5725f45117f66c6fb;hpb=d23612efe13a14d3ffe84198d643fa1ad2ded660;p=ffmpeg diff --git a/libavformat/mpegts.c b/libavformat/mpegts.c index 0d38e6f92a9..2ef80c088c5 100644 --- a/libavformat/mpegts.c +++ b/libavformat/mpegts.c @@ -21,6 +21,7 @@ //#define DEBUG //#define DEBUG_SEEK +//#define USE_SYNCPOINT_SEARCH #include "libavutil/crc.h" #include "libavutil/intreadwrite.h" @@ -28,20 +29,17 @@ #include "avformat.h" #include "mpegts.h" #include "internal.h" - -/* 1.0 second at 24Mbit/s */ -#define MAX_SCAN_PACKETS 32000 +#include "avio_internal.h" +#include "seek.h" +#include "mpeg.h" +#include "isom.h" /* maximum size in which we look for synchronisation if synchronisation is lost */ -#define MAX_RESYNC_SIZE 4096 +#define MAX_RESYNC_SIZE 65536 #define MAX_PES_PAYLOAD 200*1024 -typedef struct PESContext PESContext; - -static PESContext* add_pes_stream(MpegTSContext *ts, int pid, int pcr_pid, int stream_type); - enum MpegTSFilterType { MPEGTS_PES, MPEGTS_SECTION, @@ -139,25 +137,27 @@ enum MpegTSState { #define PES_HEADER_SIZE 9 #define MAX_PES_HEADER_SIZE (9 + 255) -struct PESContext { +typedef struct PESContext { int pid; int pcr_pid; /**< if -1 then all packets containing PCR are considered */ int stream_type; MpegTSContext *ts; AVFormatContext *stream; AVStream *st; + AVStream *sub_st; /**< stream for the embedded AC3 stream in HDMV TrueHD */ enum MpegTSState state; /* used to get the format */ int data_index; int total_size; int pes_header_size; + int extended_stream_id; int64_t pts, dts; int64_t ts_packet_pos; /**< position of first TS packet of this PES packet */ uint8_t header[MAX_PES_HEADER_SIZE]; uint8_t *buffer; -}; +} PESContext; -extern AVInputFormat mpegts_demuxer; +extern AVInputFormat ff_mpegts_demuxer; static void clear_program(MpegTSContext *ts, unsigned int programid) { @@ -239,7 +239,7 @@ static int discard_pid(MpegTSContext *ts, unsigned int pid) } /** - * Assembles PES packets out of TS packets, and then calls the "section_cb" + * Assemble PES packets out of TS packets, and then call the "section_cb" * function when they are complete. */ static void write_section_data(AVFormatContext *s, MpegTSFilter *tss1, @@ -288,7 +288,7 @@ static MpegTSFilter *mpegts_open_section_filter(MpegTSContext *ts, unsigned int MpegTSFilter *filter; MpegTSSectionFilter *sec; - dprintf(ts->stream, "Filter: pid=0x%x\n", pid); + av_dlog(ts->stream, "Filter: pid=0x%x\n", pid); if (pid >= NB_PID_MAX || ts->pids[pid]) return NULL; @@ -355,7 +355,7 @@ static void mpegts_close_filter(MpegTSContext *ts, MpegTSFilter *filter) } static int analyze(const uint8_t *buf, int size, int packet_size, int *index){ - int stat[packet_size]; + int stat[TS_MAX_PACKET_SIZE]; int i; int x=0; int best_score=0; @@ -486,48 +486,54 @@ static int parse_section_header(SectionHeader *h, typedef struct { uint32_t stream_type; - enum CodecType codec_type; + enum AVMediaType codec_type; enum CodecID codec_id; } StreamType; static const StreamType ISO_types[] = { - { 0x01, CODEC_TYPE_VIDEO, CODEC_ID_MPEG2VIDEO }, - { 0x02, CODEC_TYPE_VIDEO, CODEC_ID_MPEG2VIDEO }, - { 0x03, CODEC_TYPE_AUDIO, CODEC_ID_MP3 }, - { 0x04, CODEC_TYPE_AUDIO, CODEC_ID_MP3 }, - { 0x0f, CODEC_TYPE_AUDIO, CODEC_ID_AAC }, - { 0x10, CODEC_TYPE_VIDEO, CODEC_ID_MPEG4 }, - { 0x1b, CODEC_TYPE_VIDEO, CODEC_ID_H264 }, - { 0xd1, CODEC_TYPE_VIDEO, CODEC_ID_DIRAC }, - { 0xea, CODEC_TYPE_VIDEO, CODEC_ID_VC1 }, + { 0x01, AVMEDIA_TYPE_VIDEO, CODEC_ID_MPEG2VIDEO }, + { 0x02, AVMEDIA_TYPE_VIDEO, CODEC_ID_MPEG2VIDEO }, + { 0x03, AVMEDIA_TYPE_AUDIO, CODEC_ID_MP3 }, + { 0x04, AVMEDIA_TYPE_AUDIO, CODEC_ID_MP3 }, + { 0x0f, AVMEDIA_TYPE_AUDIO, CODEC_ID_AAC }, + { 0x10, AVMEDIA_TYPE_VIDEO, CODEC_ID_MPEG4 }, + { 0x11, AVMEDIA_TYPE_AUDIO, CODEC_ID_AAC_LATM }, /* LATM syntax */ + { 0x1b, AVMEDIA_TYPE_VIDEO, CODEC_ID_H264 }, + { 0xd1, AVMEDIA_TYPE_VIDEO, CODEC_ID_DIRAC }, + { 0xea, AVMEDIA_TYPE_VIDEO, CODEC_ID_VC1 }, { 0 }, }; static const StreamType HDMV_types[] = { - { 0x81, CODEC_TYPE_AUDIO, CODEC_ID_AC3 }, - { 0x82, CODEC_TYPE_AUDIO, CODEC_ID_DTS }, + { 0x80, AVMEDIA_TYPE_AUDIO, CODEC_ID_PCM_BLURAY }, + { 0x81, AVMEDIA_TYPE_AUDIO, CODEC_ID_AC3 }, + { 0x82, AVMEDIA_TYPE_AUDIO, CODEC_ID_DTS }, + { 0x83, AVMEDIA_TYPE_AUDIO, CODEC_ID_TRUEHD }, + { 0x84, AVMEDIA_TYPE_AUDIO, CODEC_ID_EAC3 }, + { 0x90, AVMEDIA_TYPE_SUBTITLE, CODEC_ID_HDMV_PGS_SUBTITLE }, { 0 }, }; /* ATSC ? */ static const StreamType MISC_types[] = { - { 0x81, CODEC_TYPE_AUDIO, CODEC_ID_AC3 }, - { 0x8a, CODEC_TYPE_AUDIO, CODEC_ID_DTS }, + { 0x81, AVMEDIA_TYPE_AUDIO, CODEC_ID_AC3 }, + { 0x8a, AVMEDIA_TYPE_AUDIO, CODEC_ID_DTS }, { 0 }, }; static const StreamType REGD_types[] = { - { MKTAG('d','r','a','c'), CODEC_TYPE_VIDEO, CODEC_ID_DIRAC }, - { MKTAG('A','C','-','3'), CODEC_TYPE_AUDIO, CODEC_ID_AC3 }, + { MKTAG('d','r','a','c'), AVMEDIA_TYPE_VIDEO, CODEC_ID_DIRAC }, + { MKTAG('A','C','-','3'), AVMEDIA_TYPE_AUDIO, CODEC_ID_AC3 }, { 0 }, }; /* descriptor present */ static const StreamType DESC_types[] = { - { 0x6a, CODEC_TYPE_AUDIO, CODEC_ID_AC3 }, /* AC-3 descriptor */ - { 0x7a, CODEC_TYPE_AUDIO, CODEC_ID_EAC3 }, /* E-AC-3 descriptor */ - { 0x7b, CODEC_TYPE_AUDIO, CODEC_ID_DTS }, - { 0x59, CODEC_TYPE_SUBTITLE, CODEC_ID_DVB_SUBTITLE }, /* subtitling descriptor */ + { 0x6a, AVMEDIA_TYPE_AUDIO, CODEC_ID_AC3 }, /* AC-3 descriptor */ + { 0x7a, AVMEDIA_TYPE_AUDIO, CODEC_ID_EAC3 }, /* E-AC-3 descriptor */ + { 0x7b, AVMEDIA_TYPE_AUDIO, CODEC_ID_DTS }, + { 0x56, AVMEDIA_TYPE_SUBTITLE, CODEC_ID_DVB_TELETEXT }, + { 0x59, AVMEDIA_TYPE_SUBTITLE, CODEC_ID_DVB_SUBTITLE }, /* subtitling descriptor */ { 0 }, }; @@ -543,44 +549,446 @@ static void mpegts_find_stream_type(AVStream *st, } } -static AVStream *new_pes_av_stream(PESContext *pes, uint32_t prog_reg_desc, uint32_t code) +static int mpegts_set_stream_info(AVStream *st, PESContext *pes, + uint32_t stream_type, uint32_t prog_reg_desc) { - AVStream *st = av_new_stream(pes->stream, pes->pid); - - if (!st) - return NULL; - av_set_pts_info(st, 33, 1, 90000); st->priv_data = pes; - st->codec->codec_type = CODEC_TYPE_DATA; + st->codec->codec_type = AVMEDIA_TYPE_DATA; st->codec->codec_id = CODEC_ID_NONE; st->need_parsing = AVSTREAM_PARSE_FULL; pes->st = st; + pes->stream_type = stream_type; - dprintf(pes->stream, "stream_type=%x pid=%x prog_reg_desc=%.4s\n", - pes->stream_type, pes->pid, (char*)&prog_reg_desc); + av_log(pes->stream, AV_LOG_DEBUG, + "stream=%d stream_type=%x pid=%x prog_reg_desc=%.4s\n", + st->index, pes->stream_type, pes->pid, (char*)&prog_reg_desc); st->codec->codec_tag = pes->stream_type; mpegts_find_stream_type(st, pes->stream_type, ISO_types); if (prog_reg_desc == AV_RL32("HDMV") && - st->codec->codec_id == CODEC_ID_NONE) + st->codec->codec_id == CODEC_ID_NONE) { mpegts_find_stream_type(st, pes->stream_type, HDMV_types); + if (pes->stream_type == 0x83) { + // HDMV TrueHD streams also contain an AC3 coded version of the + // audio track - add a second stream for this + AVStream *sub_st; + // priv_data cannot be shared between streams + PESContext *sub_pes = av_malloc(sizeof(*sub_pes)); + if (!sub_pes) + return AVERROR(ENOMEM); + memcpy(sub_pes, pes, sizeof(*sub_pes)); + + sub_st = av_new_stream(pes->stream, pes->pid); + if (!sub_st) { + av_free(sub_pes); + return AVERROR(ENOMEM); + } + + av_set_pts_info(sub_st, 33, 1, 90000); + sub_st->priv_data = sub_pes; + sub_st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + sub_st->codec->codec_id = CODEC_ID_AC3; + sub_st->need_parsing = AVSTREAM_PARSE_FULL; + sub_pes->sub_st = pes->sub_st = sub_st; + } + } if (st->codec->codec_id == CODEC_ID_NONE) mpegts_find_stream_type(st, pes->stream_type, MISC_types); - /* stream was not present in PMT, guess based on PES start code */ - if (st->codec->codec_id == CODEC_ID_NONE) { - if (code >= 0x1c0 && code <= 0x1df) { - st->codec->codec_type = CODEC_TYPE_AUDIO; - st->codec->codec_id = CODEC_ID_MP2; - } else if (code == 0x1bd) { - st->codec->codec_type = CODEC_TYPE_AUDIO; - st->codec->codec_id = CODEC_ID_AC3; + return 0; +} + +static void new_pes_packet(PESContext *pes, AVPacket *pkt) +{ + av_init_packet(pkt); + + pkt->destruct = av_destruct_packet; + pkt->data = pes->buffer; + pkt->size = pes->data_index; + memset(pkt->data+pkt->size, 0, FF_INPUT_BUFFER_PADDING_SIZE); + + // Separate out the AC3 substream from an HDMV combined TrueHD/AC3 PID + if (pes->sub_st && pes->stream_type == 0x83 && pes->extended_stream_id == 0x76) + pkt->stream_index = pes->sub_st->index; + else + pkt->stream_index = pes->st->index; + pkt->pts = pes->pts; + pkt->dts = pes->dts; + /* store position of first TS packet of this PES packet */ + pkt->pos = pes->ts_packet_pos; + + /* reset pts values */ + pes->pts = AV_NOPTS_VALUE; + pes->dts = AV_NOPTS_VALUE; + pes->buffer = NULL; + pes->data_index = 0; +} + +/* 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) +{ + PESContext *pes = filter->u.pes_filter.opaque; + MpegTSContext *ts = pes->ts; + const uint8_t *p; + int len, code; + + if(!ts->pkt) + return 0; + + if (is_start) { + if (pes->state == MPEGTS_PAYLOAD && pes->data_index > 0) { + new_pes_packet(pes, ts->pkt); + ts->stop_parse = 1; + } + pes->state = MPEGTS_HEADER; + pes->data_index = 0; + pes->ts_packet_pos = pos; + } + p = buf; + while (buf_size > 0) { + switch(pes->state) { + case MPEGTS_HEADER: + len = PES_START_SIZE - pes->data_index; + if (len > buf_size) + len = buf_size; + memcpy(pes->header + pes->data_index, p, len); + pes->data_index += len; + p += len; + buf_size -= len; + if (pes->data_index == PES_START_SIZE) { + /* we got all the PES or section header. We can now + decide */ +#if 0 + av_hex_dump_log(pes->stream, AV_LOG_DEBUG, pes->header, pes->data_index); +#endif + if (pes->header[0] == 0x00 && pes->header[1] == 0x00 && + pes->header[2] == 0x01) { + /* it must be an mpeg2 PES stream */ + code = pes->header[3] | 0x100; + av_dlog(pes->stream, "pid=%x pes_code=%#x\n", pes->pid, code); + + if ((pes->st && pes->st->discard == AVDISCARD_ALL) || + code == 0x1be) /* padding_stream */ + goto skip; + +#if FF_API_MAX_STREAMS + if (!pes->st && pes->stream->nb_streams == MAX_STREAMS) + goto skip; +#endif + + /* stream not present in PMT */ + if (!pes->st) { + pes->st = av_new_stream(ts->stream, pes->pid); + if (!pes->st) + return AVERROR(ENOMEM); + mpegts_set_stream_info(pes->st, pes, 0, 0); + } + + pes->total_size = AV_RB16(pes->header + 4); + /* NOTE: a zero total size means the PES size is + unbounded */ + if (!pes->total_size) + pes->total_size = MAX_PES_PAYLOAD; + + /* allocate pes buffer */ + pes->buffer = av_malloc(pes->total_size+FF_INPUT_BUFFER_PADDING_SIZE); + if (!pes->buffer) + return AVERROR(ENOMEM); + + if (code != 0x1bc && code != 0x1bf && /* program_stream_map, private_stream_2 */ + code != 0x1f0 && code != 0x1f1 && /* ECM, EMM */ + 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 == CODEC_ID_NONE) { + av_dlog(pes->stream, "pid=%x stream_type=%x probing\n", + pes->pid, pes->stream_type); + pes->st->codec->codec_id = CODEC_ID_PROBE; + } + } else { + pes->state = MPEGTS_PAYLOAD; + pes->data_index = 0; + } + } else { + /* otherwise, it should be a table */ + /* skip packet */ + skip: + pes->state = MPEGTS_SKIP; + continue; + } + } + break; + /**********************************************/ + /* PES packing parsing */ + case MPEGTS_PESHEADER: + len = PES_HEADER_SIZE - pes->data_index; + if (len < 0) + return -1; + if (len > buf_size) + len = buf_size; + memcpy(pes->header + pes->data_index, p, len); + pes->data_index += len; + p += len; + buf_size -= len; + if (pes->data_index == PES_HEADER_SIZE) { + pes->pes_header_size = pes->header[8] + 9; + pes->state = MPEGTS_PESHEADER_FILL; + } + break; + case MPEGTS_PESHEADER_FILL: + len = pes->pes_header_size - pes->data_index; + if (len < 0) + return -1; + if (len > buf_size) + len = buf_size; + memcpy(pes->header + pes->data_index, p, len); + pes->data_index += len; + p += len; + buf_size -= len; + if (pes->data_index == pes->pes_header_size) { + const uint8_t *r; + unsigned int flags, pes_ext, skip; + + flags = pes->header[7]; + r = pes->header + 9; + pes->pts = AV_NOPTS_VALUE; + pes->dts = AV_NOPTS_VALUE; + if ((flags & 0xc0) == 0x80) { + pes->dts = pes->pts = ff_parse_pes_pts(r); + r += 5; + } else if ((flags & 0xc0) == 0xc0) { + pes->pts = ff_parse_pes_pts(r); + r += 5; + pes->dts = ff_parse_pes_pts(r); + r += 5; + } + pes->extended_stream_id = -1; + if (flags & 0x01) { /* PES extension */ + pes_ext = *r++; + /* Skip PES private data, program packet sequence counter and P-STD buffer */ + skip = (pes_ext >> 4) & 0xb; + skip += skip & 0x9; + r += skip; + if ((pes_ext & 0x41) == 0x01 && + (r + 2) <= (pes->header + pes->pes_header_size)) { + /* PES extension 2 */ + if ((r[0] & 0x7f) > 0 && (r[1] & 0x80) == 0) + pes->extended_stream_id = r[1]; + } + } + + /* we got the full header. We parse it and get the payload */ + pes->state = MPEGTS_PAYLOAD; + pes->data_index = 0; + } + break; + case MPEGTS_PAYLOAD: + if (buf_size > 0 && pes->buffer) { + if (pes->data_index > 0 && pes->data_index+buf_size > pes->total_size) { + new_pes_packet(pes, ts->pkt); + pes->total_size = MAX_PES_PAYLOAD; + pes->buffer = av_malloc(pes->total_size+FF_INPUT_BUFFER_PADDING_SIZE); + if (!pes->buffer) + return AVERROR(ENOMEM); + ts->stop_parse = 1; + } else if (pes->data_index == 0 && buf_size > pes->total_size) { + // pes packet size is < ts size packet and pes data is padded with 0xff + // not sure if this is legal in ts but see issue #2392 + buf_size = pes->total_size; + } + memcpy(pes->buffer+pes->data_index, p, buf_size); + pes->data_index += buf_size; + } + buf_size = 0; + /* emit complete packets with known packet size + * decreases demuxer delay for infrequent packets like subtitles from + * a couple of seconds to milliseconds for properly muxed files. + * total_size is the number of bytes following pes_packet_length + * in the pes header, i.e. not counting the first 6 bytes */ + if (pes->total_size < MAX_PES_PAYLOAD && + pes->pes_header_size + pes->data_index == pes->total_size + 6) { + ts->stop_parse = 1; + new_pes_packet(pes, ts->pkt); + } + break; + case MPEGTS_SKIP: + buf_size = 0; + break; } } - return st; + return 0; +} + +static PESContext *add_pes_stream(MpegTSContext *ts, int pid, int pcr_pid) +{ + MpegTSFilter *tss; + PESContext *pes; + + /* if no pid found, then add a pid context */ + pes = av_mallocz(sizeof(PESContext)); + if (!pes) + return 0; + pes->ts = ts; + pes->stream = ts->stream; + pes->pid = pid; + pes->pcr_pid = pcr_pid; + pes->state = MPEGTS_SKIP; + pes->pts = AV_NOPTS_VALUE; + pes->dts = AV_NOPTS_VALUE; + tss = mpegts_open_pes_filter(ts, pid, mpegts_push_data, pes); + if (!tss) { + av_free(pes); + return 0; + } + return pes; +} + +static int mp4_read_iods(AVFormatContext *s, const uint8_t *buf, unsigned size, + int *es_id, uint8_t **dec_config_descr, + int *dec_config_descr_size) +{ + AVIOContext pb; + int tag; + unsigned len; + + ffio_init_context(&pb, buf, size, 0, NULL, NULL, NULL, NULL); + + len = ff_mp4_read_descr(s, &pb, &tag); + if (tag == MP4IODescrTag) { + avio_rb16(&pb); // ID + avio_r8(&pb); + avio_r8(&pb); + avio_r8(&pb); + avio_r8(&pb); + avio_r8(&pb); + len = ff_mp4_read_descr(s, &pb, &tag); + if (tag == MP4ESDescrTag) { + *es_id = avio_rb16(&pb); /* ES_ID */ + av_dlog(s, "ES_ID %#x\n", *es_id); + avio_r8(&pb); /* priority */ + len = ff_mp4_read_descr(s, &pb, &tag); + if (tag == MP4DecConfigDescrTag) { + *dec_config_descr = av_malloc(len); + if (!*dec_config_descr) + return AVERROR(ENOMEM); + *dec_config_descr_size = len; + avio_read(&pb, *dec_config_descr, len); + } + } + } + return 0; +} + +int ff_parse_mpeg2_descriptor(AVFormatContext *fc, AVStream *st, int stream_type, + const uint8_t **pp, const uint8_t *desc_list_end, + int mp4_dec_config_descr_len, int mp4_es_id, int pid, + uint8_t *mp4_dec_config_descr) +{ + const uint8_t *desc_end; + int desc_len, desc_tag; + char language[252]; + int i; + + desc_tag = get8(pp, desc_list_end); + if (desc_tag < 0) + return -1; + desc_len = get8(pp, desc_list_end); + if (desc_len < 0) + return -1; + desc_end = *pp + desc_len; + if (desc_end > desc_list_end) + return -1; + + av_dlog(fc, "tag: 0x%02x len=%d\n", desc_tag, desc_len); + + if (st->codec->codec_id == CODEC_ID_NONE && + stream_type == STREAM_TYPE_PRIVATE_DATA) + mpegts_find_stream_type(st, desc_tag, DESC_types); + + switch(desc_tag) { + case 0x1F: /* FMC descriptor */ + get16(pp, desc_end); + if (st->codec->codec_id == CODEC_ID_AAC_LATM && + mp4_dec_config_descr_len && mp4_es_id == pid) { + AVIOContext pb; + ffio_init_context(&pb, mp4_dec_config_descr, + mp4_dec_config_descr_len, 0, NULL, NULL, NULL, NULL); + ff_mp4_read_dec_config_descr(fc, st, &pb); + if (st->codec->codec_id == CODEC_ID_AAC && + st->codec->extradata_size > 0) + st->need_parsing = 0; + } + break; + case 0x56: /* DVB teletext descriptor */ + language[0] = get8(pp, desc_end); + language[1] = get8(pp, desc_end); + language[2] = get8(pp, desc_end); + language[3] = 0; + av_metadata_set2(&st->metadata, "language", language, 0); + break; + case 0x59: /* subtitling descriptor */ + language[0] = get8(pp, desc_end); + language[1] = get8(pp, desc_end); + language[2] = get8(pp, desc_end); + language[3] = 0; + /* hearing impaired subtitles detection */ + switch(get8(pp, desc_end)) { + case 0x20: /* DVB subtitles (for the hard of hearing) with no monitor aspect ratio criticality */ + case 0x21: /* DVB subtitles (for the hard of hearing) for display on 4:3 aspect ratio monitor */ + case 0x22: /* DVB subtitles (for the hard of hearing) for display on 16:9 aspect ratio monitor */ + case 0x23: /* DVB subtitles (for the hard of hearing) for display on 2.21:1 aspect ratio monitor */ + case 0x24: /* DVB subtitles (for the hard of hearing) for display on a high definition monitor */ + case 0x25: /* DVB subtitles (for the hard of hearing) with plano-stereoscopic disparity for display on a high definition monitor */ + st->disposition |= AV_DISPOSITION_HEARING_IMPAIRED; + break; + } + if (st->codec->extradata) { + if (st->codec->extradata_size == 4 && memcmp(st->codec->extradata, *pp, 4)) + av_log_ask_for_sample(fc, "DVB sub with multiple IDs\n"); + } else { + st->codec->extradata = av_malloc(4 + FF_INPUT_BUFFER_PADDING_SIZE); + if (st->codec->extradata) { + st->codec->extradata_size = 4; + memcpy(st->codec->extradata, *pp, 4); + } + } + *pp += 4; + av_metadata_set2(&st->metadata, "language", language, 0); + break; + case 0x0a: /* ISO 639 language descriptor */ + for (i = 0; i + 4 <= desc_len; i += 4) { + language[i + 0] = get8(pp, desc_end); + language[i + 1] = get8(pp, desc_end); + language[i + 2] = get8(pp, desc_end); + language[i + 3] = ','; + switch (get8(pp, desc_end)) { + case 0x01: st->disposition |= AV_DISPOSITION_CLEAN_EFFECTS; break; + case 0x02: st->disposition |= AV_DISPOSITION_HEARING_IMPAIRED; break; + case 0x03: st->disposition |= AV_DISPOSITION_VISUAL_IMPAIRED; break; + } + } + if (i) { + language[i - 1] = 0; + av_metadata_set2(&st->metadata, "language", language, 0); + } + break; + case 0x05: /* registration descriptor */ + st->codec->codec_tag = bytestream_get_le32(pp); + av_dlog(fc, "reg_desc=%.4s\n", (char*)&st->codec->codec_tag); + if (st->codec->codec_id == CODEC_ID_NONE && + stream_type == STREAM_TYPE_PRIVATE_DATA) + mpegts_find_stream_type(st, st->codec->codec_tag, REGD_types); + break; + default: + break; + } + *pp = desc_end; + return 0; } static void pmt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len) @@ -589,15 +997,16 @@ static void pmt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len SectionHeader h1, *h = &h1; PESContext *pes; AVStream *st; - const uint8_t *p, *p_end, *desc_list_end, *desc_end; + const uint8_t *p, *p_end, *desc_list_end; int program_info_length, pcr_pid, pid, stream_type; - int desc_list_len, desc_len, desc_tag; - int comp_page, anc_page; - char language[4]; + int desc_list_len; uint32_t prog_reg_desc = 0; /* registration descriptor */ + uint8_t *mp4_dec_config_descr = NULL; + int mp4_dec_config_descr_len = 0; + int mp4_es_id = 0; #ifdef DEBUG - dprintf(ts->stream, "PMT: len %i\n", section_len); + av_dlog(ts->stream, "PMT: len %i\n", section_len); av_hex_dump_log(ts->stream, AV_LOG_DEBUG, (uint8_t *)section, section_len); #endif @@ -606,7 +1015,7 @@ static void pmt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len if (parse_section_header(h, &p, p_end) < 0) return; - dprintf(ts->stream, "sid=0x%x sec_num=%d/%d\n", + av_dlog(ts->stream, "sid=0x%x sec_num=%d/%d\n", h->id, h->sec_num, h->last_sec_num); if (h->tid != PMT_TID) @@ -618,7 +1027,7 @@ static void pmt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len return; add_pid_to_pmt(ts, h->id, pcr_pid); - dprintf(ts->stream, "pcr_pid=0x%x\n", pcr_pid); + av_dlog(ts->stream, "pcr_pid=0x%x\n", pcr_pid); program_info_length = get16(&p, p_end) & 0xfff; if (program_info_length < 0) @@ -627,11 +1036,20 @@ static void pmt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len uint8_t tag, len; tag = get8(&p, p_end); len = get8(&p, p_end); + + av_dlog(ts->stream, "program tag: 0x%02x len=%d\n", tag, len); + if(len > program_info_length - 2) //something else is broken, exit the program_descriptors_loop break; program_info_length -= len + 2; - if(tag == 0x05 && len >= 4) { // registration descriptor + if (tag == 0x1d) { // IOD descriptor + get8(&p, p_end); // scope + get8(&p, p_end); // label + len -= 2; + mp4_read_iods(ts->stream, p, len, &mp4_es_id, + &mp4_dec_config_descr, &mp4_dec_config_descr_len); + } else if (tag == 0x05 && len >= 4) { // registration descriptor prog_reg_desc = bytestream_get_le32(&p); len -= 4; } @@ -639,7 +1057,12 @@ static void pmt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len } p += program_info_length; if (p >= p_end) - return; + goto out; + + // stop parsing after pmt, we found header + if (!ts->stream->nb_streams) + ts->stop_parse = 1; + for(;;) { st = 0; stream_type = get8(&p, p_end); @@ -652,20 +1075,25 @@ static void pmt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len /* now create ffmpeg stream */ if (ts->pids[pid] && ts->pids[pid]->type == MPEGTS_PES) { pes = ts->pids[pid]->u.pes_filter.opaque; + if (!pes->st) + pes->st = av_new_stream(pes->stream, pes->pid); st = pes->st; } else { if (ts->pids[pid]) mpegts_close_filter(ts, ts->pids[pid]); //wrongly added sdt filter probably - pes = add_pes_stream(ts, pid, pcr_pid, stream_type); + pes = add_pes_stream(ts, pid, pcr_pid); if (pes) - st = new_pes_av_stream(pes, prog_reg_desc, 0); + st = av_new_stream(pes->stream, pes->pid); } if (!st) - return; + goto out; + + if (!pes->stream_type) + mpegts_set_stream_info(st, pes, stream_type, prog_reg_desc); add_pid_to_pmt(ts, h->id, pid); - av_program_add_stream_index(ts->stream, h->id, st->index); + ff_program_add_stream_index(ts->stream, h->id, st->index); desc_list_len = get16(&p, p_end) & 0xfff; if (desc_list_len < 0) @@ -674,59 +1102,20 @@ static void pmt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len if (desc_list_end > p_end) break; for(;;) { - desc_tag = get8(&p, desc_list_end); - if (desc_tag < 0) - break; - desc_len = get8(&p, desc_list_end); - if (desc_len < 0) - break; - desc_end = p + desc_len; - if (desc_end > desc_list_end) + if (ff_parse_mpeg2_descriptor(ts->stream, st, stream_type, &p, desc_list_end, + mp4_dec_config_descr_len, mp4_es_id, pid, mp4_dec_config_descr) < 0) break; - dprintf(ts->stream, "tag: 0x%02x len=%d\n", - desc_tag, desc_len); - - if (st->codec->codec_id == CODEC_ID_NONE && - stream_type == STREAM_TYPE_PRIVATE_DATA) - mpegts_find_stream_type(st, desc_tag, DESC_types); - - switch(desc_tag) { - case 0x59: /* subtitling descriptor */ - language[0] = get8(&p, desc_end); - language[1] = get8(&p, desc_end); - language[2] = get8(&p, desc_end); - language[3] = 0; - get8(&p, desc_end); - comp_page = get16(&p, desc_end); - anc_page = get16(&p, desc_end); - st->codec->sub_id = (anc_page << 16) | comp_page; - av_metadata_set(&st->metadata, "language", language); - break; - case 0x0a: /* ISO 639 language descriptor */ - language[0] = get8(&p, desc_end); - language[1] = get8(&p, desc_end); - language[2] = get8(&p, desc_end); - language[3] = 0; - av_metadata_set(&st->metadata, "language", language); - break; - case 0x05: /* registration descriptor */ - st->codec->codec_tag = bytestream_get_le32(&p); - dprintf(ts->stream, "reg_desc=%.4s\n", (char*)&st->codec->codec_tag); - if (st->codec->codec_id == CODEC_ID_NONE && - stream_type == STREAM_TYPE_PRIVATE_DATA) - mpegts_find_stream_type(st, st->codec->codec_tag, REGD_types); - break; - default: - break; + if (prog_reg_desc == AV_RL32("HDMV") && stream_type == 0x83 && pes->sub_st) { + ff_program_add_stream_index(ts->stream, h->id, pes->sub_st->index); + pes->sub_st->codec->codec_tag = st->codec->codec_tag; } - p = desc_end; } p = desc_list_end; } - /* all parameters are there */ - ts->stop_parse++; - mpegts_close_filter(ts, filter); + + out: + av_free(mp4_dec_config_descr); } static void pat_cb(MpegTSFilter *filter, const uint8_t *section, int section_len) @@ -737,7 +1126,7 @@ static void pat_cb(MpegTSFilter *filter, const uint8_t *section, int section_len int sid, pmt_pid; #ifdef DEBUG - dprintf(ts->stream, "PAT:\n"); + av_dlog(ts->stream, "PAT:\n"); av_hex_dump_log(ts->stream, AV_LOG_DEBUG, (uint8_t *)section, section_len); #endif p_end = section + section_len - 4; @@ -756,23 +1145,20 @@ static void pat_cb(MpegTSFilter *filter, const uint8_t *section, int section_len if (pmt_pid < 0) break; - dprintf(ts->stream, "sid=0x%x pid=0x%x\n", sid, pmt_pid); + 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); - ts->stop_parse--; + if (ts->pids[pmt_pid]) + mpegts_close_filter(ts, 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); } } - /* not found */ - ts->stop_parse++; - - mpegts_close_filter(ts, filter); } static void sdt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len) @@ -784,7 +1170,7 @@ static void sdt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len char *name, *provider_name; #ifdef DEBUG - dprintf(ts->stream, "SDT:\n"); + av_dlog(ts->stream, "SDT:\n"); av_hex_dump_log(ts->stream, AV_LOG_DEBUG, (uint8_t *)section, section_len); #endif @@ -822,7 +1208,7 @@ static void sdt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len if (desc_end > desc_list_end) break; - dprintf(ts->stream, "tag: 0x%02x len=%d\n", + av_dlog(ts->stream, "tag: 0x%02x len=%d\n", desc_tag, desc_len); switch(desc_tag) { @@ -837,8 +1223,8 @@ static void sdt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len if (name) { AVProgram *program = av_new_program(ts->stream, sid); if(program) { - av_metadata_set(&program->metadata, "name", name); - av_metadata_set(&program->metadata, "provider_name", provider_name); + av_metadata_set2(&program->metadata, "service_name", name, 0); + av_metadata_set2(&program->metadata, "service_provider", provider_name, 0); } } av_free(name); @@ -853,223 +1239,6 @@ static void sdt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len } } -static int64_t get_pts(const uint8_t *p) -{ - int64_t pts = (int64_t)((p[0] >> 1) & 0x07) << 30; - pts |= (AV_RB16(p + 1) >> 1) << 15; - pts |= AV_RB16(p + 3) >> 1; - return pts; -} - -static void new_pes_packet(PESContext *pes, AVPacket *pkt) -{ - av_init_packet(pkt); - - pkt->destruct = av_destruct_packet; - pkt->data = pes->buffer; - pkt->size = pes->data_index; - memset(pkt->data+pkt->size, 0, FF_INPUT_BUFFER_PADDING_SIZE); - - pkt->stream_index = pes->st->index; - pkt->pts = pes->pts; - pkt->dts = pes->dts; - /* store position of first TS packet of this PES packet */ - pkt->pos = pes->ts_packet_pos; - - /* reset pts values */ - pes->pts = AV_NOPTS_VALUE; - pes->dts = AV_NOPTS_VALUE; - pes->buffer = NULL; - pes->data_index = 0; -} - -/* 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) -{ - PESContext *pes = filter->u.pes_filter.opaque; - MpegTSContext *ts = pes->ts; - const uint8_t *p; - int len, code; - - if(!ts->pkt) - return 0; - - if (is_start) { - if (pes->state == MPEGTS_PAYLOAD && pes->data_index > 0) { - new_pes_packet(pes, ts->pkt); - ts->stop_parse = 1; - } - pes->state = MPEGTS_HEADER; - pes->data_index = 0; - pes->ts_packet_pos = pos; - } - p = buf; - while (buf_size > 0) { - switch(pes->state) { - case MPEGTS_HEADER: - len = PES_START_SIZE - pes->data_index; - if (len > buf_size) - len = buf_size; - memcpy(pes->header + pes->data_index, p, len); - pes->data_index += len; - p += len; - buf_size -= len; - if (pes->data_index == PES_START_SIZE) { - /* we got all the PES or section header. We can now - decide */ -#if 0 - av_hex_dump_log(pes->stream, AV_LOG_DEBUG, pes->header, pes->data_index); -#endif - if (pes->header[0] == 0x00 && pes->header[1] == 0x00 && - pes->header[2] == 0x01) { - /* it must be an mpeg2 PES stream */ - code = pes->header[3] | 0x100; - dprintf(pes->stream, "pid=%x pes_code=%#x\n", pes->pid, code); - - if ((!pes->st && pes->stream->nb_streams == MAX_STREAMS) || - (pes->st && pes->st->discard == AVDISCARD_ALL) || - code == 0x1be) /* padding_stream */ - goto skip; - - /* stream not present in PMT */ - if (!pes->st) - pes->st = new_pes_av_stream(pes, 0, code); - if (!pes->st) - return AVERROR(ENOMEM); - - pes->total_size = AV_RB16(pes->header + 4); - /* NOTE: a zero total size means the PES size is - unbounded */ - if (!pes->total_size) - pes->total_size = MAX_PES_PAYLOAD; - - /* allocate pes buffer */ - pes->buffer = av_malloc(pes->total_size+FF_INPUT_BUFFER_PADDING_SIZE); - if (!pes->buffer) - return AVERROR(ENOMEM); - - if (code != 0x1bc && code != 0x1bf && /* program_stream_map, private_stream_2 */ - code != 0x1f0 && code != 0x1f1 && /* ECM, EMM */ - 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 == CODEC_ID_NONE) { - dprintf(pes->stream, "pid=%x stream_type=%x probing\n", - pes->pid, pes->stream_type); - pes->st->codec->codec_id = CODEC_ID_PROBE; - } - } else { - pes->state = MPEGTS_PAYLOAD; - pes->data_index = 0; - } - } else { - /* otherwise, it should be a table */ - /* skip packet */ - skip: - pes->state = MPEGTS_SKIP; - continue; - } - } - break; - /**********************************************/ - /* PES packing parsing */ - case MPEGTS_PESHEADER: - len = PES_HEADER_SIZE - pes->data_index; - if (len < 0) - return -1; - if (len > buf_size) - len = buf_size; - memcpy(pes->header + pes->data_index, p, len); - pes->data_index += len; - p += len; - buf_size -= len; - if (pes->data_index == PES_HEADER_SIZE) { - pes->pes_header_size = pes->header[8] + 9; - pes->state = MPEGTS_PESHEADER_FILL; - } - break; - case MPEGTS_PESHEADER_FILL: - len = pes->pes_header_size - pes->data_index; - if (len < 0) - return -1; - if (len > buf_size) - len = buf_size; - memcpy(pes->header + pes->data_index, p, len); - pes->data_index += len; - p += len; - buf_size -= len; - if (pes->data_index == pes->pes_header_size) { - const uint8_t *r; - unsigned int flags; - - flags = pes->header[7]; - r = pes->header + 9; - pes->pts = AV_NOPTS_VALUE; - pes->dts = AV_NOPTS_VALUE; - if ((flags & 0xc0) == 0x80) { - pes->dts = pes->pts = get_pts(r); - r += 5; - } else if ((flags & 0xc0) == 0xc0) { - pes->pts = get_pts(r); - r += 5; - pes->dts = get_pts(r); - r += 5; - } - - /* we got the full header. We parse it and get the payload */ - pes->state = MPEGTS_PAYLOAD; - pes->data_index = 0; - } - break; - case MPEGTS_PAYLOAD: - if (buf_size > 0) { - if (pes->data_index+buf_size > pes->total_size) { - new_pes_packet(pes, ts->pkt); - pes->total_size = MAX_PES_PAYLOAD; - pes->buffer = av_malloc(pes->total_size+FF_INPUT_BUFFER_PADDING_SIZE); - if (!pes->buffer) - return AVERROR(ENOMEM); - ts->stop_parse = 1; - } - memcpy(pes->buffer+pes->data_index, p, buf_size); - pes->data_index += buf_size; - } - buf_size = 0; - break; - case MPEGTS_SKIP: - buf_size = 0; - break; - } - } - - return 0; -} - -static PESContext *add_pes_stream(MpegTSContext *ts, int pid, int pcr_pid, int stream_type) -{ - MpegTSFilter *tss; - PESContext *pes; - - /* if no pid found, then add a pid context */ - pes = av_mallocz(sizeof(PESContext)); - if (!pes) - return 0; - pes->ts = ts; - pes->stream = ts->stream; - pes->pid = pid; - pes->pcr_pid = pcr_pid; - pes->stream_type = stream_type; - pes->state = MPEGTS_SKIP; - tss = mpegts_open_pes_filter(ts, pid, mpegts_push_data, pes); - if (!tss) { - av_free(pes); - return 0; - } - return pes; -} - /* handle one TS packet */ static int handle_packet(MpegTSContext *ts, const uint8_t *packet) { @@ -1085,7 +1254,7 @@ static int handle_packet(MpegTSContext *ts, const uint8_t *packet) is_start = packet[1] & 0x40; tss = ts->pids[pid]; if (ts->auto_guess && tss == NULL && is_start) { - add_pes_stream(ts, pid, -1, 0); + add_pes_stream(ts, pid, -1); tss = ts->pids[pid]; } if (!tss) @@ -1112,7 +1281,7 @@ static int handle_packet(MpegTSContext *ts, const uint8_t *packet) if (p >= p_end) return 0; - pos = url_ftell(ts->stream->pb); + pos = avio_tell(ts->stream->pb); ts->pos47= pos % ts->raw_packet_size; if (tss->type == MPEGTS_SECTION) { @@ -1153,44 +1322,47 @@ static int handle_packet(MpegTSContext *ts, const uint8_t *packet) /* XXX: try to find a better synchro over several packets (use get_packet_size() ?) */ -static int mpegts_resync(ByteIOContext *pb) +static int mpegts_resync(AVFormatContext *s) { + AVIOContext *pb = s->pb; int c, i; for(i = 0;i < MAX_RESYNC_SIZE; i++) { - c = url_fgetc(pb); - if (c < 0) + c = avio_r8(pb); + if (pb->eof_reached) return -1; if (c == 0x47) { - url_fseek(pb, -1, SEEK_CUR); + avio_seek(pb, -1, SEEK_CUR); return 0; } } + av_log(s, AV_LOG_ERROR, "max resync size reached, could not find sync byte\n"); /* no sync found */ return -1; } /* return -1 if error or EOF. Return 0 if OK. */ -static int read_packet(ByteIOContext *pb, uint8_t *buf, int raw_packet_size) +static int read_packet(AVFormatContext *s, uint8_t *buf, int raw_packet_size) { + AVIOContext *pb = s->pb; int skip, len; for(;;) { - len = get_buffer(pb, buf, TS_PACKET_SIZE); + len = avio_read(pb, buf, TS_PACKET_SIZE); if (len != TS_PACKET_SIZE) return AVERROR(EIO); /* check paquet sync byte */ if (buf[0] != 0x47) { /* find a new packet start */ - url_fseek(pb, -TS_PACKET_SIZE, SEEK_CUR); - if (mpegts_resync(pb) < 0) - return AVERROR_INVALIDDATA; + avio_seek(pb, -TS_PACKET_SIZE, SEEK_CUR); + if (mpegts_resync(s) < 0) + return AVERROR(EAGAIN); else continue; } else { skip = raw_packet_size - TS_PACKET_SIZE; if (skip > 0) - url_fskip(pb, skip); + avio_skip(pb, skip); break; } } @@ -1200,7 +1372,6 @@ static int read_packet(ByteIOContext *pb, uint8_t *buf, int raw_packet_size) static int handle_packets(MpegTSContext *ts, int nb_packets) { AVFormatContext *s = ts->stream; - ByteIOContext *pb = s->pb; uint8_t packet[TS_PACKET_SIZE]; int packet_num, ret; @@ -1212,7 +1383,7 @@ static int handle_packets(MpegTSContext *ts, int nb_packets) packet_num++; if (nb_packets != 0 && packet_num >= nb_packets) break; - ret = read_packet(pb, packet, ts->raw_packet_size); + ret = read_packet(s, packet, ts->raw_packet_size); if (ret != 0) return ret; ret = handle_packet(ts, packet); @@ -1245,7 +1416,7 @@ static int mpegts_probe(AVProbeData *p) else return -1; #else /* only use the extension for safer guess */ - if (match_ext(p->filename, "ts")) + if (av_match_ext(p->filename, "ts")) return AVPROBE_SCORE_MAX; else return 0; @@ -1285,7 +1456,7 @@ static int mpegts_read_header(AVFormatContext *s, AVFormatParameters *ap) { MpegTSContext *ts = s->priv_data; - ByteIOContext *pb = s->pb; + AVIOContext *pb = s->pb; uint8_t buf[5*1024]; int len; int64_t pos; @@ -1299,8 +1470,8 @@ static int mpegts_read_header(AVFormatContext *s, } /* read the first 1024 bytes to get packet size */ - pos = url_ftell(pb); - len = get_buffer(pb, buf, sizeof(buf)); + 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)); @@ -1309,22 +1480,23 @@ static int mpegts_read_header(AVFormatContext *s, ts->stream = s; ts->auto_guess = 0; - if (s->iformat == &mpegts_demuxer) { + if (s->iformat == &ff_mpegts_demuxer) { /* normal demux */ /* first do a scaning to get all the services */ - url_fseek(pb, pos, SEEK_SET); + if (avio_seek(pb, pos, SEEK_SET) < 0) + av_log(s, AV_LOG_ERROR, "Unable to seek back to the start\n"); mpegts_open_section_filter(ts, SDT_PID, sdt_cb, ts, 1); mpegts_open_section_filter(ts, PAT_PID, pat_cb, ts, 1); - handle_packets(ts, s->probesize); + handle_packets(ts, s->probesize / ts->raw_packet_size); /* if could not find service, enable auto_guess */ ts->auto_guess = 1; - dprintf(ts->stream, "tuning done\n"); + av_dlog(ts->stream, "tuning done\n"); s->ctx_flags |= AVFMTCTX_NOHEADER; } else { @@ -1340,7 +1512,7 @@ static int mpegts_read_header(AVFormatContext *s, if (!st) goto fail; av_set_pts_info(st, 60, 1, 27000000); - st->codec->codec_type = CODEC_TYPE_DATA; + st->codec->codec_type = AVMEDIA_TYPE_DATA; st->codec->codec_id = CODEC_ID_MPEG2TS; /* we iterate until we find two PCRs to estimate the bitrate */ @@ -1348,7 +1520,7 @@ static int mpegts_read_header(AVFormatContext *s, nb_pcrs = 0; nb_packets = 0; for(;;) { - ret = read_packet(s->pb, packet, ts->raw_packet_size); + ret = read_packet(s, packet, ts->raw_packet_size); if (ret < 0) return -1; pid = AV_RB16(packet + 1) & 0x1fff; @@ -1377,7 +1549,7 @@ static int mpegts_read_header(AVFormatContext *s, #endif } - url_fseek(pb, pos, SEEK_SET); + avio_seek(pb, pos, SEEK_SET); return 0; fail: return -1; @@ -1396,8 +1568,8 @@ static int mpegts_raw_read_packet(AVFormatContext *s, if (av_new_packet(pkt, TS_PACKET_SIZE) < 0) return AVERROR(ENOMEM); - pkt->pos= url_ftell(s->pb); - ret = read_packet(s->pb, pkt->data, ts->raw_packet_size); + pkt->pos= avio_tell(s->pb); + ret = read_packet(s, pkt->data, ts->raw_packet_size); if (ret < 0) { av_free_packet(pkt); return ret; @@ -1406,10 +1578,10 @@ static int mpegts_raw_read_packet(AVFormatContext *s, /* compute exact PCR for each packet */ if (parse_pcr(&pcr_h, &pcr_l, pkt->data) == 0) { /* we read the next PCR (XXX: optimize it by using a bigger buffer */ - pos = url_ftell(s->pb); + pos = avio_tell(s->pb); for(i = 0; i < MAX_PACKET_READAHEAD; i++) { - url_fseek(s->pb, pos + i * ts->raw_packet_size, SEEK_SET); - get_buffer(s->pb, pcr_buf, 12); + avio_seek(s->pb, pos + i * ts->raw_packet_size, SEEK_SET); + avio_read(s->pb, pcr_buf, 12); if (parse_pcr(&next_pcr_h, &next_pcr_l, pcr_buf) == 0) { /* XXX: not precise enough */ ts->pcr_incr = ((next_pcr_h - pcr_h) * 300 + (next_pcr_l - pcr_l)) / @@ -1417,7 +1589,7 @@ static int mpegts_raw_read_packet(AVFormatContext *s, break; } } - url_fseek(s->pb, pos, SEEK_SET); + avio_seek(s->pb, pos, SEEK_SET); /* no next PCR found: we use previous increment */ ts->cur_pcr = pcr_h * 300 + pcr_l; } @@ -1435,7 +1607,7 @@ static int mpegts_read_packet(AVFormatContext *s, MpegTSContext *ts = s->priv_data; int ret, i; - if (url_ftell(s->pb) != ts->last_pos) { + if (avio_tell(s->pb) != ts->last_pos) { /* seek detected, flush pes buffer */ for (i = 0; i < NB_PID_MAX; i++) { if (ts->pids[i] && ts->pids[i]->type == MPEGTS_PES) { @@ -1456,6 +1628,7 @@ static int mpegts_read_packet(AVFormatContext *s, PESContext *pes = ts->pids[i]->u.pes_filter.opaque; if (pes->state == MPEGTS_PAYLOAD && pes->data_index > 0) { new_pes_packet(pes, pkt); + pes->state = MPEGTS_SKIP; ret = 0; break; } @@ -1463,7 +1636,7 @@ static int mpegts_read_packet(AVFormatContext *s, } } - ts->last_pos = url_ftell(s->pb); + ts->last_pos = avio_tell(s->pb); return ret; } @@ -1492,8 +1665,8 @@ static int64_t mpegts_get_pcr(AVFormatContext *s, int stream_index, pos = ((*ppos + ts->raw_packet_size - 1 - ts->pos47) / ts->raw_packet_size) * ts->raw_packet_size + ts->pos47; if (find_next) { for(;;) { - url_fseek(s->pb, pos, SEEK_SET); - if (get_buffer(s->pb, buf, TS_PACKET_SIZE) != TS_PACKET_SIZE) + 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) { @@ -1506,8 +1679,8 @@ static int64_t mpegts_get_pcr(AVFormatContext *s, int stream_index, pos -= ts->raw_packet_size; if (pos < 0) return AV_NOPTS_VALUE; - url_fseek(s->pb, pos, SEEK_SET); - if (get_buffer(s->pb, buf, TS_PACKET_SIZE) != TS_PACKET_SIZE) + 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) { @@ -1520,6 +1693,92 @@ static int64_t mpegts_get_pcr(AVFormatContext *s, int stream_index, return timestamp; } +#ifdef USE_SYNCPOINT_SEARCH + +static int read_seek2(AVFormatContext *s, + int stream_index, + int64_t min_ts, + int64_t target_ts, + int64_t max_ts, + int flags) +{ + int64_t pos; + + int64_t ts_ret, ts_adj; + int stream_index_gen_search; + AVStream *st; + AVParserState *backup; + + backup = ff_store_parser_state(s); + + // detect direction of seeking for search purposes + flags |= (target_ts - min_ts > (uint64_t)(max_ts - target_ts)) ? + AVSEEK_FLAG_BACKWARD : 0; + + if (flags & AVSEEK_FLAG_BYTE) { + // use position directly, we will search starting from it + pos = target_ts; + } else { + // search for some position with good timestamp match + if (stream_index < 0) { + stream_index_gen_search = av_find_default_stream_index(s); + if (stream_index_gen_search < 0) { + ff_restore_parser_state(s, backup); + return -1; + } + + st = s->streams[stream_index_gen_search]; + // timestamp for default must be expressed in AV_TIME_BASE units + ts_adj = av_rescale(target_ts, + st->time_base.den, + AV_TIME_BASE * (int64_t)st->time_base.num); + } else { + ts_adj = target_ts; + stream_index_gen_search = stream_index; + } + pos = av_gen_search(s, stream_index_gen_search, ts_adj, + 0, INT64_MAX, -1, + AV_NOPTS_VALUE, + AV_NOPTS_VALUE, + flags, &ts_ret, mpegts_get_pcr); + if (pos < 0) { + ff_restore_parser_state(s, backup); + return -1; + } + } + + // search for actual matching keyframe/starting position for all streams + if (ff_gen_syncpoint_search(s, stream_index, pos, + min_ts, target_ts, max_ts, + flags) < 0) { + ff_restore_parser_state(s, backup); + return -1; + } + + ff_free_parser_state(s, backup); + return 0; +} + +static int read_seek(AVFormatContext *s, int stream_index, int64_t target_ts, int flags) +{ + int ret; + if (flags & AVSEEK_FLAG_BACKWARD) { + flags &= ~AVSEEK_FLAG_BACKWARD; + ret = read_seek2(s, stream_index, INT64_MIN, target_ts, target_ts, flags); + if (ret < 0) + // for compatibility reasons, seek to the best-fitting timestamp + ret = read_seek2(s, stream_index, INT64_MIN, target_ts, INT64_MAX, flags); + } else { + ret = read_seek2(s, stream_index, target_ts, target_ts, INT64_MAX, flags); + if (ret < 0) + // for compatibility reasons, seek to the best-fitting timestamp + ret = read_seek2(s, stream_index, INT64_MIN, target_ts, INT64_MAX, flags); + } + return ret; +} + +#else + static int read_seek(AVFormatContext *s, int stream_index, int64_t target_ts, int flags){ MpegTSContext *ts = s->priv_data; uint8_t buf[TS_PACKET_SIZE]; @@ -1528,25 +1787,27 @@ static int read_seek(AVFormatContext *s, int stream_index, int64_t target_ts, in if(av_seek_frame_binary(s, stream_index, target_ts, flags) < 0) return -1; - pos= url_ftell(s->pb); + pos= avio_tell(s->pb); for(;;) { - url_fseek(s->pb, pos, SEEK_SET); - if (get_buffer(s->pb, buf, TS_PACKET_SIZE) != TS_PACKET_SIZE) + 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; } - url_fseek(s->pb, pos, SEEK_SET); + avio_seek(s->pb, pos, SEEK_SET); return 0; } +#endif + /**************************************************************/ /* parsing functions - called from other demuxers such as RTP */ -MpegTSContext *mpegts_parse_open(AVFormatContext *s) +MpegTSContext *ff_mpegts_parse_open(AVFormatContext *s) { MpegTSContext *ts; @@ -1562,7 +1823,7 @@ MpegTSContext *mpegts_parse_open(AVFormatContext *s) /* return the consumed length if a packet was output, or -1 if no packet is output */ -int mpegts_parse_packet(MpegTSContext *ts, AVPacket *pkt, +int ff_mpegts_parse_packet(MpegTSContext *ts, AVPacket *pkt, const uint8_t *buf, int len) { int len1; @@ -1587,7 +1848,7 @@ int mpegts_parse_packet(MpegTSContext *ts, AVPacket *pkt, return len1 - len; } -void mpegts_parse_close(MpegTSContext *ts) +void ff_mpegts_parse_close(MpegTSContext *ts) { int i; @@ -1596,7 +1857,7 @@ void mpegts_parse_close(MpegTSContext *ts) av_free(ts); } -AVInputFormat mpegts_demuxer = { +AVInputFormat ff_mpegts_demuxer = { "mpegts", NULL_IF_CONFIG_SMALL("MPEG-2 transport stream format"), sizeof(MpegTSContext), @@ -1607,9 +1868,12 @@ AVInputFormat mpegts_demuxer = { read_seek, mpegts_get_pcr, .flags = AVFMT_SHOW_IDS|AVFMT_TS_DISCONT, +#ifdef USE_SYNCPOINT_SEARCH + .read_seek2 = read_seek2, +#endif }; -AVInputFormat mpegtsraw_demuxer = { +AVInputFormat ff_mpegtsraw_demuxer = { "mpegtsraw", NULL_IF_CONFIG_SMALL("MPEG-2 raw transport stream format"), sizeof(MpegTSContext), @@ -1620,4 +1884,7 @@ AVInputFormat mpegtsraw_demuxer = { read_seek, mpegts_get_pcr, .flags = AVFMT_SHOW_IDS|AVFMT_TS_DISCONT, +#ifdef USE_SYNCPOINT_SEARCH + .read_seek2 = read_seek2, +#endif };