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 "avformat.h"
Fabrice Bellard
committed
#include "mpegts.h"
Michael Niedermayer
committed
#include "allformats.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
Fabrice Bellard
committed
typedef struct PESContext PESContext;
static PESContext* add_pes_stream(MpegTSContext *ts, int pid, int stream_type);
static AVStream* new_pes_av_stream(PESContext *pes, uint32_t code);
Fabrice Bellard
committed
enum MpegTSFilterType {
MPEGTS_PES,
MPEGTS_SECTION,
Fabrice Bellard
committed
typedef void PESCallback(void *opaque, const uint8_t *buf, int len, int is_start);
Fabrice Bellard
committed
typedef struct MpegTSPESFilter {
PESCallback *pes_cb;
void *opaque;
} MpegTSPESFilter;
typedef void SectionCallback(void *opaque, const uint8_t *buf, int len);
typedef void SetServiceCallback(void *opaque, int ret);
typedef struct MpegTSSectionFilter {
int section_index;
int section_h_size;
uint8_t *section_buf;
int check_crc:1;
int end_of_section_reached:1;
SectionCallback *section_cb;
void *opaque;
} MpegTSSectionFilter;
typedef 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;
} MpegTSFilter;
typedef struct MpegTSService {
int running:1;
Daniel Kristjansson
committed
int sid; /**< MPEG Program Number of stream */
char *provider_name; /**< DVB Network name, "" if not DVB stream */
char *name; /**< DVB Service name, "MPEG Program [sid]" if not DVB stream*/
Fabrice Bellard
committed
} MpegTSService;
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;
/** 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 */
int pcr_pid; /**< 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 */
Daniel Kristjansson
committed
/** number of PMTs in the last PAT seen */
Fabrice Bellard
committed
int nb_services;
Daniel Kristjansson
committed
/** list of PMTs in the last PAT seen */
Fabrice Bellard
committed
MpegTSService **services;
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
};
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
/* 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;
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];
};
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 = (((tss->section_buf[1] & 0xf) << 8) | tss->section_buf[2]) + 3;
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_crc04C11DB7, -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);
else if (filter->type == MPEGTS_PES)
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; i++){
if(buf[i] == 0x47){
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;
c = (p[0] << 8) | p[1];
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 MpegTSService *new_service(MpegTSContext *ts, int sid,
Fabrice Bellard
committed
char *provider_name, char *name)
{
MpegTSService *service=NULL;
int i;
Fabrice Bellard
committed
#ifdef DEBUG_SI
av_log(ts->stream, AV_LOG_DEBUG, "new_service: "
Daniel Kristjansson
committed
"sid=0x%04x provider='%s' name='%s'\n",
Fabrice Bellard
committed
sid, provider_name, name);
#endif
for(i=0; i<ts->nb_services; i++)
if(ts->services[i]->sid == sid)
service= ts->services[i];
if(!service){
service = av_mallocz(sizeof(MpegTSService));
if (!service)
return NULL;
dynarray_add(&ts->services, &ts->nb_services, service);
}
Fabrice Bellard
committed
service->sid = sid;
assert((!provider_name) == (!name));
if(name){
av_free(service->provider_name);
av_free(service-> name);
service->provider_name = provider_name;
service-> name = name;
}
Fabrice Bellard
committed
return service;
}
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 */
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;
pcr_pid = get16(&p, p_end) & 0x1fff;
if (pcr_pid < 0)
return;
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;
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);
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
#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;
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_SUBTITLE_DVB:
Michael Niedermayer
committed
if(ts->pids[pid]){
assert(ts->pids[pid].type == MPEGTS_PES);
pes= ts->pids[pid]->u.pes_filter.opaque;
st= pes->st;
}else{
pes = add_pes_stream(ts, pid, stream_type);
if (pes)
st = new_pes_av_stream(pes, 0);
Michael Niedermayer
committed
}
Fabrice Bellard
committed
default:
/* we ignore the other streams */
break;
}
if (st) {
if (language[0] != 0) {
st->language[0] = language[0];
st->language[1] = language[1];
st->language[2] = language[2];
st->language[3] = language[3];
}
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;
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 {
new_service(ts, sid, NULL, NULL);
Michael Niedermayer
committed
ts->stop_parse--;
mpegts_open_section_filter(ts, pmt_pid, pmt_cb, ts, 1);
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
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
#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);
if (!name)
break;
new_service(ts, sid, provider_name, name);
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;
int val;
pts = (int64_t)((p[0] >> 1) & 0x07) << 30;
val = (p[1] << 8) | p[2];
pts |= (int64_t)(val >> 1) << 15;
val = (p[3] << 8) | p[4];
pts |= (int64_t)(val >> 1);
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 = (pes->header[4] << 8) | pes->header[5];
/* 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;
int codec_type, codec_id;
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;
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
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:
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_MPEG1VIDEO;
}
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;
pes->st = st;
}
return st;
}
static PESContext *add_pes_stream(MpegTSContext *ts, int pid, int stream_type)
Fabrice Bellard
committed
{
MpegTSFilter *tss;
PESContext *pes;
/* if no pid found, then add a pid context */
pes = av_mallocz(sizeof(PESContext));
if (!pes)
return 0;
Fabrice Bellard
committed
pes->ts = ts;
pes->stream = ts->stream;
Fabrice Bellard
committed
pes->pid = pid;
pes->stream_type = stream_type;
Fabrice Bellard
committed
tss = mpegts_open_pes_filter(ts, pid, mpegts_push_data, pes);
if (!tss) {
av_free(pes);
return 0;
Fabrice Bellard
committed
}
return pes;
Fabrice Bellard
committed
/* handle one TS packet */
Fabrice Bellard
committed
static void handle_packet(MpegTSContext *ts, const uint8_t *packet)
Fabrice Bellard
committed
AVFormatContext *s = ts->stream;
Fabrice Bellard
committed
MpegTSFilter *tss;
int len, pid, cc, cc_ok, afc, is_start;
const uint8_t *p, *p_end;
pid = ((packet[1] & 0x1f) << 8) | packet[2];
is_start = packet[1] & 0x40;
tss = ts->pids[pid];
if (ts->auto_guess && tss == NULL && is_start) {
add_pes_stream(ts, pid, 0);
Fabrice Bellard
committed
tss = ts->pids[pid];
}
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,
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;