ffmpeg源码分析之mpegts.c——精简分析


建议大家熟读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;


//AVProgram主要保存频道名称,服务商等信息,如果想获取频道名称请注意下这个结构体
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;
    }
}









  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值