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
Ivan Schreter
committed
//#define USE_SYNCPOINT_SEARCH
#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"
Ivan Schreter
committed
#include "seek.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 6
#define PES_HEADER_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;
Ian Caulfield
committed
AVStream *sub_st; /**< stream for the embedded AC3 stream in HDMV TrueHD */
enum MpegTSState state;
/* used to get the format */
int data_index;
int total_size;
int pes_header_size;
Ian Caulfield
committed
int extended_stream_id;
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
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
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[TS_MAX_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
}
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[] = {
{ 0x80, CODEC_TYPE_AUDIO, CODEC_ID_PCM_BLURAY },
{ 0x81, CODEC_TYPE_AUDIO, CODEC_ID_AC3 },
{ 0x82, CODEC_TYPE_AUDIO, CODEC_ID_DTS },
Ian Caulfield
committed
{ 0x83, CODEC_TYPE_AUDIO, CODEC_ID_TRUEHD },
{ 0x84, CODEC_TYPE_AUDIO, CODEC_ID_EAC3 },
{ 0x90, CODEC_TYPE_SUBTITLE, CODEC_ID_HDMV_PGS_SUBTITLE },
{ 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_EAC3 }, /* E-AC-3 descriptor */
{ 0x7b, CODEC_TYPE_AUDIO, CODEC_ID_DTS },
{ 0x59, CODEC_TYPE_SUBTITLE, CODEC_ID_DVB_SUBTITLE }, /* subtitling descriptor */
Wolfram Gloger
committed
{ 0 },
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;
Baptiste Coudurier
committed
st->codec->codec_id = CODEC_ID_NONE;
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") &&
Ian Caulfield
committed
st->codec->codec_id == CODEC_ID_NONE) {
mpegts_find_stream_type(st, pes->stream_type, HDMV_types);
Ian Caulfield
committed
if (pes->stream_type == 0x83) {
// HDMV TrueHD streams also contain an AC3 coded version of the
// audio track - add a second stream for this
AVStream *sub_st;
// priv_data cannot be shared between streams
PESContext *sub_pes = av_malloc(sizeof(*sub_pes));
if (!sub_pes)
return NULL;
memcpy(sub_pes, pes, sizeof(*sub_pes));
sub_st = av_new_stream(pes->stream, pes->pid);
if (!sub_st) {
av_free(sub_pes);
return NULL;
}
av_set_pts_info(sub_st, 33, 1, 90000);
sub_st->priv_data = sub_pes;
sub_st->codec->codec_type = CODEC_TYPE_AUDIO;
sub_st->codec->codec_id = CODEC_ID_AC3;
sub_st->need_parsing = AVSTREAM_PARSE_FULL;
sub_pes->sub_st = pes->sub_st = sub_st;
}
}
Baptiste Coudurier
committed
if (st->codec->codec_id == CODEC_ID_NONE)
mpegts_find_stream_type(st, pes->stream_type, MISC_types);
/* stream was not present in PMT, guess based on PES start code */
Baptiste Coudurier
committed
if (st->codec->codec_id == CODEC_ID_NONE) {
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;
// stop parsing after pmt, we found header
if (!ts->stream->nb_streams)
ts->stop_parse = 1;
Fabrice Bellard
committed
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);
Baptiste Coudurier
committed
if (st->codec->codec_id == CODEC_ID_NONE &&
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);
dprintf(ts->stream, "reg_desc=%.4s\n", (char*)&st->codec->codec_tag);
Baptiste Coudurier
committed
if (st->codec->codec_id == CODEC_ID_NONE &&
mpegts_find_stream_type(st, st->codec->codec_tag, REGD_types);
break;
default:
break;
}
p = desc_end;
Ian Caulfield
committed
if (prog_reg_desc == AV_RL32("HDMV") && stream_type == 0x83 && pes->sub_st) {
av_program_add_stream_index(ts->stream, h->id, pes->sub_st->index);
pes->sub_st->codec->codec_tag = st->codec->codec_tag;
}
}
p = desc_list_end;
Fabrice Bellard
committed
}
/* all parameters are there */
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);
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
}
}
}
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
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
#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);
Ian Caulfield
committed
// Separate out the AC3 substream from an HDMV combined TrueHD/AC3 PID
if (pes->sub_st && pes->stream_type == 0x83 && pes->extended_stream_id == 0x76)
pkt->stream_index = pes->sub_st->index;
else
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;
dprintf(pes->stream, "pid=%x pes_code=%#x\n", pes->pid, code);
if ((!pes->st && pes->stream->nb_streams == MAX_STREAMS) ||
(pes->st && pes->st->discard == AVDISCARD_ALL) ||
code == 0x1be) /* padding_stream */
goto skip;
/* stream not present in PMT */
if (!pes->st)
pes->st = new_pes_av_stream(pes, 0, code);
if (!pes->st)
return AVERROR(ENOMEM);
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 = MAX_PES_PAYLOAD;
/* allocate pes buffer */
pes->buffer = av_malloc(pes->total_size+FF_INPUT_BUFFER_PADDING_SIZE);
if (!pes->buffer)
return AVERROR(ENOMEM);
if (code != 0x1bc && code != 0x1bf && /* program_stream_map, private_stream_2 */
code != 0x1f0 && code != 0x1f1 && /* ECM, EMM */
code != 0x1ff && code != 0x1f2 && /* program_stream_directory, DSMCC_stream */
code != 0x1f8) { /* ITU-T Rec. H.222.1 type E stream */
pes->state = MPEGTS_PESHEADER;
Baptiste Coudurier
committed
if (pes->st->codec->codec_id == CODEC_ID_NONE) {
dprintf(pes->stream, "pid=%x stream_type=%x probing\n",
pes->pid, pes->stream_type);