#include "avio_internal.h"
#include "mpeg.h"
#include "isom.h"
+#if CONFIG_ICONV
+#include <iconv.h>
+#endif
/* maximum size in which we look for synchronization if
* synchronization is lost */
(prev_dividend) = (dividend); \
} while (0)
+#define PROBE_PACKET_MAX_BUF 8192
+#define PROBE_PACKET_MARGIN 5
+
enum MpegTSFilterType {
MPEGTS_PES,
MPEGTS_SECTION,
int es_id;
int last_cc; /* last cc code (-1 if first packet) */
int64_t last_pcr;
+ int discard;
enum MpegTSFilterType type;
union {
MpegTSPESFilter pes_filter;
/** filters for various streams specified by PMT + for the PAT and PMT */
MpegTSFilter *pids[NB_PID_MAX];
int current_pid;
+
+ AVStream *epg_stream;
};
#define MPEGTS_OPTIONS \
return best_score - FFMAX(stat_all - 10*best_score, 0)/10;
}
-/* autodetect fec presence. Must have at least 1024 bytes */
-static int get_packet_size(const uint8_t *buf, int size)
+/* autodetect fec presence */
+static int get_packet_size(AVFormatContext* s)
{
int score, fec_score, dvhs_score;
+ int margin;
+ int ret;
- if (size < (TS_FEC_PACKET_SIZE * 5 + 1))
- return AVERROR_INVALIDDATA;
+ /*init buffer to store stream for probing */
+ uint8_t buf[PROBE_PACKET_MAX_BUF] = {0};
+ int buf_size = 0;
- score = analyze(buf, size, TS_PACKET_SIZE, 0);
- dvhs_score = analyze(buf, size, TS_DVHS_PACKET_SIZE, 0);
- fec_score = analyze(buf, size, TS_FEC_PACKET_SIZE, 0);
- av_log(NULL, AV_LOG_TRACE, "score: %d, dvhs_score: %d, fec_score: %d \n",
- score, dvhs_score, fec_score);
-
- if (score > fec_score && score > dvhs_score)
- return TS_PACKET_SIZE;
- else if (dvhs_score > score && dvhs_score > fec_score)
- return TS_DVHS_PACKET_SIZE;
- else if (score < fec_score && dvhs_score < fec_score)
- return TS_FEC_PACKET_SIZE;
- else
- return AVERROR_INVALIDDATA;
+ while (buf_size < PROBE_PACKET_MAX_BUF) {
+ ret = avio_read_partial(s->pb, buf + buf_size, PROBE_PACKET_MAX_BUF - buf_size);
+ if (ret < 0)
+ return AVERROR_INVALIDDATA;
+ buf_size += ret;
+
+ score = analyze(buf, buf_size, TS_PACKET_SIZE, 0);
+ dvhs_score = analyze(buf, buf_size, TS_DVHS_PACKET_SIZE, 0);
+ fec_score = analyze(buf, buf_size, TS_FEC_PACKET_SIZE, 0);
+ av_log(s, AV_LOG_TRACE, "Probe: %d, score: %d, dvhs_score: %d, fec_score: %d \n",
+ buf_size, score, dvhs_score, fec_score);
+
+ margin = mid_pred(score, fec_score, dvhs_score);
+
+ if (buf_size < PROBE_PACKET_MAX_BUF)
+ margin += PROBE_PACKET_MARGIN; /*if buffer not filled */
+
+ if (score > margin)
+ return TS_PACKET_SIZE;
+ else if (dvhs_score > margin)
+ return TS_DVHS_PACKET_SIZE;
+ else if (fec_score > margin)
+ return TS_FEC_PACKET_SIZE;
+ }
+ return AVERROR_INVALIDDATA;
}
typedef struct SectionHeader {
return NULL;
if (len > p_end - p)
return NULL;
+#if CONFIG_ICONV
+ if (len) {
+ const char *encodings[] = {
+ "ISO6937", "ISO-8859-5", "ISO-8859-6", "ISO-8859-7",
+ "ISO-8859-8", "ISO-8859-9", "ISO-8859-10", "ISO-8859-11",
+ "", "ISO-8859-13", "ISO-8859-14", "ISO-8859-15", "", "", "", "",
+ "", "UCS-2BE", "KSC_5601", "GB2312", "UCS-2BE", "UTF-8", "", "",
+ "", "", "", "", "", "", "", ""
+ };
+ iconv_t cd;
+ char *in, *out;
+ size_t inlen = len, outlen = inlen * 6 + 1;
+ if (len >= 3 && p[0] == 0x10 && !p[1] && p[2] && p[2] <= 0xf && p[2] != 0xc) {
+ char iso8859[12];
+ snprintf(iso8859, sizeof(iso8859), "ISO-8859-%d", p[2]);
+ inlen -= 3;
+ in = (char *)p + 3;
+ cd = iconv_open("UTF-8", iso8859);
+ } else if (p[0] < 0x20) {
+ inlen -= 1;
+ in = (char *)p + 1;
+ cd = iconv_open("UTF-8", encodings[*p]);
+ } else {
+ in = (char *)p;
+ cd = iconv_open("UTF-8", encodings[0]);
+ }
+ if (cd == (iconv_t)-1)
+ goto no_iconv;
+ str = out = av_malloc(outlen);
+ if (!str) {
+ iconv_close(cd);
+ return NULL;
+ }
+ if (iconv(cd, &in, &inlen, &out, &outlen) == -1) {
+ iconv_close(cd);
+ av_freep(&str);
+ goto no_iconv;
+ }
+ iconv_close(cd);
+ *out = 0;
+ *pp = p + len;
+ return str;
+ }
+no_iconv:
+#endif
str = av_malloc(len + 1);
if (!str)
return NULL;
{ 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 },
};
static int new_pes_packet(PESContext *pes, AVPacket *pkt)
{
- char *sd;
+ uint8_t *sd;
av_init_packet(pkt);
|| pes->st->codecpar->codec_id == AV_CODEC_ID_DVB_SUBTITLE)
) {
AVProgram *p = NULL;
+ int pcr_found = 0;
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];
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;
- pes->st->pts_wrap_reference = st->pts_wrap_reference;
- pes->st->pts_wrap_behavior = st->pts_wrap_behavior;
+ pcr_found = 1;
+ if (st) {
+ pes->st->pts_wrap_reference = st->pts_wrap_reference;
+ pes->st->pts_wrap_behavior = st->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) {
+ av_log(pes->stream, AV_LOG_VERBOSE,
+ "Forcing DTS/PTS to be unset for a "
+ "non-trustworthy PES packet for PID %d as "
+ "PCR hasn't been received yet.\n",
+ pes->pid);
+ pes->dts = pes->pts = AV_NOPTS_VALUE;
+ }
}
}
break;
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);
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 0xfd: /* ARIB data coding type descriptor */
+ // STD-B24, fascicle 3, chapter 4 defines private_stream_1
+ // for captions
+ if (stream_type == STREAM_TYPE_PRIVATE_DATA) {
+ // This structure is defined in STD-B10, part 1, listing 5.4 and
+ // part 2, 6.2.20).
+ // Listing of data_component_ids is in STD-B10, part 2, Annex J.
+ // Component tag limits are documented in TR-B14, fascicle 2,
+ // Vol. 3, Section 2, 4.2.8.1
+ int actual_component_tag = st->stream_identifier - 1;
+ int picked_profile = FF_PROFILE_UNKNOWN;
+ int data_component_id = get16(pp, desc_end);
+ if (data_component_id < 0)
+ return AVERROR_INVALIDDATA;
+
+ switch (data_component_id) {
+ case 0x0008:
+ // [0x30..0x37] are component tags utilized for
+ // non-mobile captioning service ("profile A").
+ if (actual_component_tag >= 0x30 &&
+ actual_component_tag <= 0x37) {
+ picked_profile = FF_PROFILE_ARIB_PROFILE_A;
+ }
+ break;
+ case 0x0012:
+ // component tag 0x87 signifies a mobile/partial reception
+ // (1seg) captioning service ("profile C").
+ if (actual_component_tag == 0x87) {
+ picked_profile = FF_PROFILE_ARIB_PROFILE_C;
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (picked_profile == FF_PROFILE_UNKNOWN)
+ break;
+
+ 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;
+ }
+ break;
default:
break;
}
return 0;
}
-static AVStream *find_matching_stream(MpegTSContext *ts, int pid,
+static AVStream *find_matching_stream(MpegTSContext *ts, int pid, unsigned int programid,
int stream_identifier, int pmt_stream_idx)
{
AVFormatContext *s = ts->stream;
for (i = 0; i < s->nb_streams; i++) {
AVStream *st = s->streams[i];
+ if (st->program_num != programid)
+ continue;
if (stream_identifier != -1) { /* match based on "stream identifier descriptor" if present */
if (st->stream_identifier == stream_identifier+1) {
found = st;
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);
if (st) {
pes->st = st;
pes->stream_type = stream_type;
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);
if (st) {
pes->st = st;
pes->stream_type = stream_type;
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);
}
if (!st) {
st = avformat_new_stream(ts->stream, NULL);
}
}
+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;
- if (pid && discard_pid(ts, pid))
- return 0;
is_start = packet[1] & 0x40;
tss = ts->pids[pid];
if (ts->auto_guess && !tss && is_start) {
}
if (!tss)
return 0;
+ if (is_start)
+ tss->discard = discard_pid(ts, pid);
+ if (tss->discard)
+ return 0;
ts->current_pid = pid;
afc = (packet[3] >> 4) & 3;
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;
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;
- uint8_t buf[8 * 1024] = {0};
- int len;
int64_t pos, probesize = s->probesize;
s->internal->prefer_codec_framerate = 1;
if (ffio_ensure_seekback(pb, probesize) < 0)
av_log(s, AV_LOG_WARNING, "Failed to allocate buffers for seekback\n");
- /* read the first 8192 bytes to get packet size */
pos = avio_tell(pb);
- len = avio_read(pb, buf, sizeof(buf));
- ts->raw_packet_size = get_packet_size(buf, len);
+ ts->raw_packet_size = get_packet_size(s);
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;
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 */
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)
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)