Newer
Older
* MPEG2 transport stream (aka DVB) demuxer
* Copyright (c) 2002-2003 Fabrice Bellard
Diego Biurrun
committed
* This file is part of FFmpeg.
*
* FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
Diego Biurrun
committed
* version 2.1 of the License, or (at your option) any later version.
Diego Biurrun
committed
* FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General Public
Diego Biurrun
committed
* License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
//#define DEBUG
//#define DEBUG_SEEK
#include "libavutil/crc.h"
#include "libavutil/intreadwrite.h"
#include "libavcodec/bytestream.h"
#include "avformat.h"
Fabrice Bellard
committed
#include "mpegts.h"
Diego Biurrun
committed
#include "internal.h"
Fabrice Bellard
committed
/* 1.0 second at 24Mbit/s */
Fabrice Bellard
committed
#define MAX_SCAN_PACKETS 32000
/* maximum size in which we look for synchronisation if
synchronisation is lost */
#define MAX_RESYNC_SIZE 4096
Fabrice Bellard
committed
typedef struct PESContext PESContext;
Michael Niedermayer
committed
static PESContext* add_pes_stream(MpegTSContext *ts, int pid, int pcr_pid, int stream_type);
Fabrice Bellard
committed
enum MpegTSFilterType {
MPEGTS_PES,
MPEGTS_SECTION,
typedef struct MpegTSFilter MpegTSFilter;
typedef int PESCallback(MpegTSFilter *f, const uint8_t *buf, int len, int is_start, int64_t pos);
Fabrice Bellard
committed
typedef struct MpegTSPESFilter {
PESCallback *pes_cb;
void *opaque;
} MpegTSPESFilter;
typedef void SectionCallback(MpegTSFilter *f, const uint8_t *buf, int len);
Fabrice Bellard
committed
typedef void SetServiceCallback(void *opaque, int ret);
typedef struct MpegTSSectionFilter {
int section_index;
int section_h_size;
uint8_t *section_buf;
unsigned int check_crc:1;
unsigned int end_of_section_reached:1;
Fabrice Bellard
committed
SectionCallback *section_cb;
void *opaque;
} MpegTSSectionFilter;
struct MpegTSFilter {
int pid;
int last_cc; /* last cc code (-1 if first packet) */
Fabrice Bellard
committed
enum MpegTSFilterType type;
union {
MpegTSPESFilter pes_filter;
MpegTSSectionFilter section_filter;
} u;
Fabrice Bellard
committed
Nico Sabbi
committed
#define MAX_PIDS_PER_PROGRAM 64
Nico Sabbi
committed
unsigned int id; //program id/service id
unsigned int nb_pids;
unsigned int pids[MAX_PIDS_PER_PROGRAM];
Nico Sabbi
committed
Fabrice Bellard
committed
struct MpegTSContext {
Fabrice Bellard
committed
/* user data */
AVFormatContext *stream;
Daniel Kristjansson
committed
/** raw packet size, including FEC if present */
int raw_packet_size;
Michael Niedermayer
committed
int pos47;
Daniel Kristjansson
committed
/** if true, all pids are analyzed to find streams */
int auto_guess;
Fabrice Bellard
committed
Daniel Kristjansson
committed
/** compute exact PCR for each transport stream packet */
int mpeg2ts_compute_pcr;
Fabrice Bellard
committed
Daniel Kristjansson
committed
int64_t cur_pcr; /**< used to estimate the exact PCR */
int pcr_incr; /**< used to estimate the exact PCR */
Fabrice Bellard
committed
/* data needed to handle file based ts */
Daniel Kristjansson
committed
/** stop parsing loop */
int stop_parse;
/** packet containing Audio/Video data */
AVPacket *pkt;
/** to detect seek */
int64_t last_pos;
Fabrice Bellard
committed
/******************************************/
/* private mpegts data */
/* scan context */
Nico Sabbi
committed
/** structure to keep track of Program->pids mapping */
unsigned int nb_prg;
struct Program *prg;
Fabrice Bellard
committed
Daniel Kristjansson
committed
/** filters for various streams specified by PMT + for the PAT and PMT */
Fabrice Bellard
committed
MpegTSFilter *pids[NB_PID_MAX];
Fabrice Bellard
committed
};
/* TS stream handling */
enum MpegTSState {
MPEGTS_HEADER = 0,
MPEGTS_PESHEADER_FILL,
MPEGTS_PAYLOAD,
MPEGTS_SKIP,
};
/* enough for PES header + length */
#define PES_START_SIZE 9
#define MAX_PES_HEADER_SIZE (9 + 255)
struct PESContext {
int pid;
Michael Niedermayer
committed
int pcr_pid; /**< if -1 then all packets containing PCR are considered */
int stream_type;
MpegTSContext *ts;
AVFormatContext *stream;
AVStream *st;
enum MpegTSState state;
/* used to get the format */
int data_index;
int total_size;
int pes_header_size;
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];
extern AVInputFormat mpegts_demuxer;
Nico Sabbi
committed
static void clear_program(MpegTSContext *ts, unsigned int programid)
{
int i;
for(i=0; i<ts->nb_prg; i++)
if(ts->prg[i].id == programid)
ts->prg[i].nb_pids = 0;
}
static void clear_programs(MpegTSContext *ts)
{
av_freep(&ts->prg);
ts->nb_prg=0;
}
static void add_pat_entry(MpegTSContext *ts, unsigned int programid)
{
struct Program *p;
void *tmp = av_realloc(ts->prg, (ts->nb_prg+1)*sizeof(struct Program));
Nico Sabbi
committed
if(!tmp)
return;
ts->prg = tmp;
p = &ts->prg[ts->nb_prg];
p->id = programid;
p->nb_pids = 0;
ts->nb_prg++;
}
static void add_pid_to_pmt(MpegTSContext *ts, unsigned int programid, unsigned int pid)
{
int i;
struct Program *p = NULL;
Nico Sabbi
committed
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
for(i=0; i<ts->nb_prg; i++) {
if(ts->prg[i].id == programid) {
p = &ts->prg[i];
break;
}
}
if(!p)
return;
if(p->nb_pids >= MAX_PIDS_PER_PROGRAM)
return;
p->pids[p->nb_pids++] = pid;
}
/**
* \brief discard_pid() decides if the pid is to be discarded according
* to caller's programs selection
* \param ts : - TS context
* \param pid : - pid
* \return 1 if the pid is only comprised in programs that have .discard=AVDISCARD_ALL
* 0 otherwise
*/
static int discard_pid(MpegTSContext *ts, unsigned int pid)
{
int i, j, k;
int used = 0, discarded = 0;
struct Program *p;
Nico Sabbi
committed
for(i=0; i<ts->nb_prg; i++) {
p = &ts->prg[i];
for(j=0; j<p->nb_pids; j++) {
if(p->pids[j] != pid)
continue;
//is program with id p->id set to be discarded?
for(k=0; k<ts->stream->nb_programs; k++) {
if(ts->stream->programs[k]->id == p->id) {
if(ts->stream->programs[k]->discard == AVDISCARD_ALL)
discarded++;
else
used++;
}
}
}
}
return !used && discarded;
Nico Sabbi
committed
}
Daniel Kristjansson
committed
/**
* Assembles PES packets out of TS packets, and then calls the "section_cb"
* function when they are complete.
*/
Fabrice Bellard
committed
static void write_section_data(AVFormatContext *s, MpegTSFilter *tss1,
const uint8_t *buf, int buf_size, int is_start)
{
MpegTSSectionFilter *tss = &tss1->u.section_filter;
int len;
Fabrice Bellard
committed
if (is_start) {
memcpy(tss->section_buf, buf, buf_size);
tss->section_index = buf_size;
tss->section_h_size = -1;
tss->end_of_section_reached = 0;
} else {
if (tss->end_of_section_reached)
return;
len = 4096 - tss->section_index;
if (buf_size < len)
len = buf_size;
memcpy(tss->section_buf + tss->section_index, buf, len);
tss->section_index += len;
}
/* compute section length if possible */
if (tss->section_h_size == -1 && tss->section_index >= 3) {
len = (AV_RB16(tss->section_buf + 1) & 0xfff) + 3;
Fabrice Bellard
committed
if (len > 4096)
return;
tss->section_h_size = len;
}
if (tss->section_h_size != -1 && tss->section_index >= tss->section_h_size) {
tss->end_of_section_reached = 1;
Wolfram Gloger
committed
if (!tss->check_crc ||
av_crc(av_crc_get_table(AV_CRC_32_IEEE), -1,
tss->section_buf, tss->section_h_size) == 0)
tss->section_cb(tss1, tss->section_buf, tss->section_h_size);
Fabrice Bellard
committed
}
}
Stefan Huehner
committed
static MpegTSFilter *mpegts_open_section_filter(MpegTSContext *ts, unsigned int pid,
Fabrice Bellard
committed
SectionCallback *section_cb, void *opaque,
int check_crc)
{
MpegTSFilter *filter;
MpegTSSectionFilter *sec;
dprintf(ts->stream, "Filter: pid=0x%x\n", pid);
Fabrice Bellard
committed
if (pid >= NB_PID_MAX || ts->pids[pid])
return NULL;
filter = av_mallocz(sizeof(MpegTSFilter));
Fabrice Bellard
committed
return NULL;
ts->pids[pid] = filter;
filter->type = MPEGTS_SECTION;
filter->pid = pid;
filter->last_cc = -1;
sec = &filter->u.section_filter;
sec->section_cb = section_cb;
sec->opaque = opaque;
sec->section_buf = av_malloc(MAX_SECTION_SIZE);
sec->check_crc = check_crc;
if (!sec->section_buf) {
av_free(filter);
return NULL;
}
return filter;
}
Stefan Huehner
committed
static MpegTSFilter *mpegts_open_pes_filter(MpegTSContext *ts, unsigned int pid,
Fabrice Bellard
committed
PESCallback *pes_cb,
void *opaque)
{
MpegTSFilter *filter;
MpegTSPESFilter *pes;
if (pid >= NB_PID_MAX || ts->pids[pid])
return NULL;
filter = av_mallocz(sizeof(MpegTSFilter));
Fabrice Bellard
committed
return NULL;
ts->pids[pid] = filter;
filter->type = MPEGTS_PES;
filter->pid = pid;
filter->last_cc = -1;
pes = &filter->u.pes_filter;
pes->pes_cb = pes_cb;
pes->opaque = opaque;
return filter;
}
Stefan Huehner
committed
static void mpegts_close_filter(MpegTSContext *ts, MpegTSFilter *filter)
Fabrice Bellard
committed
{
int pid;
pid = filter->pid;
if (filter->type == MPEGTS_SECTION)
av_freep(&filter->u.section_filter.section_buf);
PESContext *pes = filter->u.pes_filter.opaque;
av_freep(&pes->buffer);
/* referenced private data will be freed later in
* av_close_input_stream */
if (!((PESContext *)filter->u.pes_filter.opaque)->st) {
av_freep(&filter->u.pes_filter.opaque);
}
}
Fabrice Bellard
committed
av_free(filter);
ts->pids[pid] = NULL;
}
static int analyze(const uint8_t *buf, int size, int packet_size, int *index){
int stat[packet_size];
int i;
int x=0;
int best_score=0;
memset(stat, 0, packet_size*sizeof(int));
for(x=i=0; i<size-3; i++){
if(buf[i] == 0x47 && !(buf[i+1] & 0x80) && (buf[i+3] & 0x30)){
stat[x]++;
if(stat[x] > best_score){
best_score= stat[x];
if(index) *index= x;
}
}
x++;
if(x == packet_size) x= 0;
}
return best_score;
}
/* autodetect fec presence. Must have at least 1024 bytes */
Fabrice Bellard
committed
static int get_packet_size(const uint8_t *buf, int size)
if (size < (TS_FEC_PACKET_SIZE * 5 + 1))
return -1;
score = analyze(buf, size, TS_PACKET_SIZE, NULL);
dvhs_score = analyze(buf, size, TS_DVHS_PACKET_SIZE, NULL);
fec_score= analyze(buf, size, 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);
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;
Fabrice Bellard
committed
typedef struct SectionHeader {
uint8_t tid;
uint16_t id;
uint8_t version;
uint8_t sec_num;
uint8_t last_sec_num;
} SectionHeader;
static inline int get8(const uint8_t **pp, const uint8_t *p_end)
Fabrice Bellard
committed
const uint8_t *p;
int c;
p = *pp;
if (p >= p_end)
return -1;
c = *p++;
*pp = p;
return c;
Fabrice Bellard
committed
static inline int get16(const uint8_t **pp, const uint8_t *p_end)
{
const uint8_t *p;
int c;
p = *pp;
if ((p + 1) >= p_end)
return -1;
Fabrice Bellard
committed
p += 2;
*pp = p;
return c;
}
/* read and allocate a DVB string preceeded by its length */
static char *getstr8(const uint8_t **pp, const uint8_t *p_end)
Fabrice Bellard
committed
const uint8_t *p;
char *str;
Fabrice Bellard
committed
p = *pp;
len = get8(&p, p_end);
if (len < 0)
return NULL;
if ((p + len) > p_end)
return NULL;
str = av_malloc(len + 1);
if (!str)
return NULL;
memcpy(str, p, len);
str[len] = '\0';
p += len;
*pp = p;
return str;
}
static int parse_section_header(SectionHeader *h,
Fabrice Bellard
committed
const uint8_t **pp, const uint8_t *p_end)
{
int val;
val = get8(pp, p_end);
if (val < 0)
return -1;
h->tid = val;
*pp += 2;
val = get16(pp, p_end);
if (val < 0)
return -1;
h->id = val;
val = get8(pp, p_end);
if (val < 0)
return -1;
h->version = (val >> 1) & 0x1f;
val = get8(pp, p_end);
if (val < 0)
return -1;
h->sec_num = val;
val = get8(pp, p_end);
if (val < 0)
return -1;
h->last_sec_num = val;
Fabrice Bellard
committed
}
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
typedef struct {
uint32_t stream_type;
enum CodecType 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 },
{ 0 },
};
static const StreamType HDMV_types[] = {
{ 0x81, CODEC_TYPE_AUDIO, CODEC_ID_AC3 },
{ 0x82, CODEC_TYPE_AUDIO, CODEC_ID_DTS },
{ 0 },
};
/* ATSC ? */
static const StreamType MISC_types[] = {
{ 0x81, CODEC_TYPE_AUDIO, CODEC_ID_AC3 },
{ 0x8a, CODEC_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 },
{ 0 },
};
/* descriptor present */
static const StreamType DESC_types[] = {
{ 0x6a, CODEC_TYPE_AUDIO, CODEC_ID_AC3 }, /* AC-3 descriptor */
{ 0x7a, CODEC_TYPE_AUDIO, CODEC_ID_AC3 },
{ 0x7b, CODEC_TYPE_AUDIO, CODEC_ID_DTS },
{ 0x59, CODEC_TYPE_SUBTITLE, CODEC_ID_DVB_SUBTITLE }, /* subtitling descriptor */
};
static void mpegts_find_stream_type(AVStream *st,
uint32_t stream_type, const StreamType *types)
{
for (; types->stream_type; types++) {
if (stream_type == types->stream_type) {
st->codec->codec_type = types->codec_type;
st->codec->codec_id = types->codec_id;
return;
}
}
}
static AVStream *new_pes_av_stream(PESContext *pes, uint32_t prog_reg_desc, uint32_t code)
{
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_id = CODEC_ID_PROBE;
st->need_parsing = AVSTREAM_PARSE_FULL;
pes->st = st;
dprintf(pes->stream, "stream_type=%x pid=%x prog_reg_desc=%.4s\n",
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_PROBE)
mpegts_find_stream_type(st, pes->stream_type, HDMV_types);
if (st->codec->codec_id == CODEC_ID_PROBE)
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_PROBE) {
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 st;
}
Fabrice Bellard
committed
static void pmt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len)
Fabrice Bellard
committed
{
MpegTSContext *ts = filter->u.section_filter.opaque;
Fabrice Bellard
committed
SectionHeader h1, *h = &h1;
PESContext *pes;
AVStream *st;
const uint8_t *p, *p_end, *desc_list_end, *desc_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];
uint32_t prog_reg_desc = 0; /* registration descriptor */
#ifdef DEBUG
dprintf(ts->stream, "PMT: len %i\n", section_len);
Panagiotis Issaris
committed
av_hex_dump_log(ts->stream, AV_LOG_DEBUG, (uint8_t *)section, section_len);
Fabrice Bellard
committed
#endif
Fabrice Bellard
committed
p_end = section + section_len - 4;
p = section;
if (parse_section_header(h, &p, p_end) < 0)
return;
dprintf(ts->stream, "sid=0x%x sec_num=%d/%d\n",
Daniel Kristjansson
committed
h->id, h->sec_num, h->last_sec_num);
Michael Niedermayer
committed
if (h->tid != PMT_TID)
Fabrice Bellard
committed
return;
Nico Sabbi
committed
clear_program(ts, h->id);
Fabrice Bellard
committed
pcr_pid = get16(&p, p_end) & 0x1fff;
if (pcr_pid < 0)
return;
Nico Sabbi
committed
add_pid_to_pmt(ts, h->id, pcr_pid);
dprintf(ts->stream, "pcr_pid=0x%x\n", pcr_pid);
Fabrice Bellard
committed
program_info_length = get16(&p, p_end) & 0xfff;
if (program_info_length < 0)
return;
while(program_info_length >= 2) {
uint8_t tag, len;
tag = get8(&p, p_end);
len = get8(&p, p_end);
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
prog_reg_desc = bytestream_get_le32(&p);
len -= 4;
}
p += len;
}
Fabrice Bellard
committed
p += program_info_length;
if (p >= p_end)
return;
for(;;) {
Fabrice Bellard
committed
stream_type = get8(&p, p_end);
if (stream_type < 0)
break;
pid = get16(&p, p_end) & 0x1fff;
if (pid < 0)
break;
/* now create ffmpeg stream */
if (ts->pids[pid] && ts->pids[pid]->type == MPEGTS_PES) {
pes = ts->pids[pid]->u.pes_filter.opaque;
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);
if (pes)
st = new_pes_av_stream(pes, prog_reg_desc, 0);
}
if (!st)
return;
add_pid_to_pmt(ts, h->id, pid);
av_program_add_stream_index(ts->stream, h->id, st->index);
desc_list_len = get16(&p, p_end) & 0xfff;
if (desc_list_len < 0)
break;
desc_list_end = p + desc_list_len;
if (desc_list_end > p_end)
Fabrice Bellard
committed
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)
break;
dprintf(ts->stream, "tag: 0x%02x len=%d\n",
Daniel Kristjansson
committed
desc_tag, desc_len);
if (st->codec->codec_id == CODEC_ID_PROBE &&
stream_type == STREAM_TYPE_PRIVATE_DATA)
mpegts_find_stream_type(st, desc_tag, DESC_types);
switch(desc_tag) {
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);
st->codec->codec_tag = bytestream_get_le32(&p);
if (st->codec->codec_id == CODEC_ID_PROBE &&
stream_type == STREAM_TYPE_PRIVATE_DATA)
mpegts_find_stream_type(st, st->codec->codec_tag, REGD_types);
break;
default:
break;
}
p = desc_end;
}
p = desc_list_end;
Fabrice Bellard
committed
}
/* all parameters are there */
Michael Niedermayer
committed
ts->stop_parse++;
mpegts_close_filter(ts, filter);
Fabrice Bellard
committed
}
static void pat_cb(MpegTSFilter *filter, const uint8_t *section, int section_len)
Fabrice Bellard
committed
{
MpegTSContext *ts = filter->u.section_filter.opaque;
Fabrice Bellard
committed
SectionHeader h1, *h = &h1;
const uint8_t *p, *p_end;
int sid, pmt_pid;
#ifdef DEBUG
dprintf(ts->stream, "PAT:\n");
Panagiotis Issaris
committed
av_hex_dump_log(ts->stream, AV_LOG_DEBUG, (uint8_t *)section, section_len);
Fabrice Bellard
committed
#endif
p_end = section + section_len - 4;
p = section;
if (parse_section_header(h, &p, p_end) < 0)
return;
if (h->tid != PAT_TID)
return;
Nico Sabbi
committed
clear_programs(ts);
Fabrice Bellard
committed
for(;;) {
sid = get16(&p, p_end);
if (sid < 0)
break;
pmt_pid = get16(&p, p_end) & 0x1fff;
if (pmt_pid < 0)
break;
dprintf(ts->stream, "sid=0x%x pid=0x%x\n", sid, pmt_pid);
Fabrice Bellard
committed
if (sid == 0x0000) {
/* NIT info */
} else {
Nico Sabbi
committed
av_new_program(ts->stream, sid);
Michael Niedermayer
committed
ts->stop_parse--;
mpegts_open_section_filter(ts, pmt_pid, pmt_cb, ts, 1);
Nico Sabbi
committed
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);
Fabrice Bellard
committed
}
}
/* not found */
Michael Niedermayer
committed
ts->stop_parse++;
Fabrice Bellard
committed
mpegts_close_filter(ts, filter);
Fabrice Bellard
committed
}
static void sdt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len)
Fabrice Bellard
committed
{
MpegTSContext *ts = filter->u.section_filter.opaque;
Fabrice Bellard
committed
SectionHeader h1, *h = &h1;
const uint8_t *p, *p_end, *desc_list_end, *desc_end;
int onid, val, sid, desc_list_len, desc_tag, desc_len, service_type;
char *name, *provider_name;
#ifdef DEBUG
dprintf(ts->stream, "SDT:\n");
Panagiotis Issaris
committed
av_hex_dump_log(ts->stream, AV_LOG_DEBUG, (uint8_t *)section, section_len);
Fabrice Bellard
committed
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
#endif
p_end = section + section_len - 4;
p = section;
if (parse_section_header(h, &p, p_end) < 0)
return;
if (h->tid != SDT_TID)
return;
onid = get16(&p, p_end);
if (onid < 0)
return;
val = get8(&p, p_end);
if (val < 0)
return;
for(;;) {
sid = get16(&p, p_end);
if (sid < 0)
break;
val = get8(&p, p_end);
if (val < 0)
break;
desc_list_len = get16(&p, p_end) & 0xfff;
if (desc_list_len < 0)
break;
desc_list_end = p + desc_list_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);
desc_end = p + desc_len;
if (desc_end > desc_list_end)
break;
dprintf(ts->stream, "tag: 0x%02x len=%d\n",
Daniel Kristjansson
committed
desc_tag, desc_len);
Fabrice Bellard
committed
switch(desc_tag) {
case 0x48:
service_type = get8(&p, p_end);
if (service_type < 0)
break;
provider_name = getstr8(&p, p_end);
if (!provider_name)
break;
name = getstr8(&p, p_end);
Nico Sabbi
committed
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);
}
Nico Sabbi
committed
}
Fabrice Bellard
committed
break;
default:
break;
}
p = desc_end;
}
p = desc_list_end;
}
}
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;
Fabrice Bellard
committed
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;
Fabrice Bellard
committed
MpegTSContext *ts = pes->ts;
Fabrice Bellard
committed
const uint8_t *p;
int len, code;
if(!ts->pkt)
if (is_start) {
if (pes->state == MPEGTS_PAYLOAD && pes->data_index > 0) {
new_pes_packet(pes, ts->pkt);
ts->stop_parse = 1;
}
Fabrice Bellard
committed
pes->state = MPEGTS_HEADER;
pes->data_index = 0;
pes->ts_packet_pos = pos;
}
p = buf;
while (buf_size > 0) {
Fabrice Bellard
committed
switch(pes->state) {
case MPEGTS_HEADER:
Fabrice Bellard
committed
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;
Fabrice Bellard
committed
if (pes->data_index == PES_START_SIZE) {
/* we got all the PES or section header. We can now
decide */
#if 0
Panagiotis Issaris
committed
av_hex_dump_log(pes->stream, AV_LOG_DEBUG, pes->header, pes->data_index);
Fabrice Bellard
committed
if (pes->header[0] == 0x00 && pes->header[1] == 0x00 &&
pes->header[2] == 0x01) {
/* it must be an mpeg2 PES stream */
Fabrice Bellard
committed
code = pes->header[3] | 0x100;
/* stream not present in PMT */
if (!pes->st)
pes->st = new_pes_av_stream(pes, 0, code);
if (!pes->st)
return AVERROR(ENOMEM);
if (pes->st->discard == AVDISCARD_ALL ||
Baptiste Coudurier
committed
!((code >= 0x1c0 && code <= 0x1df) ||
Isaac Richards
committed
(code >= 0x1e0 && code <= 0x1ef) ||
Fabrice Bellard
committed
pes->state = MPEGTS_PESHEADER_FILL;
pes->total_size = AV_RB16(pes->header + 4);
Fabrice Bellard
committed
/* NOTE: a zero total size means the PES size is
unbounded */
pes->pes_header_size = pes->header[8] + 9;
} else {
/* otherwise, it should be a table */
/* skip packet */
skip:
Fabrice Bellard
committed
pes->state = MPEGTS_SKIP;
continue;
}
}
break;
/**********************************************/
/* PES packing parsing */
case MPEGTS_PESHEADER_FILL:
Fabrice Bellard
committed
len = pes->pes_header_size - pes->data_index;
Fabrice Bellard
committed
if (len > buf_size)
len = buf_size;
memcpy(pes->header + pes->data_index, p, len);
pes->data_index += len;
p += len;
buf_size -= len;
Fabrice Bellard
committed
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);
Fabrice Bellard
committed
r += 5;
} else if ((flags & 0xc0) == 0xc0) {
pes->pts = get_pts(r);
r += 5;
pes->dts = get_pts(r);
r += 5;
}
if (pes->total_size > pes->data_index - 6)
pes->total_size -= pes->data_index - 6;
else
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);
Fabrice Bellard
committed
/* we got the full header. We parse it and get the payload */
pes->state = MPEGTS_PAYLOAD;
Fabrice Bellard
committed
}
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);