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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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;
/* 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
117
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
146
147
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;
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 ||
mpegts_crc32(tss->section_buf, tss->section_h_size) == 0)
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,
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
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
if (pid >= NB_PID_MAX || ts->pids[pid])
return NULL;
filter = av_mallocz(sizeof(MpegTSFilter));
if (!filter)
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,
PESCallback *pes_cb,
void *opaque)
{
MpegTSFilter *filter;
MpegTSPESFilter *pes;
if (pid >= NB_PID_MAX || ts->pids[pid])
return NULL;
filter = av_mallocz(sizeof(MpegTSFilter));
if (!filter)
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);
fec_score= analyze(buf, size, TS_FEC_PACKET_SIZE, NULL);
// av_log(NULL, AV_LOG_DEBUG, "score: %d, fec_score: %d \n", score, fec_score);
if (score > fec_score) return TS_PACKET_SIZE;
else if(score < fec_score) return TS_FEC_PACKET_SIZE;
else return -1;
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
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
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,
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
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
}
static MpegTSService *new_service(MpegTSContext *ts, int sid,
char *provider_name, char *name)
{
MpegTSService *service;
#ifdef DEBUG_SI
printf("new_service: sid=0x%04x provider='%s' name='%s'\n",
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;
int comp_page, anc_page;
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;
424
425
426
427
428
429
430
431
432
433
434
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
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 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) {
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) {
Fabrice Bellard
committed
ts->pmt_filter = mpegts_open_section_filter(ts, pmt_pid,
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
565
566
567
568
569
570
571
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
#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,
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
626
627
628
629
630
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
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
#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,
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,
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;
Fabrice Bellard
committed
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
}
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
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
931
932
933
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;
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;
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 */