建议大家熟读iso13818-1,碰到问题很多情况是因为没有熟悉标准。
1、源码中涉及的主要结构体
//ts文件过滤器的结构体
struct MpegTSFilter {//ts的过滤器
int pid;
int es_id;
int last_cc; /* last cc code (-1 if first packet) */
int64_t last_pcr;
enum MpegTSFilterType type;//过滤器类型,分辨PES,PCR,SECTION
union {
MpegTSPESFilter pes_filter;
MpegTSSectionFilter section_filter;
}u;
};
//section过滤器结构体
typedef struct MpegTSSectionFilter {
int section_index; //section的索引
int section_h_size; //头大小
int last_ver;
unsigned crc;
unsigned last_crc;
uint8_t *section_buf; //保存section数据
unsigned int check_crc : 1;
unsigned int end_of_section_reached : 1;
SectionCallback *section_cb; //回调函数
void *opaque; //类似于类指针的东西
} MpegTSSectionFilter;
//节目的结构体
struct Program {
unsigned int id; // program id/service id
unsigned int nb_pids;
unsigned int pids[MAX_PIDS_PER_PROGRAM];
int pmt_found; //标识pmt是否已经找到
};
struct MpegTSContext {
const AVClass *class;
/* user data */
AVFormatContext *stream;
/** raw packet size, including FEC if present */
int raw_packet_size;
int size_stat[3];
int size_stat_count;
#define SIZE_STAT_THRESHOLD 10
int64_t pos47_full;
/** 如果为真, 所有的pid将会用来去寻找流 */
int auto_guess;
/** 对于每个ts包都进行精确的计算 */
int mpeg2ts_compute_pcr;
/** 修复 dvb teletext pts */
int fix_teletext_pts;
int64_t cur_pcr; /**< used to estimate the exact PCR */
int pcr_incr; /**< used to estimate the exact PCR */
/* data needed to handle file based ts */
/** stop parsing loop */
int stop_parse;
/** packet containing Audio/Video data */
AVPacket *pkt;
/** to detect seek */
int64_t last_pos;
int skip_changes;
int skip_clear;
int scan_all_pmts;
int resync_size;
/******************************************/
/* private mpegts data */
/* scan context */
/** structure to keep track of Program->pids mapping */
unsigned int nb_prg;
struct Program *prg;
int8_t crc_validity[NB_PID_MAX];
/** filters for various streams specified by PMT + for the PAT and PMT */
MpegTSFilter *pids[NB_PID_MAX];
int current_pid;
};
typedef struct PESContext {
int pid;
int pcr_pid; //如果此值为-1,那么所有的包都将被认为包含PCR
int stream_type;
MpegTSContext *ts;
AVFormatContext *stream;
AVStream *st;
AVStream *sub_st; /**< stream for the embedded AC3 stream in HDMV TrueHD */
enum MpegTSState state;
/* used to get the format */
int data_index;
int flags; /**< 从AVPacket flags复制而来*/
int total_size;
int pes_header_size;
int extended_stream_id;
uint8_t stream_id;
int64_t pts, dts;
int64_t ts_packet_pos; /**< PES包中第一个ts包的位置 */
uint8_t header[MAX_PES_HEADER_SIZE];
AVBufferRef *buffer;
SLConfigDescr sl;
} PESContext;
typedef struct AVProgram {
int id;
int flags;
enum AVDiscard discard; ///< selects which program to discard and which to feed to the caller
unsigned int *stream_index;
unsigned int nb_stream_indexes;
AVDictionary *metadata;
int program_num;
int pmt_pid;
int pcr_pid;
int64_t start_time;
int64_t end_time;
int64_t pts_wrap_reference; ///< reference dts for wrap detection
int pts_wrap_behavior; ///< behavior on wrap detection
} AVProgram;
2、主要回调函数
//原型函数
//回调函数,获取PES
typedef int PESCallback (MpegTSFilter *f, const uint8_t *buf, int len,
int is_start, int64_t pos);
typedef struct MpegTSPESFilter {
PESCallback *pes_cb;//回调函数
void *opaque; //类指针
} MpegTSPESFilter;
//回调函数,获取Section
typedef void SectionCallback (MpegTSFilter *f, const uint8_t *buf, int len);
//回调函数,设置Service
typedef void SetServiceCallback (void *opaque, int ret);
//接收函数
static void m4sl_cb(MpegTSFilter *filter, const uint8_t *section,
int section_len)
{
MpegTSContext *ts = filter->u.section_filter.opaque;
MpegTSSectionFilter *tssf = &filter->u.section_filter;
SectionHeader h;
const uint8_t *p, *p_end;
AVIOContext pb;
int mp4_descr_count = 0;
Mp4Descr mp4_descr[MAX_MP4_DESCR_COUNT] = { { 0 } };
int i, pid;
AVFormatContext *s = ts->stream;
p_end = section + section_len - 4;
p = section;
if (parse_section_header(&h, &p, p_end) < 0)
return;
if (h.tid != M4OD_TID)
return;
if (skip_identical(&h, tssf))
return;
mp4_read_od(s, p, (unsigned) (p_end - p), mp4_descr, &mp4_descr_count,
MAX_MP4_DESCR_COUNT);
for (pid = 0; pid < NB_PID_MAX; pid++) {
if (!ts->pids[pid])
continue;
for (i = 0; i < mp4_descr_count; i++) {
PESContext *pes;
AVStream *st;
if (ts->pids[pid]->es_id != mp4_descr[i].es_id)
continue;
if (ts->pids[pid]->type != MPEGTS_PES) {
av_log(s, AV_LOG_ERROR, "pid %x is not PES\n", pid);
continue;
}
pes = ts->pids[pid]->u.pes_filter.opaque;
st = pes->st;
if (!st)
continue;
pes->sl = mp4_descr[i].sl;
ffio_init_context(&pb, mp4_descr[i].dec_config_descr,
mp4_descr[i].dec_config_descr_len, 0,
NULL, NULL, NULL, NULL);
ff_mp4_read_dec_config_descr(s, st, &pb);
if (st->codecpar->codec_id == AV_CODEC_ID_AAC &&
st->codecpar->extradata_size > 0)
st->need_parsing = 0;
if (st->codecpar->codec_id == AV_CODEC_ID_H264 &&
st->codecpar->extradata_size > 0)
st->need_parsing = 0;
st->codecpar->codec_type = avcodec_get_type(st->codecpar->codec_id);
st->internal->need_context_update = 1;
}
}
for (i = 0; i < mp4_descr_count; i++)
av_free(mp4_descr[i].dec_config_descr);
}
static void scte_data_cb(MpegTSFilter *filter, const uint8_t *section,
int section_len)
{
AVProgram *prg = NULL;
MpegTSContext *ts = filter->u.section_filter.opaque;
int idx = ff_find_stream_index(ts->stream, filter->pid);
if (idx < 0)
return;
new_data_packet(section, section_len, ts->pkt);
ts->pkt->stream_index = idx;
prg = av_find_program_from_stream(ts->stream, NULL, idx);
if (prg && prg->pcr_pid != -1 && prg->discard != AVDISCARD_ALL) {
MpegTSFilter *f = ts->pids[prg->pcr_pid];
if (f && f->last_pcr != -1)
ts->pkt->pts = ts->pkt->dts = f->last_pcr/300;
}
ts->stop_parse = 1;
}
//回调函数,接收pmt表
static void pmt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len)
{
MpegTSContext *ts = filter->u.section_filter.opaque;
MpegTSSectionFilter *tssf = &filter->u.section_filter;
SectionHeader h1, *h = &h1;
PESContext *pes;
AVStream *st;
const uint8_t *p, *p_end, *desc_list_end;
int program_info_length, pcr_pid, pid, stream_type;
int desc_list_len;
uint32_t prog_reg_desc = 0; /* registration descriptor */
int mp4_descr_count = 0;
Mp4Descr mp4_descr[MAX_MP4_DESCR_COUNT] = { { 0 } };
int i;
av_log(ts->stream, AV_LOG_TRACE, "PMT: len %i\n", section_len);
hex_dump_debug(ts->stream, section, section_len);
p_end = section + section_len - 4;
p = section;
if (parse_section_header(h, &p, p_end) < 0)
return;
if (skip_identical(h, tssf))
return;
av_log(ts->stream, AV_LOG_TRACE, "sid=0x%x sec_num=%d/%d version=%d tid=%d\n",
h->id, h->sec_num, h->last_sec_num, h->version, h->tid);
if (h->tid != PMT_TID)
return;
if (!ts->scan_all_pmts && ts->skip_changes)
return;
if (!ts->skip_clear)
clear_program(ts, h->id);
pcr_pid = get16(&p, p_end);
if (pcr_pid < 0)
return;
pcr_pid &= 0x1fff;
add_pid_to_pmt(ts, h->id, pcr_pid);
set_pcr_pid(ts->stream, h->id, pcr_pid);
av_log(ts->stream, AV_LOG_TRACE, "pcr_pid=0x%x\n", pcr_pid);
program_info_length = get16(&p, p_end);
if (program_info_length < 0)
return;
program_info_length &= 0xfff;
while (program_info_length >= 2) {
uint8_t tag, len;
tag = get8(&p, p_end);
len = get8(&p, p_end);
av_log(ts->stream, AV_LOG_TRACE, "program tag: 0x%02x len=%d\n", tag, len);
if (len > program_info_length - 2)
// something else is broken, exit the program_descriptors_loop
break;
program_info_length -= len + 2;
if (tag == 0x1d) { // IOD descriptor
get8(&p, p_end); // scope
get8(&p, p_end); // label
len -= 2;
mp4_read_iods(ts->stream, p, len, mp4_descr + mp4_descr_count,
&mp4_descr_count, MAX_MP4_DESCR_COUNT);
} else if (tag == 0x05 && len >= 4) { // registration descriptor
prog_reg_desc = bytestream_get_le32(&p);
len -= 4;
}
p += len;
}
p += program_info_length;
if (p >= p_end)
goto out;
// stop parsing after pmt, we found header
if (!ts->stream->nb_streams)
ts->stop_parse = 2;
set_pmt_found(ts, h->id);
for (;;) {
st = 0;
pes = NULL;
stream_type = get8(&p, p_end);
if (stream_type < 0)
break;
pid = get16(&p, p_end);
if (pid < 0)
goto out;
pid &= 0x1fff;
if (pid == ts->current_pid)
goto out;
/* now create stream */
if (ts->pids[pid] && ts->pids[pid]->type == MPEGTS_PES) {
pes = ts->pids[pid]->u.pes_filter.opaque;
if (!pes->st) {
pes->st = avformat_new_stream(pes->stream, NULL);
if (!pes->st)
goto out;
pes->st->id = pes->pid;
}
st = pes->st;
} else if (is_pes_stream(stream_type, prog_reg_desc)) {
if (ts->pids[pid])
mpegts_close_filter(ts, ts->pids[pid]); // wrongly added sdt filter probably
pes = add_pes_stream(ts, pid, pcr_pid);
if (pes) {
st = avformat_new_stream(pes->stream, NULL);
if (!st)
goto out;
st->id = pes->pid;
}
} else {
int idx = ff_find_stream_index(ts->stream, pid);
if (idx >= 0) {
st = ts->stream->streams[idx];
} else {
st = avformat_new_stream(ts->stream, NULL);
if (!st)
goto out;
st->id = pid;
st->codecpar->codec_type = AVMEDIA_TYPE_DATA;
if (stream_type == 0x86 && prog_reg_desc == AV_RL32("CUEI")) {
mpegts_find_stream_type(st, stream_type, SCTE_types);
mpegts_open_section_filter(ts, pid, scte_data_cb, ts, 1);
}
}
}
if (!st)
goto out;
if (pes && !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);
if (desc_list_len < 0)
goto out;
desc_list_len &= 0xfff;
desc_list_end = p + desc_list_len;
if (desc_list_end > p_end)
goto out;
for (;;) {
if (ff_parse_mpeg2_descriptor(ts->stream, st, stream_type, &p,
desc_list_end, mp4_descr,
mp4_descr_count, pid, ts) < 0)
break;
if (pes && 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->codecpar->codec_tag = st->codecpar->codec_tag;
}
}
p = desc_list_end;
}
if (!ts->pids[pcr_pid])
mpegts_open_pcr_filter(ts, pcr_pid);
out:
for (i = 0; i < mp4_descr_count; i++)
av_free(mp4_descr[i].dec_config_descr);
}
//回调函数,接收pat表
static void pat_cb(MpegTSFilter *filter, const uint8_t *section, int section_len)
{
MpegTSContext *ts = filter->u.section_filter.opaque;
MpegTSSectionFilter *tssf = &filter->u.section_filter;
SectionHeader h1, *h = &h1;
const uint8_t *p, *p_end;
int sid, pmt_pid;
AVProgram *program;
av_log(ts->stream, AV_LOG_TRACE, "PAT:\n");
hex_dump_debug(ts->stream, section, section_len);
p_end = section + section_len - 4;
p = section;
if (parse_section_header(h, &p, p_end) < 0)
return;
if (h->tid != PAT_TID)
return;
if (ts->skip_changes)
return;
if (skip_identical(h, tssf))
return;
ts->stream->ts_id = h->id;
clear_programs(ts);
for (;;) {
sid = get16(&p, p_end);
if (sid < 0)
break;
pmt_pid = get16(&p, p_end);
if (pmt_pid < 0)
break;
pmt_pid &= 0x1fff;
if (pmt_pid == ts->current_pid)
break;
av_log(ts->stream, AV_LOG_TRACE, "sid=0x%x pid=0x%x\n", sid, pmt_pid);
if (sid == 0x0000) {
/* NIT info */
} else {
MpegTSFilter *fil = ts->pids[pmt_pid];
program = av_new_program(ts->stream, sid);
if (program) {
program->program_num = sid;
program->pmt_pid = pmt_pid;
}
if (fil)
if ( fil->type != MPEGTS_SECTION
|| fil->pid != pmt_pid
|| fil->u.section_filter.section_cb != pmt_cb)
mpegts_close_filter(ts, ts->pids[pmt_pid]);
if (!ts->pids[pmt_pid])
mpegts_open_section_filter(ts, pmt_pid, pmt_cb, ts, 1);
add_pat_entry(ts, sid);
add_pid_to_pmt(ts, sid, 0); // add pat pid to program
add_pid_to_pmt(ts, sid, pmt_pid);
}
}
if (sid < 0) {
int i,j;
for (j=0; j<ts->stream->nb_programs; j++) {
for (i = 0; i < ts->nb_prg; i++)
if (ts->prg[i].id == ts->stream->programs[j]->id)
break;
if (i==ts->nb_prg && !ts->skip_clear)
clear_avprogram(ts, ts->stream->programs[j]->id);
}
}
}
//回调函数,接收sdt表
static void sdt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len)
{
MpegTSContext *ts = filter->u.section_filter.opaque;
MpegTSSectionFilter *tssf = &filter->u.section_filter;
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;
av_log(ts->stream, AV_LOG_TRACE, "SDT:\n");
hex_dump_debug(ts->stream, section, section_len);
p_end = section + section_len - 4;
p = section;
if (parse_section_header(h, &p, p_end) < 0)
return;
if (h->tid != SDT_TID)
return;
if (ts->skip_changes)
return;
if (skip_identical(h, tssf))
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);
if (desc_list_len < 0)
break;
desc_list_len &= 0xfff;
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_len < 0 || desc_end > desc_list_end)
break;
av_log(ts->stream, AV_LOG_TRACE, "tag: 0x%02x len=%d\n",
desc_tag, desc_len);
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) {
AVProgram *program = av_new_program(ts->stream, sid);
if (program) {
av_dict_set(&program->metadata, "service_name", name, 0);
av_dict_set(&program->metadata, "service_provider",
provider_name, 0);
}
}
av_free(name);
av_free(provider_name);
break;
default:
break;
}
p = desc_end;
}
p = desc_list_end;
}
}