mpegts代码分析

来源:互联网 发布:java程序员面试指南 编辑:程序博客网 时间:2024/06/06 04:05

因为TS流的复用和解复用是通过一个结构体 AVInputFormat 传递给解复用器的。所以重点分析该结构体提供的外部接口:

AVInputFormat mpegtsraw_demuxer = {    "mpegtsraw",    NULL_IF_CONFIG_SMALL("MPEG-2 raw transport stream format"),    sizeof(MpegTSContext),    NULL,    mpegts_read_header,    mpegts_raw_read_packet,    mpegts_read_close,    read_seek,    mpegts_get_pcr,    .flags = AVFMT_SHOW_IDS|AVFMT_TS_DISCONT,};


1、mpegts_read_header():


功能:判断是TS流的类型,建立TS流的业务信息表SDT表和PAD表


static int mpegts_read_header(AVFormatContext *s,                              AVFormatParameters *ap){    MpegTSContext *ts = s->priv_data;    ByteIOContext *pb = s->pb;    uint8_t buf[5*1024];    int len;    int64_t pos;    if (ap) {        ts->mpeg2ts_compute_pcr = ap->mpeg2ts_compute_pcr;        if(ap->mpeg2ts_raw){            av_log(s, AV_LOG_ERROR, "use mpegtsraw_demuxer!\n");            return -1;        }    }/* read the first 1024 bytes to get packet size */
    pos = url_ftell(pb); //保存流的当前位置,便于检测操作完成后恢复到原来的位置,这样在播放的时候就不会浪费一段流。    len = get_buffer(pb, buf, sizeof(buf));////读取一段流来检测TS包的大小    if (len != sizeof(buf))        goto fail;    ts->raw_packet_size = get_packet_size(buf, sizeof(buf)); //得到TS包的pack的size。在get_packedt_size中buf的大小必须大于1024,否则直接返回-1。得到TS流包的大小,通常是188bytes,我目前见过的都是188个字节的。
    //TS包的大小有三种:
    //1) 通常情况下的188字节
    //2)日本弄了个192Bytes的DVH-S格式
    //3)在188Bytes的基础上,加上16Bytes的FEC(前向纠错),也就是204bytes
/*static int get_packet_size(const uint8_t *buf, int size){    int score, fec_score, dvhs_score;//Must have 1024 byte    if (size < (TS_FEC_PACKET_SIZE * 5 + 1))        return -1;    score    = analyze(buf, size, TS_PACKET_SIZE, NULL); //通过在size长度内进行对比,获得一个0x47在在间隔packet_size出现的最大概率,以下类似    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;          //188    else if(dvhs_score > score && dvhs_score > fec_score) return TS_DVHS_PACKET_SIZE;//192    else if(score < fec_score && dvhs_score < fec_score) return TS_FEC_PACKET_SIZE;  //204    else                       return -1;}*/
/* 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-3; i++){        if(buf[i] == 0x47 && !(buf[i+1] & 0x80) && (buf[i+3] & 0x30)){            stat[x]++;             //sta[x]记录了在x位置上(这是在0~packet_size范围内),0x47出现的次数。记住x是每到paket_size就清零的。            if(stat[x] > best_score){                best_score= stat[x]; //best_score存储了0x47在某个位置上出现的最大的次数                if(index) *index= x;            }        }        x++;        if(x == packet_size) x= 0;    }    return best_score;} */
  if (ts->raw_packet_size <= 0)        goto fail;    ts->stream = s;

//auto_guess = 1, 则在handle_packet的函数中只要发现一个PES的pid就    //建立该PES的stream    //auto_guess = 0, 则忽略。

    //auto_guess主要作用是用来在TS流中没有业务信息时,如果被设置成了1的话,

    //那么就会将任何一个PID的流当做媒体流建立对应的PES数据结构。    //在mpegts_read_header函数的过程中发现了PES的pid,但    //是不建立对应的流,只是分析PSI信息。    //相关的代码见handle_packet函数的下面的代码:    // tss = ts->pids[pid];    //if (ts->auto_guess && tss == NULL && is_start) {    //    add_pes_stream(ts, pid, -1, 0);    //    tss = ts->pids[pid];    //}

ts->auto_guess = 0; if (s->iformat == &mpegts_demuxer) { //若当前输入的流为TS流 /* normal demux */ /* first do a scaning to get all the services */ url_fseek(pb, pos, SEEK_SET);
        //挂载解析SDT表的回调函数到ts->pids变量上,这样在handle_packet函数中根据对应的pid找到对应处理回调函数
mpegts_scan_sdt(ts);//这儿调用了mpegts_open_section_filter(ts, SDT_PID,sdt_cb, ts, 1),这个函数将在后面进行分析。//同上,只是挂上PAT表解析的回调函数 mpegts_set_service(ts);//这儿调用了mpegts_open_section_filter(ts, PAT_PID,pat_cb, ts, 1)函数 //探测一段流,便于检测出SDT,PAT,PMT表 handle_packets(ts, s->probesize); //这儿调用了handle_packet(ts, packet)函数 /* if could not find service, enable auto_guess *///打开add pes stream的标志,这样在handle_packet函数中发现了pes的
        //pid,就会自动建立该pes的stream。 ts->auto_guess = 1;#ifdef DEBUG_SI av_log(ts->stream, AV_LOG_DEBUG, "tuning done\n");#endif s->ctx_flags |= AVFMTCTX_NOHEADER; } else { AVStream *st; int pcr_pid, pid, nb_packets, nb_pcrs, ret, pcr_l; int64_t pcrs[2], pcr_h; int packet_count[2]; uint8_t packet[TS_PACKET_SIZE]; /* only read packets */ st = av_new_stream(s, 0); if (!st) goto fail; av_set_pts_info(st, 60, 1, 27000000); // st->codec->codec_type = CODEC_TYPE_DATA; st->codec->codec_id = CODEC_ID_MPEG2TS; /* we iterate until we find two PCRs to estimate the bitrate */ pcr_pid = -1; nb_pcrs = 0; nb_packets = 0; for(;;) { ret = read_packet(s->pb, packet, ts->raw_packet_size); if (ret < 0) return -1; pid = AV_RB16(packet + 1) & 0x1fff; if ((pcr_pid == -1 || pcr_pid == pid) && parse_pcr(&pcr_h, &pcr_l, packet) == 0) { pcr_pid = pid; packet_count[nb_pcrs] = nb_packets; pcrs[nb_pcrs] = pcr_h * 300 + pcr_l; nb_pcrs++; if (nb_pcrs >= 2) break; } nb_packets++; } /* NOTE1: the bitrate is computed without the FEC */ /* NOTE2: it is only the bitrate of the start of the stream */ ts->pcr_incr = (pcrs[1] - pcrs[0]) / (packet_count[1] - packet_count[0]); ts->cur_pcr = pcrs[0] - ts->pcr_incr * packet_count[0]; s->bit_rate = (TS_PACKET_SIZE * 8) * 27e6 / ts->pcr_incr; st->codec->bit_rate = s->bit_rate; st->start_time = ts->cur_pcr;#if 0 av_log(ts->stream, AV_LOG_DEBUG, "start=%0.3f pcr=%0.3f incr=%d\n", st->start_time / 1000000.0, pcrs[0] / 27e6, ts->pcr_incr);#endif } //恢复到检测前的位置。 url_fseek(pb, pos, SEEK_SET); return 0; fail: return -1;}

2、下面接着分析上面注释时候的函数mpegts_open_section_filter(MpegTSContext   *ts,   unsigned int pid , SectionCallback   *section_cb, void  *opaque,                                         int  check_crc):

函数的功能:将pid值为pid的滤波器挂载在ts->PID[pid]上,并设置filter的相关参数

struct MpegTSContext {    /* user data */    AVFormatContext *stream;    /** raw packet size, including FEC if present            */    int raw_packet_size;    int pos47;    /** if true, all pids are analyzed to find streams       */    int auto_guess;    /** compute exact PCR for each transport stream packet   */    int mpeg2ts_compute_pcr;    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;    /******************************************/    /* private mpegts data */    /* scan context */    /** structure to keep track of Program->pids mapping     */    unsigned int nb_prg;    struct Program *prg;    /** filters for various streams specified by PMT + for the PAT and PMT */    MpegTSFilter *pids[NB_PID_MAX];  //这儿每一个PID都有一个filter滤波器};


static MpegTSFilter *mpegts_open_section_filter(MpegTSContext *ts, unsigned int pid,                                         SectionCallback *section_cb, void *opaque,                                         int check_crc){    MpegTSFilter *filter;    MpegTSSectionFilter *sec;#ifdef DEBUG_SI    av_log(ts->stream, AV_LOG_DEBUG, "Filter: pid=0x%x\n", pid);#endif    if (pid >= NB_PID_MAX || ts->pids[pid])        return NULL;    filter = av_mallocz(sizeof(MpegTSFilter)); //给filter分配空间,挂载到MpegTSContext的pids上,就是该实例    if (!filter)        return NULL;    ts->pids[pid] = filter;   //挂载filter    filter->type = MPEGTS_SECTION;    filter->pid = pid;        //需要截获的PID值    filter->last_cc = -1;    sec = &filter->u.section_filter; //设置filter相关的参数,因为业务信息表的分析的单位是段,
    //所以该filter的类型是MPEGTS_SECTION sec->section_cb = section_cb; //设置filter回调处理函数 sec->opaque = opaque; sec->section_buf = av_malloc(MAX_SECTION_SIZE); //分配段数据处理的缓冲区,调用handle_packet函数后会调用
    //write_section_data将ts包中的业务信息表的数据存储在这儿,
    //直到一个段收集完成才交付上面注册的回调函数处理 sec->check_crc = check_crc; if (!sec->section_buf) { av_free(filter); return NULL; } return filter;}


3、函数:handle_packets(MpegTSContext *ts, int nb_packets)



static int handle_packets(MpegTSContext *ts, int nb_packets){    AVFormatContext *s = ts->stream;    ByteIOContext *pb = s->pb;    uint8_t packet[TS_PACKET_SIZE];    int packet_num, ret;    ts->stop_parse = 0;    packet_num = 0;    for(;;) {        if (ts->stop_parse>0)            break;        packet_num++;        if (nb_packets != 0 && packet_num >= nb_packets)            break;//resrech syn (0x47),cpy one ts pkt to packet.        ret = read_packet(pb, packet, ts->raw_packet_size);         if (ret != 0)            return ret;        handle_packet(ts, packet);    }    return 0;}


read_packet()函数:读取一个packet到buf中:


static int read_packet(ByteIOContext *pb, uint8_t *buf, int raw_packet_size){    int skip, len;    for(;;) {        len = get_buffer(pb, buf, TS_PACKET_SIZE); //读一个包的长度到buf中        if (len != TS_PACKET_SIZE)            return AVERROR(EIO);        /* check paquet sync byte */        if (buf[0] != 0x47) {    //如果当前的数据没有同步,则需要重新同步后,在读数据到buf中。            /* find a new packet start */            url_fseek(pb, -TS_PACKET_SIZE, SEEK_CUR); //移动指针            if (mpegts_resync(pb) < 0)      //重新进行同步,即将指针指向packet的头部                return AVERROR_INVALIDDATA;            else                continue;        } else {            skip = raw_packet_size - TS_PACKET_SIZE;            if (skip > 0)                url_fskip(pb, skip); //跳过一个包的长度,即指向下一个包            break;        }    }    return 0;}


handle_packet()函数:

收集PID的信息

/* handle one TS packet */static void handle_packet(MpegTSContext *ts, const uint8_t *packet){    AVFormatContext *s = ts->stream;    MpegTSFilter *tss;    int len, pid, cc, cc_ok, afc, is_start;    const uint8_t *p, *p_end;    int64_t pos;    pid = AV_RB16(packet + 1) & 0x1fff; //获得当前包的PID的值    if(pid && discard_pid(ts, pid))     //        return;    //PES or PSI     is_start = packet[1] & 0x40;    tss = ts->pids[pid];                   if (ts->auto_guess && tss == NULL && is_start) {        add_pes_stream(ts, pid, -1, 0);        tss = ts->pids[pid];    }    if (!tss)                         //仅当PID为SDT或PAT时,才继续处理,否则直接返回        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;    /* 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;    pos = url_ftell(ts->stream->pb);    ts->pos47= pos % ts->raw_packet_size;    if (tss->type == MPEGTS_SECTION) {        if (is_start) {            /* pointer field present */            len = *p++;              //pointer_field指针:当一个分段开始时,其值为0。当pay_unit_start_indicator为1时,传输流有效负载的第一字节应该包含此指针。如果一个传输流中没有一个分段开始,则pay_unit_start_indicator为0,且没有pointer_field字段。            if (p + len > p_end)                return;            if (len && cc_ok) {                /* write remaining section bytes */                write_section_data(s, tss,                                   p, len, 0);                /* check whether filter has been closed */                if (!ts->pids[pid])                    return;            }            p += len;            if (p < p_end) {                write_section_data(s, tss,                                   p, p_end - p, 1);            }        } else {            if (cc_ok) {                write_section_data(s, tss,                                   p, p_end - p, 0);            }        }    } else {        // Note: The position here points actually behind the current packet.        tss->u.pes_filter.pes_cb(tss,                                 p, p_end - p, is_start, pos - ts->raw_packet_size);    }}







原创粉丝点击