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 */
Baptiste Coudurier
committed
#define MAX_RESYNC_SIZE 65536
Fabrice Bellard
committed
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
/* 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];
/* 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)
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
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
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 },
{ 0x11, CODEC_TYPE_AUDIO, CODEC_ID_AAC }, /* LATM syntax */
{ 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 */
{ 0x56, CODEC_TYPE_SUBTITLE, CODEC_ID_DVB_TELETEXT },
{ 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 int mpegts_set_stream_info(AVStream *st, PESContext *pes,
uint32_t stream_type, uint32_t prog_reg_desc)
{
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;
pes->stream_type = stream_type;
av_log(pes->stream, AV_LOG_DEBUG,
"stream=%d stream_type=%x pid=%x prog_reg_desc=%.4s\n",
st->index, 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 AVERROR(ENOMEM);
Ian Caulfield
committed
memcpy(sub_pes, pes, sizeof(*sub_pes));
sub_st = av_new_stream(pes->stream, pes->pid);
if (!sub_st) {
av_free(sub_pes);
return AVERROR(ENOMEM);
Ian Caulfield
committed
}
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);
return 0;
Fabrice Bellard
committed
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
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
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
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;
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);
// 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;
MpegTSContext *ts = pes->ts;
const uint8_t *p;
int len, code;
if(!ts->pkt)
return 0;
if (is_start) {
if (pes->state == MPEGTS_PAYLOAD && pes->data_index > 0) {
new_pes_packet(pes, ts->pkt);
ts->stop_parse = 1;
}
pes->state = MPEGTS_HEADER;
pes->data_index = 0;
pes->ts_packet_pos = pos;
}
p = buf;
while (buf_size > 0) {
switch(pes->state) {
case MPEGTS_HEADER:
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;
if (pes->data_index == PES_START_SIZE) {
/* we got all the PES or section header. We can now
decide */
#if 0
av_hex_dump_log(pes->stream, AV_LOG_DEBUG, pes->header, pes->data_index);
#endif
if (pes->header[0] == 0x00 && pes->header[1] == 0x00 &&
pes->header[2] == 0x01) {
/* it must be an mpeg2 PES stream */
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 = av_new_stream(ts->stream, pes->pid);
if (!pes->st)
return AVERROR(ENOMEM);
mpegts_set_stream_info(pes->st, pes, 0, 0);
}
pes->total_size = AV_RB16(pes->header + 4);
/* 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;
if (pes->st->codec->codec_id == CODEC_ID_NONE) {
dprintf(pes->stream, "pid=%x stream_type=%x probing\n",
pes->pid, pes->stream_type);
pes->st->codec->codec_id = CODEC_ID_PROBE;
}
} else {
pes->state = MPEGTS_PAYLOAD;
pes->data_index = 0;
}
} else {
/* otherwise, it should be a table */
/* skip packet */
skip:
pes->state = MPEGTS_SKIP;
continue;
}
}
break;
/**********************************************/
/* PES packing parsing */
case MPEGTS_PESHEADER:
len = PES_HEADER_SIZE - pes->data_index;
if (len < 0)
return -1;
if (len > buf_size)
len = buf_size;
memcpy(pes->header + pes->data_index, p, len);
pes->data_index += len;
p += len;
buf_size -= len;
if (pes->data_index == PES_HEADER_SIZE) {
pes->pes_header_size = pes->header[8] + 9;
pes->state = MPEGTS_PESHEADER_FILL;
}
break;
case MPEGTS_PESHEADER_FILL:
len = pes->pes_header_size - pes->data_index;
if (len < 0)
return -1;
if (len > buf_size)
len = buf_size;
memcpy(pes->header + pes->data_index, p, len);
pes->data_index += len;
p += len;
buf_size -= len;
if (pes->data_index == pes->pes_header_size) {
const uint8_t *r;
unsigned int flags, pes_ext, skip;
flags = pes->header[7];
r = pes->header + 9;
pes->pts = AV_NOPTS_VALUE;
pes->dts = AV_NOPTS_VALUE;
if ((flags & 0xc0) == 0x80) {
pes->dts = 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;
}
pes->extended_stream_id = -1;
if (flags & 0x01) { /* PES extension */
pes_ext = *r++;
/* Skip PES private data, program packet sequence counter and P-STD buffer */
skip = (pes_ext >> 4) & 0xb;
skip += skip & 0x9;
r += skip;
if ((pes_ext & 0x41) == 0x01 &&
(r + 2) <= (pes->header + pes->pes_header_size)) {
/* PES extension 2 */
if ((r[0] & 0x7f) > 0 && (r[1] & 0x80) == 0)
pes->extended_stream_id = r[1];
}
}
/* we got the full header. We parse it and get the payload */
pes->state = MPEGTS_PAYLOAD;
pes->data_index = 0;
}
break;
case MPEGTS_PAYLOAD:
if (buf_size > 0 && pes->buffer) {
if (pes->data_index+buf_size > pes->total_size) {
new_pes_packet(pes, ts->pkt);
pes->total_size = MAX_PES_PAYLOAD;
pes->buffer = av_malloc(pes->total_size+FF_INPUT_BUFFER_PADDING_SIZE);
if (!pes->buffer)
return AVERROR(ENOMEM);
ts->stop_parse = 1;
}
memcpy(pes->buffer+pes->data_index, p, buf_size);
pes->data_index += buf_size;
}
buf_size = 0;
break;
case MPEGTS_SKIP:
buf_size = 0;
break;
}
}
return 0;
}
static PESContext *add_pes_stream(MpegTSContext *ts, int pid, int pcr_pid)
{
MpegTSFilter *tss;
PESContext *pes;
/* if no pid found, then add a pid context */
pes = av_mallocz(sizeof(PESContext));
if (!pes)
return 0;
pes->ts = ts;
pes->stream = ts->stream;
pes->pid = pid;
pes->pcr_pid = pcr_pid;
pes->state = MPEGTS_SKIP;
pes->pts = AV_NOPTS_VALUE;
pes->dts = AV_NOPTS_VALUE;
tss = mpegts_open_pes_filter(ts, pid, mpegts_push_data, pes);
if (!tss) {
av_free(pes);
return 0;
}
return pes;
}
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);
st = av_new_stream(pes->stream, pes->pid);
if (!pes->stream_type)
mpegts_set_stream_info(st, pes, stream_type, prog_reg_desc);
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) {
case 0x56: /* DVB teletext 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);
break;
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;