Newer
Older
* MPEG2 transport stream (aka DVB) demuxer
Fabrice Bellard
committed
* 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
#include "libavutil/crc.h"
#include "avformat.h"
Fabrice Bellard
committed
#include "mpegts.h"
Fabrice Bellard
committed
//#define DEBUG_SI
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
#define REGISTRATION_DESCRIPTOR 5
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);
static AVStream* new_pes_av_stream(PESContext *pes, uint32_t code);
Fabrice Bellard
committed
enum MpegTSFilterType {
MPEGTS_PES,
MPEGTS_SECTION,
typedef struct MpegTSFilter MpegTSFilter;
typedef void PESCallback(MpegTSFilter *f, const uint8_t *buf, int len, int is_start);
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
typedef struct {
unsigned int id; //program id/service id
unsigned int nb_pids;
unsigned int pids[MAX_PIDS_PER_PROGRAM];
} Program_t;
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;
Fabrice Bellard
committed
/******************************************/
/* private mpegts data */
/* scan context */
Nico Sabbi
committed
/** structure to keep track of Program->pids mapping */
unsigned int nb_prg;
Program_t *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;
uint8_t header[MAX_PES_HEADER_SIZE];
};
extern AVInputFormat mpegts_demuxer;
Nico Sabbi
committed
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
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
218
219
220
221
222
223
224
225
226
227
228
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)
{
Program_t *p;
void *tmp = av_realloc(ts->prg, (ts->nb_prg+1)*sizeof(Program_t));
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;
Program_t *p = NULL;
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;
Program_t *p;
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;
Fabrice Bellard
committed
#ifdef DEBUG_SI
av_log(ts->stream, AV_LOG_DEBUG, "Filter: pid=0x%x\n", pid);
Fabrice Bellard
committed
#endif
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);
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
}
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;
Måns Rullgård
committed
int comp_page = 0, anc_page = 0; /* initialize to kill warnings */
char language[4] = {0}; /* initialize to kill warnings */
int has_hdmv_descr = 0;
Fabrice Bellard
committed
#ifdef DEBUG_SI
av_log(ts->stream, AV_LOG_DEBUG, "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
p_end = section + section_len - 4;
p = section;
if (parse_section_header(h, &p, p_end) < 0)
return;
#ifdef DEBUG_SI
av_log(ts->stream, AV_LOG_DEBUG, "sid=0x%x sec_num=%d/%d\n",
Daniel Kristjansson
committed
h->id, h->sec_num, h->last_sec_num);
Fabrice Bellard
committed
#endif
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);
Fabrice Bellard
committed
#ifdef DEBUG_SI
av_log(ts->stream, AV_LOG_DEBUG, "pcr_pid=0x%x\n", pcr_pid);
Fabrice Bellard
committed
#endif
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 == REGISTRATION_DESCRIPTOR && len >= 4) {
uint8_t bytes[4];
bytes[0] = get8(&p, p_end);
bytes[1] = get8(&p, p_end);
bytes[2] = get8(&p, p_end);
bytes[3] = get8(&p, p_end);
len -= 4;
if(bytes[0] == 'H' && bytes[1] == 'D' &&
bytes[2] == 'M' && bytes[3] == 'V')
has_hdmv_descr = 1;
}
p += len;
}
Fabrice Bellard
committed
p += program_info_length;
if (p >= p_end)
return;
for(;;) {
language[0] = 0;
st = 0;
Fabrice Bellard
committed
stream_type = get8(&p, p_end);
if (stream_type < 0)
break;
pid = get16(&p, p_end) & 0x1fff;
if (pid < 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)
Fabrice Bellard
committed
break;
for(;;) {
desc_tag = get8(&p, desc_list_end);
if (desc_tag < 0)
break;
if (stream_type == STREAM_TYPE_PRIVATE_DATA) {
if((desc_tag == 0x6A) || (desc_tag == 0x7A)) {
/*assume DVB AC-3 Audio*/
stream_type = STREAM_TYPE_AUDIO_AC3;
} else if(desc_tag == 0x7B) {
/* DVB DTS audio */
stream_type = STREAM_TYPE_AUDIO_DTS;
}
desc_len = get8(&p, desc_list_end);
desc_end = p + desc_len;
if (desc_end > desc_list_end)
break;
#ifdef DEBUG_SI
av_log(ts->stream, AV_LOG_DEBUG, "tag: 0x%02x len=%d\n",
Daniel Kristjansson
committed
desc_tag, desc_len);
#endif
switch(desc_tag) {
case DVB_SUBT_DESCID:
if (stream_type == STREAM_TYPE_PRIVATE_DATA)
stream_type = STREAM_TYPE_SUBTITLE_DVB;
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);
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;
break;
case REGISTRATION_DESCRIPTOR: /*MPEG-2 Registration descriptor */
{
uint8_t bytes[4];
bytes[0] = get8(&p, desc_end);
bytes[1] = get8(&p, desc_end);
bytes[2] = get8(&p, desc_end);
bytes[3] = get8(&p, desc_end);
if(bytes[0] == 'd' && bytes[1] == 'r' &&
bytes[2] == 'a' && bytes[3] == 'c')
has_dirac_descr = 1;
break;
}
default:
break;
}
p = desc_end;
}
p = desc_list_end;
Fabrice Bellard
committed
#ifdef DEBUG_SI
av_log(ts->stream, AV_LOG_DEBUG, "stream_type=%d pid=0x%x\n",
Daniel Kristjansson
committed
stream_type, pid);
Fabrice Bellard
committed
#endif
/* now create ffmpeg stream */
switch(stream_type) {
Michael Niedermayer
committed
case STREAM_TYPE_AUDIO_MPEG1:
case STREAM_TYPE_AUDIO_MPEG2:
case STREAM_TYPE_VIDEO_MPEG1:
case STREAM_TYPE_VIDEO_MPEG2:
case STREAM_TYPE_VIDEO_MPEG4:
case STREAM_TYPE_VIDEO_H264:
case STREAM_TYPE_AUDIO_AAC:
Isaac Richards
committed
case STREAM_TYPE_AUDIO_AC3:
case STREAM_TYPE_AUDIO_DTS:
case STREAM_TYPE_AUDIO_HDMV_DTS:
case STREAM_TYPE_SUBTITLE_DVB:
if((stream_type == STREAM_TYPE_AUDIO_HDMV_DTS && !has_hdmv_descr)
|| (stream_type == STREAM_TYPE_VIDEO_DIRAC && !has_dirac_descr))
if(ts->pids[pid] && ts->pids[pid]->type == MPEGTS_PES){
Michael Niedermayer
committed
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
Michael Niedermayer
committed
pes = add_pes_stream(ts, pid, pcr_pid, stream_type);
Michael Niedermayer
committed
}
Nico Sabbi
committed
add_pid_to_pmt(ts, h->id, pid);
if(st)
av_program_add_stream_index(ts->stream, h->id, st->index);
Fabrice Bellard
committed
default:
/* we ignore the other streams */
break;
}
if (st) {
if (language[0] != 0) {
}
if (stream_type == STREAM_TYPE_SUBTITLE_DVB) {
Michael Niedermayer
committed
st->codec->sub_id = (anc_page << 16) | comp_page;
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_SI
av_log(ts->stream, AV_LOG_DEBUG, "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;
#ifdef DEBUG_SI
av_log(ts->stream, AV_LOG_DEBUG, "sid=0x%x pid=0x%x\n", sid, pmt_pid);
Fabrice Bellard
committed
#endif
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
}
Michael Niedermayer
committed
static void mpegts_set_service(MpegTSContext *ts)
Fabrice Bellard
committed
{
mpegts_open_section_filter(ts, PAT_PID,
Fabrice Bellard
committed
pat_cb, ts, 1);
}
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_SI
av_log(ts->stream, AV_LOG_DEBUG, "SDT:\n");
Panagiotis Issaris
committed
av_hex_dump_log(ts->stream, AV_LOG_DEBUG, (uint8_t *)section, section_len);
Fabrice Bellard
committed
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
#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;
#ifdef DEBUG_SI
av_log(ts->stream, AV_LOG_DEBUG, "tag: 0x%02x len=%d\n",
Daniel Kristjansson
committed
desc_tag, desc_len);
Fabrice Bellard
committed
#endif
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_set_program_name(program, provider_name, name);
}
Fabrice Bellard
committed
break;
default:
break;
}
p = desc_end;
}
p = desc_list_end;
}
}
Fabrice Bellard
committed
/* scan services in a transport stream by looking at the SDT */
Stefan Huehner
committed
static void mpegts_scan_sdt(MpegTSContext *ts)
Fabrice Bellard
committed
{
Michael Niedermayer
committed
mpegts_open_section_filter(ts, SDT_PID,
Fabrice Bellard
committed
sdt_cb, ts, 1);
}
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;
}
/* return non zero if a packet could be constructed */
static void mpegts_push_data(MpegTSFilter *filter,
Fabrice Bellard
committed
const uint8_t *buf, int buf_size, int is_start)
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)
return;
if (is_start) {
Fabrice Bellard
committed
pes->state = MPEGTS_HEADER;
pes->data_index = 0;
}
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;
if (!((code >= 0x1c0 && code <= 0x1df) ||
Isaac Richards
committed
(code >= 0x1e0 && code <= 0x1ef) ||
Fabrice Bellard
committed
if (!pes->st) {
/* allocate stream */
new_pes_av_stream(pes, code);
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 */
if (pes->total_size)
pes->total_size += 6;
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;
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->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;
}
break;
case MPEGTS_PAYLOAD:
Fabrice Bellard
committed
if (pes->total_size) {
len = pes->total_size - pes->data_index;
if (len > buf_size)
len = buf_size;
} else {
len = buf_size;
}
Fabrice Bellard
committed
AVPacket *pkt = ts->pkt;
if (pes->st && av_new_packet(pkt, len) == 0) {
memcpy(pkt->data, p, len);
pkt->stream_index = pes->st->index;
pkt->pts = pes->pts;
pkt->dts = pes->dts;
Fabrice Bellard
committed
/* reset pts values */
pes->pts = AV_NOPTS_VALUE;
pes->dts = AV_NOPTS_VALUE;
ts->stop_parse = 1;
return;
}
}
buf_size = 0;
break;
case MPEGTS_SKIP:
buf_size = 0;
break;
}
}
Fabrice Bellard
committed
}
static AVStream* new_pes_av_stream(PESContext *pes, uint32_t code)
{
AVStream *st;
enum CodecID codec_id;
enum CodecType codec_type;
switch(pes->stream_type){
case STREAM_TYPE_AUDIO_MPEG1:
case STREAM_TYPE_AUDIO_MPEG2:
codec_type = CODEC_TYPE_AUDIO;
codec_id = CODEC_ID_MP3;
break;
case STREAM_TYPE_VIDEO_MPEG1:
case STREAM_TYPE_VIDEO_MPEG2:
codec_type = CODEC_TYPE_VIDEO;
codec_id = CODEC_ID_MPEG2VIDEO;
break;
case STREAM_TYPE_VIDEO_MPEG4:
codec_type = CODEC_TYPE_VIDEO;
codec_id = CODEC_ID_MPEG4;
break;
case STREAM_TYPE_VIDEO_H264:
codec_type = CODEC_TYPE_VIDEO;
codec_id = CODEC_ID_H264;
break;
case STREAM_TYPE_VIDEO_VC1:
codec_type = CODEC_TYPE_VIDEO;
codec_id = CODEC_ID_VC1;
break;
case STREAM_TYPE_VIDEO_DIRAC:
codec_type = CODEC_TYPE_VIDEO;
codec_id = CODEC_ID_DIRAC;
break;
case STREAM_TYPE_AUDIO_AAC:
codec_type = CODEC_TYPE_AUDIO;
codec_id = CODEC_ID_AAC;
break;
case STREAM_TYPE_AUDIO_AC3:
codec_type = CODEC_TYPE_AUDIO;
codec_id = CODEC_ID_AC3;
break;
case STREAM_TYPE_AUDIO_DTS:
case STREAM_TYPE_AUDIO_HDMV_DTS:
codec_type = CODEC_TYPE_AUDIO;
codec_id = CODEC_ID_DTS;
break;
case STREAM_TYPE_SUBTITLE_DVB:
codec_type = CODEC_TYPE_SUBTITLE;
codec_id = CODEC_ID_DVB_SUBTITLE;
break;
default:
if (code >= 0x1c0 && code <= 0x1df) {
codec_type = CODEC_TYPE_AUDIO;
codec_id = CODEC_ID_MP2;
} else if (code == 0x1bd) {
codec_type = CODEC_TYPE_AUDIO;
codec_id = CODEC_ID_AC3;
} else {
codec_type = CODEC_TYPE_VIDEO;
codec_id = CODEC_ID_PROBE;
}
break;
}
st = av_new_stream(pes->stream, pes->pid);
if (st) {
av_set_pts_info(st, 33, 1, 90000);
st->priv_data = pes;
Michael Niedermayer
committed
st->codec->codec_type = codec_type;
st->codec->codec_id = codec_id;