*/
#include "libavutil/buffer.h"
+#include "libavutil/common.h"
#include "libavutil/crc.h"
#include "libavutil/internal.h"
#include "libavutil/intreadwrite.h"
#include "libavutil/mathematics.h"
#include "libavutil/opt.h"
#include "libavutil/avassert.h"
+#include "libavutil/dovi_meta.h"
#include "libavcodec/bytestream.h"
#include "libavcodec/get_bits.h"
#include "libavcodec/opus.h"
} u;
};
-#define MAX_PIDS_PER_PROGRAM 64
+struct Stream {
+ int idx;
+ int stream_identifier;
+};
+
+#define MAX_STREAMS_PER_PROGRAM 128
+#define MAX_PIDS_PER_PROGRAM (MAX_STREAMS_PER_PROGRAM + 2)
struct Program {
unsigned int id; // program id/service id
unsigned int nb_pids;
unsigned int pids[MAX_PIDS_PER_PROGRAM];
+ unsigned int nb_streams;
+ struct Stream streams[MAX_STREAMS_PER_PROGRAM];
/** have we found pmt for this program */
int pmt_found;
/** raw packet size, including FEC if present */
int raw_packet_size;
- 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 fix_teletext_pts;
int64_t cur_pcr; /**< used to estimate the exact PCR */
- int pcr_incr; /**< used to estimate the exact PCR */
+ int64_t pcr_incr; /**< used to estimate the exact PCR */
/* data needed to handle file based ts */
/** stop parsing loop */
/** filters for various streams specified by PMT + for the PAT and PMT */
MpegTSFilter *pids[NB_PID_MAX];
int current_pid;
+
+ AVStream *epg_stream;
+ AVBufferPool* pools[32];
};
#define MPEGTS_OPTIONS \
prg->nb_stream_indexes = 0;
}
-static void clear_program(MpegTSContext *ts, unsigned int programid)
+static void clear_program(struct Program *p)
{
- 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;
- ts->prg[i].pmt_found = 0;
- }
+ if (!p)
+ return;
+ p->nb_pids = 0;
+ p->nb_streams = 0;
+ p->pmt_found = 0;
}
static void clear_programs(MpegTSContext *ts)
ts->nb_prg = 0;
}
-static void add_pat_entry(MpegTSContext *ts, unsigned int programid)
+static struct Program * add_program(MpegTSContext *ts, unsigned int programid)
{
- struct Program *p;
+ struct Program *p = get_program(ts, programid);
+ if (p)
+ return p;
if (av_reallocp_array(&ts->prg, ts->nb_prg + 1, sizeof(*ts->prg)) < 0) {
ts->nb_prg = 0;
- return;
+ return NULL;
}
p = &ts->prg[ts->nb_prg];
p->id = programid;
- p->nb_pids = 0;
- p->pmt_found = 0;
+ clear_program(p);
ts->nb_prg++;
+ return p;
}
-static void add_pid_to_pmt(MpegTSContext *ts, unsigned int programid,
- unsigned int pid)
+static void add_pid_to_program(struct Program *p, unsigned int pid)
{
- struct Program *p = get_program(ts, programid);
int i;
if (!p)
return;
p->pids[p->nb_pids++] = pid;
}
-static void set_pmt_found(MpegTSContext *ts, unsigned int programid)
-{
- struct Program *p = get_program(ts, programid);
- if (!p)
- return;
-
- p->pmt_found = 1;
-}
-
static void update_av_program_info(AVFormatContext *s, unsigned int programid,
unsigned int pid, int version)
{
int used = 0, discarded = 0;
struct Program *p;
+ if (pid == PAT_PID)
+ return 0;
+
/* If none of the programs have .discard=AVDISCARD_ALL then there's
* no way we have to discard this packet */
for (k = 0; k < ts->stream->nb_programs; k++)
{
MpegTSFilter *filter;
MpegTSSectionFilter *sec;
+ uint8_t *section_buf = av_mallocz(MAX_SECTION_SIZE);
+
+ if (!section_buf)
+ return NULL;
- if (!(filter = mpegts_open_filter(ts, pid, MPEGTS_SECTION)))
+ if (!(filter = mpegts_open_filter(ts, pid, MPEGTS_SECTION))) {
+ av_free(section_buf);
return NULL;
+ }
sec = &filter->u.section_filter;
sec->section_cb = section_cb;
sec->opaque = opaque;
- sec->section_buf = av_mallocz(MAX_SECTION_SIZE);
+ sec->section_buf = section_buf;
sec->check_crc = check_crc;
sec->last_ver = -1;
- if (!sec->section_buf) {
- av_free(filter);
- return NULL;
- }
return filter;
}
/*init buffer to store stream for probing */
uint8_t buf[PROBE_PACKET_MAX_BUF] = {0};
int buf_size = 0;
+ int max_iterations = 16;
- while (buf_size < PROBE_PACKET_MAX_BUF) {
+ while (buf_size < PROBE_PACKET_MAX_BUF && max_iterations--) {
ret = avio_read_partial(s->pb, buf + buf_size, PROBE_PACKET_MAX_BUF - buf_size);
if (ret < 0)
return AVERROR_INVALIDDATA;
{ 0x24, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_HEVC },
{ 0x42, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_CAVS },
{ 0xd1, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_DIRAC },
+ { 0xd2, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_AVS2 },
{ 0xea, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_VC1 },
{ 0 },
};
st->codecpar->codec_id = types->codec_id;
st->internal->need_context_update = 1;
}
- st->request_probe = 0;
+ st->internal->request_probe = 0;
return;
}
}
mpegts_find_stream_type(st, pes->stream_type, ISO_types);
if (pes->stream_type == 4 || pes->stream_type == 0x0f)
- st->request_probe = 50;
+ st->internal->request_probe = 50;
if ((prog_reg_desc == AV_RL32("HDMV") ||
prog_reg_desc == AV_RL32("HDPR")) &&
st->codecpar->codec_id == AV_CODEC_ID_NONE) {
st->codecpar->codec_type = old_codec_type;
}
if ((st->codecpar->codec_id == AV_CODEC_ID_NONE ||
- (st->request_probe > 0 && st->request_probe < AVPROBE_SCORE_STREAM_RETRY / 5)) &&
+ (st->internal->request_probe > 0 && st->internal->request_probe < AVPROBE_SCORE_STREAM_RETRY / 5)) &&
st->probe_packets > 0 &&
stream_type == STREAM_TYPE_PRIVATE_DATA) {
st->codecpar->codec_type = AVMEDIA_TYPE_DATA;
st->codecpar->codec_id = AV_CODEC_ID_BIN_DATA;
- st->request_probe = AVPROBE_SCORE_STREAM_RETRY / 5;
+ st->internal->request_probe = AVPROBE_SCORE_STREAM_RETRY / 5;
}
/* queue a context update if properties changed */
static void new_data_packet(const uint8_t *buffer, int len, AVPacket *pkt)
{
- av_init_packet(pkt);
+ av_packet_unref(pkt);
pkt->data = (uint8_t *)buffer;
pkt->size = len;
}
{
uint8_t *sd;
- av_init_packet(pkt);
+ av_packet_unref(pkt);
pkt->buf = pes->buffer;
pkt->data = pes->buffer->data;
return (get_bits_count(&gb) + 7) >> 3;
}
+static AVBufferRef *buffer_pool_get(MpegTSContext *ts, int size)
+{
+ int index = av_log2(size + AV_INPUT_BUFFER_PADDING_SIZE);
+ if (!ts->pools[index]) {
+ int pool_size = FFMIN(MAX_PES_PAYLOAD + AV_INPUT_BUFFER_PADDING_SIZE, 2 << index);
+ ts->pools[index] = av_buffer_pool_init(pool_size, NULL);
+ if (!ts->pools[index])
+ return NULL;
+ }
+ return av_buffer_pool_get(ts->pools[index]);
+}
+
/* 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,
pes->total_size = MAX_PES_PAYLOAD;
/* allocate pes buffer */
- pes->buffer = av_buffer_alloc(pes->total_size +
- AV_INPUT_BUFFER_PADDING_SIZE);
+ pes->buffer = buffer_pool_get(ts, pes->total_size);
if (!pes->buffer)
return AVERROR(ENOMEM);
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->codecpar->codec_id == AV_CODEC_ID_NONE && !pes->st->request_probe) {
+ if (pes->st->codecpar->codec_id == AV_CODEC_ID_NONE && !pes->st->internal->request_probe) {
av_log(pes->stream, AV_LOG_TRACE,
"pid=%x stream_type=%x probing\n",
pes->pid,
pes->stream_type);
- pes->st->request_probe = 1;
+ pes->st->internal->request_probe = 1;
}
} else {
pes->pes_header_size = 6;
st = pst;
}
}
- if (f->last_pcr != -1 && st && st->discard != AVDISCARD_ALL) {
+ if (f->last_pcr != -1 && !f->discard) {
// 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 = f->last_pcr / 300;
pcr_found = 1;
- pes->st->pts_wrap_reference = st->pts_wrap_reference;
- pes->st->pts_wrap_behavior = st->pts_wrap_behavior;
+ if (st) {
+ pes->st->internal->pts_wrap_reference = st->internal->pts_wrap_reference;
+ pes->st->internal->pts_wrap_behavior = st->internal->pts_wrap_behavior;
+ }
if (pes->dts == AV_NOPTS_VALUE || pes->dts < pcr) {
pes->pts = pes->dts = pcr;
} else if (pes->st->codecpar->codec_id == AV_CODEC_ID_DVB_TELETEXT &&
}
}
- if (!pcr_found) {
+ if (pes->st->codecpar->codec_id == AV_CODEC_ID_DVB_TELETEXT &&
+ !pcr_found) {
av_log(pes->stream, AV_LOG_VERBOSE,
"Forcing DTS/PTS to be unset for a "
"non-trustworthy PES packet for PID %d as "
if (ret < 0)
return ret;
pes->total_size = MAX_PES_PAYLOAD;
- pes->buffer = av_buffer_alloc(pes->total_size +
- AV_INPUT_BUFFER_PADDING_SIZE);
+ pes->buffer = buffer_pool_get(ts, pes->total_size);
if (!pes->buffer)
return AVERROR(ENOMEM);
ts->stop_parse = 1;
if (idx < 0)
return;
+ /**
+ * In case we receive an SCTE-35 packet before mpegts context is fully
+ * initialized.
+ */
+ if (!ts->pkt)
+ return;
+
new_data_packet(section, section_len, ts->pkt);
ts->pkt->stream_index = idx;
prg = av_find_program_from_stream(ts->stream, NULL, idx);
av_log(fc, AV_LOG_TRACE, "tag: 0x%02x len=%d\n", desc_tag, desc_len);
- if ((st->codecpar->codec_id == AV_CODEC_ID_NONE || st->request_probe > 0) &&
+ if ((st->codecpar->codec_id == AV_CODEC_ID_NONE || st->internal->request_probe > 0) &&
stream_type == STREAM_TYPE_PRIVATE_DATA)
mpegts_find_stream_type(st, desc_tag, DESC_types);
switch (desc_tag) {
- case 0x02: /* video stream descriptor */
+ case VIDEO_STREAM_DESCRIPTOR:
if (get8(pp, desc_end) & 0x1) {
st->disposition |= AV_DISPOSITION_STILL_IMAGE;
}
break;
- case 0x1E: /* SL descriptor */
+ case SL_DESCRIPTOR:
desc_es_id = get16(pp, desc_end);
if (desc_es_id < 0)
break;
mpegts_open_section_filter(ts, pid, m4sl_cb, ts, 1);
}
break;
- case 0x1F: /* FMC descriptor */
+ case FMC_DESCRIPTOR:
if (get16(pp, desc_end) < 0)
break;
if (mp4_descr_count > 0 &&
(st->codecpar->codec_id == AV_CODEC_ID_AAC_LATM ||
- (st->request_probe == 0 && st->codecpar->codec_id == AV_CODEC_ID_NONE) ||
- st->request_probe > 0) &&
+ (st->internal->request_probe == 0 && st->codecpar->codec_id == AV_CODEC_ID_NONE) ||
+ st->internal->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,
ff_mp4_read_dec_config_descr(fc, st, &pb);
if (st->codecpar->codec_id == AV_CODEC_ID_AAC &&
st->codecpar->extradata_size > 0) {
- st->request_probe = st->need_parsing = 0;
+ st->internal->request_probe = st->need_parsing = 0;
st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
st->internal->need_context_update = 1;
}
case 0x56: /* DVB teletext descriptor */
{
uint8_t *extradata = NULL;
- int language_count = desc_len / 5;
+ int language_count = desc_len / 5, ret;
if (desc_len > 0 && desc_len % 5 != 0)
return AVERROR_INVALIDDATA;
av_assert0(language_count <= sizeof(language) / 4);
if (st->codecpar->extradata == NULL) {
- if (ff_alloc_extradata(st->codecpar, language_count * 2)) {
- return AVERROR(ENOMEM);
- }
+ ret = ff_alloc_extradata(st->codecpar, language_count * 2);
+ if (ret < 0)
+ return ret;
}
if (st->codecpar->extradata_size < language_count * 2)
* subtitling_type (1 byte),
* composition_page_id (2 bytes),
* ancillary_page_id (2 bytes) */
- int language_count = desc_len / 8;
+ int language_count = desc_len / 8, ret;
if (desc_len > 0 && desc_len % 8 != 0)
return AVERROR_INVALIDDATA;
av_assert0(language_count <= sizeof(language) / 4);
if (st->codecpar->extradata == NULL) {
- if (ff_alloc_extradata(st->codecpar, language_count * 5)) {
- return AVERROR(ENOMEM);
- }
+ ret = ff_alloc_extradata(st->codecpar, language_count * 5);
+ if (ret < 0)
+ return ret;
}
if (st->codecpar->extradata_size < language_count * 5)
}
}
break;
- case 0x0a: /* ISO 639 language descriptor */
+ case 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);
av_dict_set(&st->metadata, "language", language, AV_DICT_DONT_OVERWRITE);
}
break;
- case 0x05: /* registration descriptor */
+ case REGISTRATION_DESCRIPTOR:
st->codecpar->codec_tag = bytestream_get_le32(pp);
av_log(fc, AV_LOG_TRACE, "reg_desc=%.4s\n", (char *)&st->codecpar->codec_tag);
- if (st->codecpar->codec_id == AV_CODEC_ID_NONE || st->request_probe > 0) {
+ if (st->codecpar->codec_id == AV_CODEC_ID_NONE || st->internal->request_probe > 0) {
mpegts_find_stream_type(st, st->codecpar->codec_tag, REGD_types);
if (st->codecpar->codec_tag == MKTAG('B', 'S', 'S', 'D'))
- st->request_probe = 50;
+ st->internal->request_probe = 50;
}
break;
case 0x52: /* stream identifier descriptor */
st->stream_identifier = 1 + get8(pp, desc_end);
break;
- case 0x26: /* metadata descriptor */
+ case METADATA_DESCRIPTOR:
if (get16(pp, desc_end) == 0xFFFF)
*pp += 4;
if (get8(pp, desc_end) == 0xFF) {
return AVERROR_INVALIDDATA;
if (channel_config_code <= 0x8) {
st->codecpar->extradata[9] = channels = channel_config_code ? channel_config_code : 2;
+ AV_WL32(&st->codecpar->extradata[12], 48000);
st->codecpar->extradata[18] = channel_config_code ? (channels > 2) : /* Dual Mono */ 255;
st->codecpar->extradata[19] = opus_stream_cnt[channel_config_code];
st->codecpar->extradata[20] = opus_coupled_stream_cnt[channel_config_code];
memcpy(&st->codecpar->extradata[21], opus_channel_map[channels - 1], channels);
+ st->codecpar->extradata_size = st->codecpar->extradata[18] ? 21 + channels : 19;
} else {
avpriv_request_sample(fc, "Opus in MPEG-TS - channel_config_code > 0x8");
}
st->codecpar->codec_type = AVMEDIA_TYPE_SUBTITLE;
st->codecpar->codec_id = AV_CODEC_ID_ARIB_CAPTION;
st->codecpar->profile = picked_profile;
- st->request_probe = 0;
+ st->internal->request_probe = 0;
+ }
+ break;
+ case 0xb0: /* DOVI video stream descriptor */
+ {
+ uint32_t buf;
+ AVDOVIDecoderConfigurationRecord *dovi;
+ size_t dovi_size;
+ int ret;
+ if (desc_end - *pp < 4) // (8 + 8 + 7 + 6 + 1 + 1 + 1) / 8
+ return AVERROR_INVALIDDATA;
+
+ dovi = av_dovi_alloc(&dovi_size);
+ if (!dovi)
+ return AVERROR(ENOMEM);
+
+ dovi->dv_version_major = get8(pp, desc_end);
+ dovi->dv_version_minor = get8(pp, desc_end);
+ buf = get16(pp, desc_end);
+ dovi->dv_profile = (buf >> 9) & 0x7f; // 7 bits
+ dovi->dv_level = (buf >> 3) & 0x3f; // 6 bits
+ dovi->rpu_present_flag = (buf >> 2) & 0x01; // 1 bit
+ dovi->el_present_flag = (buf >> 1) & 0x01; // 1 bit
+ dovi->bl_present_flag = buf & 0x01; // 1 bit
+ if (desc_end - *pp >= 20) { // 4 + 4 * 4
+ buf = get8(pp, desc_end);
+ dovi->dv_bl_signal_compatibility_id = (buf >> 4) & 0x0f; // 4 bits
+ } else {
+ // 0 stands for None
+ // Dolby Vision V1.2.93 profiles and levels
+ dovi->dv_bl_signal_compatibility_id = 0;
+ }
+
+ ret = av_stream_add_side_data(st, AV_PKT_DATA_DOVI_CONF,
+ (uint8_t *)dovi, dovi_size);
+ if (ret < 0) {
+ av_free(dovi);
+ return ret;
+ }
+
+ av_log(fc, AV_LOG_TRACE, "DOVI, version: %d.%d, profile: %d, level: %d, "
+ "rpu flag: %d, el flag: %d, bl flag: %d, compatibility id: %d\n",
+ dovi->dv_version_major, dovi->dv_version_minor,
+ dovi->dv_profile, dovi->dv_level,
+ dovi->rpu_present_flag,
+ dovi->el_present_flag,
+ dovi->bl_present_flag,
+ dovi->dv_bl_signal_compatibility_id);
}
break;
default:
return 0;
}
-static AVStream *find_matching_stream(MpegTSContext *ts, int pid,
- int stream_identifier, int pmt_stream_idx)
+static AVStream *find_matching_stream(MpegTSContext *ts, int pid, unsigned int programid,
+ int stream_identifier, int pmt_stream_idx, struct Program *p)
{
AVFormatContext *s = ts->stream;
int i;
AVStream *found = NULL;
- for (i = 0; i < s->nb_streams; i++) {
- AVStream *st = s->streams[i];
- if (stream_identifier != -1) { /* match based on "stream identifier descriptor" if present */
- if (st->stream_identifier == stream_identifier+1) {
- found = st;
- break;
- }
- } else if (st->pmt_stream_idx == pmt_stream_idx) { /* match based on position within the PMT */
- found = st;
- break;
+ if (stream_identifier) { /* match based on "stream identifier descriptor" if present */
+ for (i = 0; i < p->nb_streams; i++) {
+ if (p->streams[i].stream_identifier == stream_identifier)
+ if (!found || pmt_stream_idx == i) /* fallback to idx based guess if multiple streams have the same identifier */
+ found = s->streams[p->streams[i].idx];
}
+ } else if (pmt_stream_idx < p->nb_streams) { /* match based on position within the PMT */
+ found = s->streams[p->streams[pmt_stream_idx].idx];
}
if (found) {
{
MpegTSContext *ts = filter->u.section_filter.opaque;
MpegTSSectionFilter *tssf = &filter->u.section_filter;
+ struct Program old_program;
SectionHeader h1, *h = &h1;
PESContext *pes;
AVStream *st;
int desc_list_len;
uint32_t prog_reg_desc = 0; /* registration descriptor */
int stream_identifier = -1;
+ struct Program *prg;
int mp4_descr_count = 0;
Mp4Descr mp4_descr[MAX_MP4_DESCR_COUNT] = { { 0 } };
if (!ts->scan_all_pmts && ts->skip_changes)
return;
- if (ts->skip_unknown_pmt && !get_program(ts, h->id))
+ prg = get_program(ts, h->id);
+ if (prg)
+ old_program = *prg;
+ else
+ clear_program(&old_program);
+
+ if (ts->skip_unknown_pmt && !prg)
+ return;
+ if (prg && prg->nb_pids && prg->pids[0] != ts->current_pid)
return;
if (!ts->skip_clear)
- clear_program(ts, h->id);
+ clear_avprogram(ts, h->id);
+ clear_program(prg);
+ add_pid_to_program(prg, ts->current_pid);
pcr_pid = get16(&p, p_end);
if (pcr_pid < 0)
return;
pcr_pid &= 0x1fff;
- add_pid_to_pmt(ts, h->id, pcr_pid);
+ add_pid_to_program(prg, pcr_pid);
update_av_program_info(ts->stream, h->id, pcr_pid, h->version);
av_log(ts->stream, AV_LOG_TRACE, "pcr_pid=0x%x\n", pcr_pid);
// something else is broken, exit the program_descriptors_loop
break;
program_info_length -= len + 2;
- if (tag == 0x1d) { // IOD descriptor
+ if (tag == IOD_DESCRIPTOR) {
get8(&p, p_end); // scope
get8(&p, p_end); // label
len -= 2;
mp4_read_iods(ts->stream, p, len, mp4_descr + mp4_descr_count,
&mp4_descr_count, MAX_MP4_DESCR_COUNT);
- } else if (tag == 0x05 && len >= 4) { // registration descriptor
+ } else if (tag == REGISTRATION_DESCRIPTOR && len >= 4) {
prog_reg_desc = bytestream_get_le32(&p);
len -= 4;
}
goto out;
// stop parsing after pmt, we found header
- if (!ts->stream->nb_streams)
+ if (!ts->pkt)
ts->stop_parse = 2;
- set_pmt_found(ts, h->id);
+ if (prg)
+ prg->pmt_found = 1;
-
- for (i = 0; ; i++) {
+ for (i = 0; i < MAX_STREAMS_PER_PROGRAM; i++) {
st = 0;
pes = NULL;
stream_type = get8(&p, p_end);
if (pid == ts->current_pid)
goto out;
- if (ts->merge_pmt_versions)
- stream_identifier = parse_stream_identifier_desc(p, p_end);
+ stream_identifier = parse_stream_identifier_desc(p, p_end) + 1;
/* now create stream */
if (ts->pids[pid] && ts->pids[pid]->type == MPEGTS_PES) {
pes = ts->pids[pid]->u.pes_filter.opaque;
if (ts->merge_pmt_versions && !pes->st) {
- st = find_matching_stream(ts, pid, stream_identifier, i);
+ st = find_matching_stream(ts, pid, h->id, stream_identifier, i, &old_program);
if (st) {
pes->st = st;
pes->stream_type = stream_type;
if (!pes->st)
goto out;
pes->st->id = pes->pid;
- pes->st->program_num = h->id;
- pes->st->pmt_version = h->version;
- pes->st->pmt_stream_idx = i;
}
st = pes->st;
} else if (is_pes_stream(stream_type, prog_reg_desc)) {
mpegts_close_filter(ts, ts->pids[pid]); // wrongly added sdt filter probably
pes = add_pes_stream(ts, pid, pcr_pid);
if (ts->merge_pmt_versions && pes && !pes->st) {
- st = find_matching_stream(ts, pid, stream_identifier, i);
+ st = find_matching_stream(ts, pid, h->id, stream_identifier, i, &old_program);
if (st) {
pes->st = st;
pes->stream_type = stream_type;
if (!st)
goto out;
st->id = pes->pid;
- st->program_num = h->id;
- st->pmt_version = h->version;
- st->pmt_stream_idx = i;
}
} else {
int idx = ff_find_stream_index(ts->stream, pid);
st = ts->stream->streams[idx];
}
if (ts->merge_pmt_versions && !st) {
- st = find_matching_stream(ts, pid, stream_identifier, i);
+ st = find_matching_stream(ts, pid, h->id, stream_identifier, i, &old_program);
}
if (!st) {
st = avformat_new_stream(ts->stream, NULL);
if (!st)
goto out;
st->id = pid;
- st->program_num = h->id;
- st->pmt_version = h->version;
- st->pmt_stream_idx = i;
st->codecpar->codec_type = AVMEDIA_TYPE_DATA;
if (stream_type == 0x86 && prog_reg_desc == AV_RL32("CUEI")) {
mpegts_find_stream_type(st, stream_type, SCTE_types);
if (pes && !pes->stream_type)
mpegts_set_stream_info(st, pes, stream_type, prog_reg_desc);
- add_pid_to_pmt(ts, h->id, pid);
+ add_pid_to_program(prg, pid);
+ if (prg) {
+ prg->streams[i].idx = st->index;
+ prg->streams[i].stream_identifier = stream_identifier;
+ prg->nb_streams++;
+ }
av_program_add_stream_index(ts->stream, h->id, st->index);
SectionHeader h1, *h = &h1;
const uint8_t *p, *p_end;
int sid, pmt_pid;
+ int nb_prg = 0;
AVProgram *program;
av_log(ts->stream, AV_LOG_TRACE, "PAT:\n");
return;
ts->stream->ts_id = h->id;
- clear_programs(ts);
for (;;) {
sid = get16(&p, p_end);
if (sid < 0)
/* NIT info */
} else {
MpegTSFilter *fil = ts->pids[pmt_pid];
+ struct Program *prg;
program = av_new_program(ts->stream, sid);
if (program) {
program->program_num = sid;
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);
+ prg = add_program(ts, sid);
+ if (prg) {
+ unsigned prg_idx = prg - ts->prg;
+ if (prg->nb_pids && prg->pids[0] != pmt_pid)
+ clear_program(prg);
+ add_pid_to_program(prg, pmt_pid);
+ if (prg_idx > nb_prg)
+ FFSWAP(struct Program, ts->prg[nb_prg], ts->prg[prg_idx]);
+ if (prg_idx >= nb_prg)
+ nb_prg++;
+ }
}
}
+ ts->nb_prg = nb_prg;
if (sid < 0) {
int i,j;
}
}
+static void eit_cb(MpegTSFilter *filter, const uint8_t *section, int section_len)
+{
+ MpegTSContext *ts = filter->u.section_filter.opaque;
+ const uint8_t *p, *p_end;
+ SectionHeader h1, *h = &h1;
+
+ /*
+ * Sometimes we receive EPG packets but SDT table do not have
+ * eit_pres_following or eit_sched turned on, so we open EPG
+ * stream directly here.
+ */
+ if (!ts->epg_stream) {
+ ts->epg_stream = avformat_new_stream(ts->stream, NULL);
+ if (!ts->epg_stream)
+ return;
+ ts->epg_stream->id = EIT_PID;
+ ts->epg_stream->codecpar->codec_type = AVMEDIA_TYPE_DATA;
+ ts->epg_stream->codecpar->codec_id = AV_CODEC_ID_EPG;
+ }
+
+ if (ts->epg_stream->discard == AVDISCARD_ALL)
+ return;
+
+ p_end = section + section_len - 4;
+ p = section;
+
+ if (parse_section_header(h, &p, p_end) < 0)
+ return;
+ if (h->tid < EIT_TID || h->tid > OEITS_END_TID)
+ return;
+
+ av_log(ts->stream, AV_LOG_TRACE, "EIT: tid received = %.02x\n", h->tid);
+
+ /**
+ * Service_id 0xFFFF is reserved, it indicates that the current EIT table
+ * is scrambled.
+ */
+ if (h->id == 0xFFFF) {
+ av_log(ts->stream, AV_LOG_TRACE, "Scrambled EIT table received.\n");
+ return;
+ }
+
+ /**
+ * In case we receive an EPG packet before mpegts context is fully
+ * initialized.
+ */
+ if (!ts->pkt)
+ return;
+
+ new_data_packet(section, section_len, ts->pkt);
+ ts->pkt->stream_index = ts->epg_stream->index;
+ ts->stop_parse = 1;
+}
+
static void sdt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len)
{
MpegTSContext *ts = filter->u.section_filter.opaque;
const uint8_t *packet);
/* handle one TS packet */
-static int handle_packet(MpegTSContext *ts, const uint8_t *packet)
+static int handle_packet(MpegTSContext *ts, const uint8_t *packet, int64_t pos)
{
MpegTSFilter *tss;
int len, pid, cc, expected_cc, cc_ok, afc, is_start, is_discontinuity,
has_adaptation, has_payload;
const uint8_t *p, *p_end;
- int64_t pos;
pid = AV_RB16(packet + 1) & 0x1fff;
is_start = packet[1] & 0x40;
if (p >= p_end || !has_payload)
return 0;
- pos = avio_tell(ts->stream->pb);
if (pos >= 0) {
av_assert0(pos >= TS_PACKET_SIZE);
ts->pos47_full = pos - TS_PACKET_SIZE;
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, int seekback, const uint8_t *current_packet)
{
MpegTSContext *ts = s->priv_data;
AVIOContext *pb = s->pb;
int c, i;
uint64_t pos = avio_tell(pb);
-
- avio_seek(pb, -FFMIN(seekback, pos), SEEK_CUR);
+ int64_t back = FFMIN(seekback, pos);
//Special case for files like 01c56b0dc1.ts
if (current_packet[0] == 0x80 && current_packet[12] == 0x47) {
- avio_seek(pb, 12, SEEK_CUR);
+ avio_seek(pb, 12 - back, SEEK_CUR);
return 0;
}
+ avio_seek(pb, -back, SEEK_CUR);
+
for (i = 0; i < ts->resync_size; i++) {
c = avio_r8(pb);
if (avio_feof(pb))
return AVERROR_EOF;
if (c == 0x47) {
+ int new_packet_size, ret;
avio_seek(pb, -1, SEEK_CUR);
- reanalyze(s->priv_data);
+ pos = avio_tell(pb);
+ ret = ffio_ensure_seekback(pb, PROBE_PACKET_MAX_BUF);
+ if (ret < 0)
+ return ret;
+ new_packet_size = get_packet_size(s);
+ if (new_packet_size > 0 && new_packet_size != ts->raw_packet_size) {
+ av_log(ts->stream, AV_LOG_WARNING, "changing packet size to %d\n", new_packet_size);
+ ts->raw_packet_size = new_packet_size;
+ }
+ avio_seek(pb, pos, SEEK_SET);
return 0;
}
}
ret = read_packet(s, packet, ts->raw_packet_size, &data);
if (ret != 0)
break;
- ret = handle_packet(ts, data);
+ ret = handle_packet(ts, data, avio_tell(s->pb));
finished_reading_packet(s, ts->raw_packet_size);
if (ret != 0)
break;
return ret;
}
-static int mpegts_probe(AVProbeData *p)
+static int mpegts_probe(const AVProbeData *p)
{
const int size = p->buf_size;
int maxscore = 0;
MpegTSContext *ts = s->priv_data;
AVIOContext *pb = s->pb;
int64_t pos, probesize = s->probesize;
+ int64_t seekback = FFMAX(s->probesize, (int64_t)ts->resync_size + PROBE_PACKET_MAX_BUF);
s->internal->prefer_codec_framerate = 1;
- if (ffio_ensure_seekback(pb, probesize) < 0)
+ if (ffio_ensure_seekback(pb, seekback) < 0)
av_log(s, AV_LOG_WARNING, "Failed to allocate buffers for seekback\n");
pos = avio_tell(pb);
seek_back(s, pb, pos);
mpegts_open_section_filter(ts, SDT_PID, sdt_cb, ts, 1);
-
mpegts_open_section_filter(ts, PAT_PID, pat_cb, ts, 1);
+ mpegts_open_section_filter(ts, EIT_PID, eit_cb, ts, 1);
handle_packets(ts, probesize / ts->raw_packet_size);
/* if could not find service, enable auto_guess */
AVStream *st;
int pcr_pid, pid, nb_packets, nb_pcrs, ret, pcr_l;
int64_t pcrs[2], pcr_h;
- int packet_count[2];
uint8_t packet[TS_PACKET_SIZE];
const uint8_t *data;
parse_pcr(&pcr_h, &pcr_l, data) == 0) {
finished_reading_packet(s, ts->raw_packet_size);
pcr_pid = pid;
- packet_count[nb_pcrs] = nb_packets;
pcrs[nb_pcrs] = pcr_h * 300 + pcr_l;
nb_pcrs++;
if (nb_pcrs >= 2) {
} else {
av_log(ts->stream, AV_LOG_WARNING, "invalid pcr pair %"PRId64" >= %"PRId64"\n", pcrs[0], pcrs[1]);
pcrs[0] = pcrs[1];
- packet_count[0] = packet_count[1];
nb_pcrs--;
}
}
/* NOTE1: the bitrate is computed without the FEC */
/* NOTE2: it is only the bitrate of the start of the stream */
- ts->pcr_incr = (pcrs[1] - pcrs[0]) / (packet_count[1] - packet_count[0]);
- ts->cur_pcr = pcrs[0] - ts->pcr_incr * packet_count[0];
+ ts->pcr_incr = pcrs[1] - pcrs[0];
+ ts->cur_pcr = pcrs[0] - ts->pcr_incr * (nb_packets - 1);
s->bit_rate = TS_PACKET_SIZE * 8 * 27000000LL / ts->pcr_incr;
st->codecpar->bit_rate = s->bit_rate;
st->start_time = ts->cur_pcr;
- av_log(ts->stream, AV_LOG_TRACE, "start=%0.3f pcr=%0.3f incr=%d\n",
+ av_log(ts->stream, AV_LOG_TRACE, "start=%0.3f pcr=%0.3f incr=%"PRId64"\n",
st->start_time / 1000000.0, pcrs[0] / 27e6, ts->pcr_incr);
}
uint8_t pcr_buf[12];
const uint8_t *data;
- if (av_new_packet(pkt, TS_PACKET_SIZE) < 0)
- return AVERROR(ENOMEM);
+ if ((ret = av_new_packet(pkt, TS_PACKET_SIZE)) < 0)
+ return ret;
ret = read_packet(s, pkt->data, ts->raw_packet_size, &data);
pkt->pos = avio_tell(s->pb);
if (ret < 0) {
- av_packet_unref(pkt);
return ret;
}
if (data != pkt->data)
- memcpy(pkt->data, data, ts->raw_packet_size);
+ memcpy(pkt->data, data, TS_PACKET_SIZE);
finished_reading_packet(s, ts->raw_packet_size);
if (ts->mpeg2ts_compute_pcr) {
/* compute exact PCR for each packet */
clear_programs(ts);
+ for (i = 0; i < FF_ARRAY_ELEMS(ts->pools); i++)
+ av_buffer_pool_uninit(&ts->pools[i]);
+
for (i = 0; i < NB_PID_MAX; i++)
if (ts->pids[i])
mpegts_close_filter(ts, ts->pids[i]);
int64_t *ppos, int64_t pos_limit)
{
MpegTSContext *ts = s->priv_data;
+ AVPacket *pkt;
int64_t pos;
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;
+ pkt = av_packet_alloc();
+ if (!pkt)
+ 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)
+ int ret = av_read_frame(s, pkt);
+ if (ret < 0) {
+ av_packet_free(&pkt);
return AV_NOPTS_VALUE;
- 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) {
- int64_t dts = pkt.dts;
- *ppos = pkt.pos;
- av_packet_unref(&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) {
+ int64_t dts = pkt->dts;
+ *ppos = pkt->pos;
+ av_packet_free(&pkt);
return dts;
}
}
- pos = pkt.pos;
- av_packet_unref(&pkt);
+ pos = pkt->pos;
+ av_packet_unref(pkt);
}
+ av_packet_free(&pkt);
return AV_NOPTS_VALUE;
}
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);
+ mpegts_open_section_filter(ts, EIT_PID, eit_cb, ts, 1);
return ts;
}
buf++;
len--;
} else {
- handle_packet(ts, buf);
+ handle_packet(ts, buf, len1 - len + TS_PACKET_SIZE);
buf += TS_PACKET_SIZE;
len -= TS_PACKET_SIZE;
if (ts->stop_parse == 1)