Newer
Older
Fabrice Bellard
committed
}
if (!tss)
return;
/* continuity check (currently not used) */
cc = (packet[3] & 0xf);
cc_ok = (tss->last_cc < 0) || ((((tss->last_cc + 1) & 0x0f) == cc));
tss->last_cc = cc;
Fabrice Bellard
committed
/* skip adaptation field */
afc = (packet[3] >> 4) & 3;
p = packet + 4;
if (afc == 0) /* reserved value */
return;
if (afc == 2) /* adaptation field only */
return;
if (afc == 3) {
/* skip adapation field */
p += p[0] + 1;
}
/* if past the end of packet, ignore */
p_end = packet + TS_PACKET_SIZE;
if (p >= p_end)
return;
Fabrice Bellard
committed
if (tss->type == MPEGTS_SECTION) {
if (is_start) {
/* pointer field present */
len = *p++;
if (p + len > p_end)
return;
if (len && cc_ok) {
Wolfram Gloger
committed
/* write remaining section bytes */
Fabrice Bellard
committed
p, len, 0);
Wolfram Gloger
committed
/* check whether filter has been closed */
if (!ts->pids[pid])
return;
Fabrice Bellard
committed
}
p += len;
if (p < p_end) {
Fabrice Bellard
committed
p, p_end - p, 1);
}
} else {
if (cc_ok) {
Fabrice Bellard
committed
p, p_end - p, 0);
Fabrice Bellard
committed
}
Fabrice Bellard
committed
}
} else {
tss->u.pes_filter.pes_cb(tss->u.pes_filter.opaque,
Fabrice Bellard
committed
p, p_end - p, is_start);
}
}
Fabrice Bellard
committed
/* XXX: try to find a better synchro over several packets (use
get_packet_size() ?) */
Fabrice Bellard
committed
static int mpegts_resync(ByteIOContext *pb)
Fabrice Bellard
committed
{
int c, i;
for(i = 0;i < MAX_RESYNC_SIZE; i++) {
c = url_fgetc(pb);
if (c < 0)
return -1;
if (c == 0x47) {
url_fseek(pb, -1, SEEK_CUR);
return 0;
}
}
/* no sync found */
return -1;
}
Fabrice Bellard
committed
/* return -1 if error or EOF. Return 0 if OK. */
static int read_packet(ByteIOContext *pb, uint8_t *buf, int raw_packet_size)
Fabrice Bellard
committed
{
Fabrice Bellard
committed
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
int skip, len;
for(;;) {
len = get_buffer(pb, buf, TS_PACKET_SIZE);
if (len != TS_PACKET_SIZE)
return AVERROR_IO;
/* 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;
else
continue;
} else {
skip = raw_packet_size - TS_PACKET_SIZE;
if (skip > 0)
url_fskip(pb, skip);
break;
}
}
return 0;
}
static int handle_packets(MpegTSContext *ts, int nb_packets)
{
AVFormatContext *s = ts->stream;
Fabrice Bellard
committed
ByteIOContext *pb = &s->pb;
Fabrice Bellard
committed
uint8_t packet[TS_PACKET_SIZE];
int packet_num, ret;
Fabrice Bellard
committed
ts->stop_parse = 0;
packet_num = 0;
Fabrice Bellard
committed
if (ts->stop_parse)
break;
packet_num++;
if (nb_packets != 0 && packet_num >= nb_packets)
break;
Fabrice Bellard
committed
ret = read_packet(pb, packet, ts->raw_packet_size);
if (ret != 0)
return ret;
handle_packet(ts, packet);
Fabrice Bellard
committed
}
return 0;
}
Fabrice Bellard
committed
static int mpegts_probe(AVProbeData *p)
{
Fabrice Bellard
committed
#if 1
int score, fec_score, dvhs_score;
if (size < (TS_FEC_PACKET_SIZE * CHECK_COUNT))
return -1;
score = analyze(p->buf, TS_PACKET_SIZE *CHECK_COUNT, TS_PACKET_SIZE, NULL);
dvhs_score = analyze(p->buf, TS_DVHS_PACKET_SIZE *CHECK_COUNT, TS_DVHS_PACKET_SIZE, NULL);
fec_score= analyze(p->buf, TS_FEC_PACKET_SIZE*CHECK_COUNT, TS_FEC_PACKET_SIZE, NULL);
// av_log(NULL, AV_LOG_DEBUG, "score: %d, dvhs_score: %d, fec_score: %d \n", score, dvhs_score, fec_score);
// 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;
Fabrice Bellard
committed
#else
/* only use the extension for safer guess */
if (match_ext(p->filename, "ts"))
return AVPROBE_SCORE_MAX;
else
return 0;
#endif
Fabrice Bellard
committed
}
static void set_service_cb(void *opaque, int ret)
Fabrice Bellard
committed
{
MpegTSContext *ts = opaque;
ts->set_service_ret = ret;
ts->stop_parse = 1;
}
Fabrice Bellard
committed
/* return the 90 kHz PCR and the extension for the 27 MHz PCR. return
(-1) if not available */
static int parse_pcr(int64_t *ppcr_high, int *ppcr_low,
Fabrice Bellard
committed
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
const uint8_t *packet)
{
int afc, len, flags;
const uint8_t *p;
unsigned int v;
afc = (packet[3] >> 4) & 3;
if (afc <= 1)
return -1;
p = packet + 4;
len = p[0];
p++;
if (len == 0)
return -1;
flags = *p++;
len--;
if (!(flags & 0x10))
return -1;
if (len < 6)
return -1;
v = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
*ppcr_high = ((int64_t)v << 1) | (p[4] >> 7);
*ppcr_low = ((p[4] & 1) << 8) | p[5];
return 0;
}
Fabrice Bellard
committed
static int mpegts_read_header(AVFormatContext *s,
AVFormatParameters *ap)
{
MpegTSContext *ts = s->priv_data;
ByteIOContext *pb = &s->pb;
uint8_t buf[1024];
int len, sid, i;
Fabrice Bellard
committed
int64_t pos;
MpegTSService *service;
Fabrice Bellard
committed
if (ap) {
ts->mpeg2ts_raw = ap->mpeg2ts_raw;
ts->mpeg2ts_compute_pcr = ap->mpeg2ts_compute_pcr;
}
Fabrice Bellard
committed
/* read the first 1024 bytes to get packet size */
pos = url_ftell(pb);
len = get_buffer(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;
Fabrice Bellard
committed
ts->stream = s;
Fabrice Bellard
committed
ts->auto_guess = 0;
Fabrice Bellard
committed
if (!ts->mpeg2ts_raw) {
/* normal demux */
Fabrice Bellard
committed
Fabrice Bellard
committed
if (!ts->auto_guess) {
ts->set_service_ret = -1;
Michael Niedermayer
committed
Fabrice Bellard
committed
/* first do a scaning to get all the services */
Fabrice Bellard
committed
url_fseek(pb, pos, SEEK_SET);
Fabrice Bellard
committed
mpegts_scan_sdt(ts);
Nico Sabbi
committed
handle_packets(ts, s->probesize);
Fabrice Bellard
committed
if (ts->nb_services <= 0) {
/* no SDT found, we try to look at the PAT */
Fabrice Bellard
committed
/* First remove the SDT filters from each PID */
int i;
for (i=0; i < NB_PID_MAX; i++) {
if (ts->pids[i])
mpegts_close_filter(ts, ts->pids[i]);
}
url_fseek(pb, pos, SEEK_SET);
mpegts_scan_pat(ts);
Nico Sabbi
committed
handle_packets(ts, s->probesize);
Fabrice Bellard
committed
}
Wolfram Gloger
committed
if (ts->nb_services <= 0) {
/* raw transport stream */
ts->auto_guess = 1;
s->ctx_flags |= AVFMTCTX_NOHEADER;
goto do_pcr;
}
Fabrice Bellard
committed
/* tune to first service found */
for(i=0; i<ts->nb_services && ts->set_service_ret; i++){
service = ts->services[i];
sid = service->sid;
Fabrice Bellard
committed
#ifdef DEBUG_SI
Daniel Kristjansson
committed
av_log(NULL, AV_LOG_DEBUG, "tuning to '%s'\n", service->name);
Fabrice Bellard
committed
#endif
/* now find the info for the first service if we found any,
otherwise try to filter all PATs */
url_fseek(pb, pos, SEEK_SET);
mpegts_set_service(ts, sid, set_service_cb, ts);
Nico Sabbi
committed
handle_packets(ts, s->probesize);
Fabrice Bellard
committed
/* if could not find service, exit */
if (ts->set_service_ret != 0) {
if(ts->auto_guess)
return -1;
else {
//let's retry with auto_guess set
ts->auto_guess = 1;
goto goto_auto_guess;
}
}
Fabrice Bellard
committed
#ifdef DEBUG_SI
Daniel Kristjansson
committed
av_log(NULL, AV_LOG_DEBUG, "tuning done\n");
Fabrice Bellard
committed
#endif
}
s->ctx_flags |= AVFMTCTX_NOHEADER;
} else {
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];
Fabrice Bellard
committed
/* only read packets */
Wolfram Gloger
committed
do_pcr:
Fabrice Bellard
committed
st = av_new_stream(s, 0);
if (!st)
goto fail;
av_set_pts_info(st, 60, 1, 27000000);
Michael Niedermayer
committed
st->codec->codec_type = CODEC_TYPE_DATA;
st->codec->codec_id = CODEC_ID_MPEG2TS;
Fabrice Bellard
committed
/* we iterate until we find two PCRs to estimate the bitrate */
pcr_pid = -1;
nb_pcrs = 0;
nb_packets = 0;
for(;;) {
ret = read_packet(&s->pb, packet, ts->raw_packet_size);
if (ret < 0)
return -1;
pid = ((packet[1] & 0x1f) << 8) | packet[2];
if ((pcr_pid == -1 || pcr_pid == pid) &&
parse_pcr(&pcr_h, &pcr_l, packet) == 0) {
pcr_pid = pid;
packet_count[nb_pcrs] = nb_packets;
pcrs[nb_pcrs] = pcr_h * 300 + pcr_l;
nb_pcrs++;
if (nb_pcrs >= 2)
break;
}
nb_packets++;
}
Fabrice Bellard
committed
Fabrice Bellard
committed
/* 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];
s->bit_rate = (TS_PACKET_SIZE * 8) * 27e6 / ts->pcr_incr;
Michael Niedermayer
committed
st->codec->bit_rate = s->bit_rate;
Fabrice Bellard
committed
#if 0
Daniel Kristjansson
committed
av_log(NULL, AV_LOG_DEBUG, "start=%0.3f pcr=%0.3f incr=%d\n",
Fabrice Bellard
committed
st->start_time / 1000000.0, pcrs[0] / 27e6, ts->pcr_incr);
Fabrice Bellard
committed
#endif
Fabrice Bellard
committed
url_fseek(pb, pos, SEEK_SET);
Fabrice Bellard
committed
fail:
return -1;
}
Fabrice Bellard
committed
#define MAX_PACKET_READAHEAD ((128 * 1024) / 188)
static int mpegts_raw_read_packet(AVFormatContext *s,
AVPacket *pkt)
{
MpegTSContext *ts = s->priv_data;
int ret, i;
int64_t pcr_h, next_pcr_h, pos;
int pcr_l, next_pcr_l;
uint8_t pcr_buf[12];
if (av_new_packet(pkt, TS_PACKET_SIZE) < 0)
return -ENOMEM;
Fabrice Bellard
committed
ret = read_packet(&s->pb, pkt->data, ts->raw_packet_size);
if (ret < 0) {
av_free_packet(pkt);
return ret;
}
if (ts->mpeg2ts_compute_pcr) {
/* 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);
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);
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)) /
Fabrice Bellard
committed
(i + 1);
break;
}
}
url_fseek(&s->pb, pos, SEEK_SET);
/* no next PCR found: we use previous increment */
ts->cur_pcr = pcr_h * 300 + pcr_l;
}
pkt->pts = ts->cur_pcr;
pkt->duration = ts->pcr_incr;
ts->cur_pcr += ts->pcr_incr;
}
pkt->stream_index = 0;
return 0;
}
Fabrice Bellard
committed
static int mpegts_read_packet(AVFormatContext *s,
AVPacket *pkt)
{
MpegTSContext *ts = s->priv_data;
Fabrice Bellard
committed
if (!ts->mpeg2ts_raw) {
ts->pkt = pkt;
return handle_packets(ts, 0);
} else {
return mpegts_raw_read_packet(s, pkt);
}
}
static int mpegts_read_close(AVFormatContext *s)
{
MpegTSContext *ts = s->priv_data;
int i;
for(i=0;i<NB_PID_MAX;i++)
if (ts->pids[i]) mpegts_close_filter(ts, ts->pids[i]);
for(i = 0; i < ts->nb_services; i++){
av_free(ts->services[i]->provider_name);
av_free(ts->services[i]->name);
av_free(ts->services[i]);
}
av_freep(&ts->services);
return 0;
}
static 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, pid;
const int find_next= 1;
pos = ((*ppos + ts->raw_packet_size - 1) / ts->raw_packet_size) * ts->raw_packet_size;
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
if (find_next) {
for(;;) {
url_fseek(&s->pb, pos, SEEK_SET);
if (get_buffer(&s->pb, buf, TS_PACKET_SIZE) != TS_PACKET_SIZE)
return AV_NOPTS_VALUE;
pid = ((buf[1] & 0x1f) << 8) | buf[2];
if (pid == ts->pcr_pid &&
parse_pcr(×tamp, &pcr_l, buf) == 0) {
break;
}
pos += ts->raw_packet_size;
}
} else {
for(;;) {
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)
return AV_NOPTS_VALUE;
pid = ((buf[1] & 0x1f) << 8) | buf[2];
if (pid == ts->pcr_pid &&
parse_pcr(×tamp, &pcr_l, buf) == 0) {
break;
}
}
}
*ppos = pos;
return timestamp;
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];
int64_t pos;
if(av_seek_frame_binary(s, stream_index, target_ts, flags) < 0)
return -1;
pos= url_ftell(&s->pb);
for(;;) {
url_fseek(&s->pb, pos, SEEK_SET);
if (get_buffer(&s->pb, buf, TS_PACKET_SIZE) != TS_PACKET_SIZE)
return -1;
// pid = ((buf[1] & 0x1f) << 8) | buf[2];
if(buf[1] & 0x40) break;
pos += ts->raw_packet_size;
url_fseek(&s->pb, pos, SEEK_SET);
return 0;
}
Fabrice Bellard
committed
/**************************************************************/
/* parsing functions - called from other demuxers such as RTP */
MpegTSContext *mpegts_parse_open(AVFormatContext *s)
{
MpegTSContext *ts;
Fabrice Bellard
committed
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
ts = av_mallocz(sizeof(MpegTSContext));
if (!ts)
return NULL;
/* no stream case, currently used by RTP */
ts->raw_packet_size = TS_PACKET_SIZE;
ts->stream = s;
ts->auto_guess = 1;
return ts;
}
/* return the consumed length if a packet was output, or -1 if no
packet is output */
int mpegts_parse_packet(MpegTSContext *ts, AVPacket *pkt,
const uint8_t *buf, int len)
{
int len1;
len1 = len;
ts->pkt = pkt;
ts->stop_parse = 0;
for(;;) {
if (ts->stop_parse)
break;
if (len < TS_PACKET_SIZE)
return -1;
if (buf[0] != 0x47) {
Wolfram Gloger
committed
buf++;
Fabrice Bellard
committed
len--;
} else {
handle_packet(ts, buf);
buf += TS_PACKET_SIZE;
len -= TS_PACKET_SIZE;
}
}
return len1 - len;
}
void mpegts_parse_close(MpegTSContext *ts)
{
int i;
for(i=0;i<NB_PID_MAX;i++)
av_free(ts->pids[i]);
av_free(ts);
}
AVInputFormat mpegts_demuxer = {
"mpegts",
"MPEG2 transport stream format",
sizeof(MpegTSContext),
mpegts_probe,
mpegts_read_header,
mpegts_read_packet,
mpegts_read_close,
mpegts_get_pcr,
Fabrice Bellard
committed
.flags = AVFMT_SHOW_IDS,