Newer
Older
/*
* MPEG2 transport stream (aka DVB) demux
Fabrice Bellard
committed
* Copyright (c) 2002-2003 Fabrice Bellard.
* This library 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
* version 2 of the License, or (at your option) any later version.
* This library 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
* License along with this library; 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"
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;
int sid;
char *provider_name;
char *name;
} MpegTSService;
Fabrice Bellard
committed
struct MpegTSContext {
Fabrice Bellard
committed
/* user data */
AVFormatContext *stream;
int raw_packet_size; /* raw packet size, including FEC if present */
Fabrice Bellard
committed
int auto_guess; /* if true, all pids are analized to find streams */
int set_service_ret;
Fabrice Bellard
committed
int mpeg2ts_raw; /* force raw MPEG2 transport stream output, if possible */
int mpeg2ts_compute_pcr; /* compute exact PCR for each transport stream packet */
/* used to estimate the exact PCR */
int64_t cur_pcr;
int pcr_incr;
Fabrice Bellard
committed
/* data needed to handle file based ts */
int stop_parse; /* stop parsing loop */
AVPacket *pkt; /* packet containing av data */
/******************************************/
/* private mpegts data */
/* scan context */
MpegTSFilter *sdt_filter;
int nb_services;
MpegTSService **services;
Fabrice Bellard
committed
/* set service context (XXX: allocated it ?) */
SetServiceCallback *set_service_cb;
void *set_service_opaque;
MpegTSFilter *pat_filter;
MpegTSFilter *pmt_filter;
int req_sid;
MpegTSFilter *pids[NB_PID_MAX];
Fabrice Bellard
committed
};
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)
Wolfram Gloger
committed
tss->section_cb(tss->opaque, tss->section_buf, tss->section_h_size);
Fabrice Bellard
committed
}
}
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
printf("Filter: pid=0x%x\n", pid);
#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;
}
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;
}
void mpegts_close_filter(MpegTSContext *ts, MpegTSFilter *filter)
{
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;
#ifdef DEBUG_SI
printf("new_service: sid=0x%04x provider='%s' name='%s'\n",
Fabrice Bellard
committed
sid, provider_name, name);
#endif
service = av_mallocz(sizeof(MpegTSService));
if (!service)
return NULL;
service->sid = sid;
service->provider_name = provider_name;
service->name = name;
dynarray_add(&ts->services, &ts->nb_services, service);
return service;
}
static void pmt_cb(void *opaque, const uint8_t *section, int section_len)
{
MpegTSContext *ts = opaque;
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];
Fabrice Bellard
committed
#ifdef DEBUG_SI
printf("PMT:\n");
Wolfram Gloger
committed
av_hex_dump(stdout, (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
printf("sid=0x%x sec_num=%d/%d\n", h->id, h->sec_num, h->last_sec_num);
#endif
Michael Niedermayer
committed
if (h->tid != PMT_TID || (ts->req_sid >= 0 && h->id != ts->req_sid) )
Fabrice Bellard
committed
return;
pcr_pid = get16(&p, p_end) & 0x1fff;
if (pcr_pid < 0)
return;
Fabrice Bellard
committed
#ifdef DEBUG_SI
printf("pcr_pid=0x%x\n", pcr_pid);
#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 &&
((desc_tag == 0x6A) || (desc_tag == 0x7A))) {
/*assume DVB AC-3 Audio*/
stream_type = STREAM_TYPE_AUDIO_AC3;
}
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
desc_len = get8(&p, desc_list_end);
desc_end = p + desc_len;
if (desc_end > desc_list_end)
break;
#ifdef DEBUG_SI
printf("tag: 0x%02x len=%d\n", 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;
default:
break;
}
p = desc_end;
}
p = desc_list_end;
Fabrice Bellard
committed
#ifdef DEBUG_SI
printf("stream_type=%d pid=0x%x\n", stream_type, pid);
#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:
pes = add_pes_stream(ts, pid, stream_type);
if (pes)
st = new_pes_av_stream(pes, 0);
Fabrice Bellard
committed
break;
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 */
ts->set_service_cb(ts->set_service_opaque, 0);
mpegts_close_filter(ts, ts->pmt_filter);
ts->pmt_filter = NULL;
}
static void pat_cb(void *opaque, const uint8_t *section, int section_len)
{
MpegTSContext *ts = opaque;
SectionHeader h1, *h = &h1;
const uint8_t *p, *p_end;
int sid, pmt_pid;
#ifdef DEBUG_SI
printf("PAT:\n");
Wolfram Gloger
committed
av_hex_dump(stdout, (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
printf("sid=0x%x pid=0x%x\n", sid, pmt_pid);
#endif
if (sid == 0x0000) {
/* NIT info */
} else {
Fabrice Bellard
committed
if (ts->req_sid == sid) {
ts->pmt_filter = mpegts_open_section_filter(ts, pmt_pid,
Fabrice Bellard
committed
pmt_cb, ts, 1);
goto found;
}
}
}
/* not found */
ts->set_service_cb(ts->set_service_opaque, -1);
found:
mpegts_close_filter(ts, ts->pat_filter);
ts->pat_filter = NULL;
}
Fabrice Bellard
committed
/* add all services found in the PAT */
static void pat_scan_cb(void *opaque, const uint8_t *section, int section_len)
{
MpegTSContext *ts = opaque;
SectionHeader h1, *h = &h1;
const uint8_t *p, *p_end;
int sid, pmt_pid;
char *provider_name, *name;
char buf[256];
#ifdef DEBUG_SI
printf("PAT:\n");
Wolfram Gloger
committed
av_hex_dump(stdout, (uint8_t *)section, section_len);
Fabrice Bellard
committed
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
#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
printf("sid=0x%x pid=0x%x\n", sid, pmt_pid);
#endif
if (sid == 0x0000) {
/* NIT info */
} else {
/* add the service with a dummy name */
snprintf(buf, sizeof(buf), "Service %x\n", sid);
name = av_strdup(buf);
provider_name = av_strdup("");
if (name && provider_name) {
new_service(ts, sid, provider_name, name);
} else {
av_freep(&name);
av_freep(&provider_name);
}
}
}
ts->stop_parse = 1;
/* remove filter */
mpegts_close_filter(ts, ts->pat_filter);
ts->pat_filter = NULL;
}
Fabrice Bellard
committed
void mpegts_set_service(MpegTSContext *ts, int sid,
SetServiceCallback *set_service_cb, void *opaque)
{
ts->set_service_cb = set_service_cb;
ts->set_service_opaque = opaque;
ts->req_sid = sid;
ts->pat_filter = mpegts_open_section_filter(ts, PAT_PID,
Fabrice Bellard
committed
pat_cb, ts, 1);
}
static void sdt_cb(void *opaque, const uint8_t *section, int section_len)
{
MpegTSContext *ts = opaque;
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
printf("SDT:\n");
Wolfram Gloger
committed
av_hex_dump(stdout, (uint8_t *)section, section_len);
Fabrice Bellard
committed
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
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
#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
printf("tag: 0x%02x len=%d\n", desc_tag, desc_len);
#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;
}
ts->stop_parse = 1;
/* remove filter */
mpegts_close_filter(ts, ts->sdt_filter);
ts->sdt_filter = NULL;
}
Fabrice Bellard
committed
/* scan services in a transport stream by looking at the SDT */
Fabrice Bellard
committed
void mpegts_scan_sdt(MpegTSContext *ts)
{
ts->sdt_filter = mpegts_open_section_filter(ts, SDT_PID,
Fabrice Bellard
committed
sdt_cb, ts, 1);
}
Fabrice Bellard
committed
/* scan services in a transport stream by looking at the PAT (better
than nothing !) */
void mpegts_scan_pat(MpegTSContext *ts)
{
ts->pat_filter = mpegts_open_section_filter(ts, PAT_PID,
Fabrice Bellard
committed
pat_scan_cb, ts, 1);
}
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 {
Fabrice Bellard
committed
int pid;
int stream_type;
Fabrice Bellard
committed
MpegTSContext *ts;
Fabrice Bellard
committed
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];
Fabrice Bellard
committed
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 */
Fabrice Bellard
committed
static void mpegts_push_data(void *opaque,
const uint8_t *buf, int buf_size, int is_start)
Fabrice Bellard
committed
PESContext *pes = opaque;
Fabrice Bellard
committed
MpegTSContext *ts = pes->ts;
Fabrice Bellard
committed
const uint8_t *p;
int len, code;
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
Fabrice Bellard
committed
av_hex_dump(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) ||
(code == 0x1bd)))
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
}
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
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_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;
st->need_parsing = 1;
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) {