libdvbpsi源码分析(四)PAT表解析/重建

孤街浪徒 提交于 2020-04-17 02:47:25

【推荐阅读】微服务还能火多久?>>>

        由上一章 libdvbpsi源码分析(三)PSI decocder详细分析,我们知道了psi decoder的构建过程。本章将延续上文 以PAT表详细解析为例,由点及面的概述libdvbpsi的实现。
        下面详细分析pat decoder解码器的创建过程:
a、首先通过dvbpsi_new创建一个通用的decoder,但还未真正的实例化为pat decoder,即pat decoder还未初始化。
dvbpsi_t *dvbpsi_new(dvbpsi_message_cb callback, enum dvbpsi_msg_level level)
{
    dvbpsi_t *p_dvbpsi = calloc(1, sizeof(dvbpsi_t));
    if (p_dvbpsi == NULL)
        return NULL;

    p_dvbpsi->p_decoder  = NULL;	//赋值为NULL,pat decoder还未初始化
    p_dvbpsi->pf_message = callback;
    p_dvbpsi->i_msg_level = level;
    return p_dvbpsi;
}
b、初始化PAT decoder 并且绑定pat表的解析handle动作。其中dvbpsi_pat_attach中dvbpsi_decoder_new创建了 真正的pat decoder实例,并通过p_dvbpsi->p_decoder = DVBPSI_DECODER(p_pat_decoder)将其赋值给了抽象的decoder。
bool dvbpsi_pat_attach(dvbpsi_t *p_dvbpsi, dvbpsi_pat_callback pf_callback, void* p_cb_data)
{
    assert(p_dvbpsi);
    assert(p_dvbpsi->p_decoder == NULL);

    /* PSI decoder configuration and initial state */
    dvbpsi_pat_decoder_t *p_pat_decoder;
	/*创建真正的pat decoder*/
    p_pat_decoder = (dvbpsi_pat_decoder_t*) dvbpsi_decoder_new(&dvbpsi_pat_sections_gather,
                                                1024, true, sizeof(dvbpsi_pat_decoder_t));	
    if (p_pat_decoder == NULL)
        return false;

    /* PAT decoder information */
    p_pat_decoder->pf_pat_callback = pf_callback;
    p_pat_decoder->p_cb_data = p_cb_data;
    p_pat_decoder->p_building_pat = NULL;
	/*将pat decoder赋值给p_decoder(类型的强转dvbpsi_pat_decoder_t* 转成dvbpsi_decoder_t*)*/
    p_dvbpsi->p_decoder = DVBPSI_DECODER(p_pat_decoder);	
    return true;
}
【总结a,b】
dvbpsi_new是一个接口,创建抽象的decoder。但具体的要创建哪种specific decoder需要caller自己去初始化,赋予其实例化的属性。通过以下接口去实例化decoder。每个表的解析动作放置在tables/目录下的各个.c文件中。
{
   dvbpsi_pat_attach,(tables/pat.c)
   dvbpsi_bat_attach,(tables/bat.c)
   ...
}
c、PAT表中section的聚集
创建pat decoder实例时,传入了callback函数dvbpsi_pat_sections_gather, 用于gather PAT表中的所有section。 只有等待表中所有section收集完整并进行CRC校验后,才开始进行表的解析或重建。
void dvbpsi_pat_sections_gather(dvbpsi_t* p_dvbpsi, dvbpsi_psi_section_t* p_section)
{
    dvbpsi_pat_decoder_t* p_pat_decoder;

    assert(p_dvbpsi);
    assert(p_dvbpsi->p_decoder);

    if (!dvbpsi_CheckPSISection(p_dvbpsi, p_section, 0x00, "PAT decoder"))
    {
        dvbpsi_DeletePSISections(p_section);
        return;
    }

    /* Now we have a valid PAT section */
    p_pat_decoder = (dvbpsi_pat_decoder_t *)p_dvbpsi->p_decoder;

    /* TS discontinuity check */
    if (p_pat_decoder->b_discontinuity)
    {
        dvbpsi_ReInitPAT(p_pat_decoder, true);
        p_pat_decoder->b_discontinuity = false;
    }
    else
    {
        if (p_pat_decoder->p_building_pat)
        {
            if (dvbpsi_CheckPAT(p_dvbpsi, p_section))
                dvbpsi_ReInitPAT(p_pat_decoder, true);
        }
        else
        {
            if(    (p_pat_decoder->b_current_valid)
                && (p_pat_decoder->current_pat.i_version == p_section->i_version)
                && (p_pat_decoder->current_pat.b_current_next ==
                                               p_section->b_current_next))
            {
                /* Don't decode since this version is already decoded */
                dvbpsi_debug(p_dvbpsi, "PAT decoder",
                             "ignoring already decoded section %d",
                             p_section->i_number);
                dvbpsi_DeletePSISections(p_section);
                return;
            }
        }
    }
    /* Add section to PAT */
    if (!dvbpsi_AddSectionPAT(p_dvbpsi, p_pat_decoder, p_section))
    {
        dvbpsi_error(p_dvbpsi, "PAT decoder", "failed decoding section %d",
                     p_section->i_number);
        dvbpsi_DeletePSISections(p_section);
        return;
    }
    /* Check if we have all the sections */
    if (dvbpsi_decoder_psi_sections_completed(DVBPSI_DECODER(p_pat_decoder)))
    {
        assert(p_pat_decoder->pf_pat_callback);

        /* Save the current information */
        p_pat_decoder->current_pat = *p_pat_decoder->p_building_pat;
        p_pat_decoder->b_current_valid = true;

        /* Decode the sections */
        dvbpsi_pat_sections_decode(p_pat_decoder->p_building_pat,
                                   p_pat_decoder->p_sections);

        /* Delete the sections */
        dvbpsi_DeletePSISections(p_pat_decoder->p_sections);
        p_pat_decoder->p_sections = NULL;

        /* signal the new PAT */
        p_pat_decoder->pf_pat_callback(p_pat_decoder->p_cb_data,
                                       p_pat_decoder->p_building_pat);

        /* Reinitialize the structures */
        dvbpsi_ReInitPAT(p_pat_decoder, false);
    }
}
d、PAT表及PMT表的解析
紧接着对pat table进行解析或重建。 解析的动作实质就是绑定到pat decoder的handle(callback)。
(两者含义其实一样,知道了table表的所有细节,可以说成解析了table,也可以说成重建了table)
PAT表的协议根据标准 ISO/IEC 13818-1 section 2.4.4.3如下表所示:

源码解析如下:

/*****************************************************************************
 * handle_PAT
 *****************************************************************************/
static void handle_PAT(void* p_data, dvbpsi_pat_t* p_pat)
{
    dvbpsi_pat_program_t* p_program = p_pat->p_first_program;
    ts_stream_t* p_stream = (ts_stream_t*) p_data;

    p_stream->pat.i_pat_version = p_pat->i_version;
    p_stream->pat.i_ts_id = p_pat->i_ts_id;

    printf("\n");
    printf("  PAT: Program Association Table\n");
    printf("\tTransport stream id : %d\n", p_pat->i_ts_id);
    printf("\tVersion number : %d\n", p_pat->i_version);
    printf("\tCurrent next   : %s\n", p_pat->b_current_next ? "yes" : "no");
    if (p_stream->pat.pid->i_prev_received > 0)
        printf("\tLast received  : %"PRId64" ms ago\n",
               (mtime_t)(p_stream->pat.pid->i_received - p_stream->pat.pid->i_prev_received));
    printf("\t\t| program_number @ [NIT|PMT]_PID\n");
    while (p_program)
    {
        /* Attach new PMT decoder */
        ts_pmt_t *p_pmt = calloc(1, sizeof(ts_pmt_t));
        if (p_pmt)
        {
            /* PMT */
            p_pmt->handle = dvbpsi_new(&dvbpsi_message, p_stream->level);
            if (p_pmt->handle == NULL)
            {
                fprintf(stderr, "dvbinfo: Failed attach new PMT decoder\n");
                free(p_pmt);
                break;
            }

            p_pmt->i_number = p_program->i_number;
            p_pmt->pid_pmt = &p_stream->pid[p_program->i_pid];
            p_pmt->pid_pmt->i_pid = p_program->i_pid;
            p_pmt->p_next = NULL;

			/*创建PMT Decoder*/	
            if (!dvbpsi_pmt_attach(p_pmt->handle, p_program->i_number, handle_PMT, p_stream))
            {
                 fprintf(stderr, "dvbinfo: Failed to attach new pmt decoder\n");
                 break;
            }

            /* insert at start of list */
            p_pmt->p_next = p_stream->pmt;
            p_stream->pmt = p_pmt;
            p_stream->i_pmt++;
            assert(p_stream->pmt);
        }
        else
            fprintf(stderr, "dvbinfo: Failed create new PMT decoder\n");

        printf("\t\t| %14d @ pid: 0x%x (%d)\n",
                p_program->i_number, p_program->i_pid, p_program->i_pid);
        p_program = p_program->p_next;
    }
    printf("\tActive         : %s\n", p_pat->b_current_next ? "yes" : "no");
    dvbpsi_pat_delete(p_pat);
}

PAT表中含有PMT表的节目号,所以需要解析PMT表。
PMT表的协议根据标准ISO/IEC 13818-1 section 2.4.4.8如下表所示:

源码解析如下:

/*****************************************************************************
 * handle_PMT
 *****************************************************************************/
static void handle_PMT(void* p_data, dvbpsi_pmt_t* p_pmt)
{
    dvbpsi_pmt_es_t* p_es = p_pmt->p_first_es;
    ts_stream_t* p_stream = (ts_stream_t*) p_data;

    /* Find signalled PMT */
    ts_pmt_t *p = p_stream->pmt;
    while (p)
    {
        if (p->i_number == p_pmt->i_program_number)
            break;
        p = p->p_next;
    }
    assert(p);

    p->i_pmt_version = p_pmt->i_version;
    p->pid_pcr = &p_stream->pid[p_pmt->i_pcr_pid];
    p_stream->pid[p_pmt->i_pcr_pid].b_pcr = true;

    printf("\n");
    printf("  PMT: Program Map Table\n");
    printf("\tProgram number : %d\n", p_pmt->i_program_number);
    printf("\tVersion number : %d\n", p_pmt->i_version);
    printf("\tPCR_PID        : 0x%x (%d)\n", p_pmt->i_pcr_pid, p_pmt->i_pcr_pid);
    printf("\tCurrent next   : %s\n", p_pmt->b_current_next ? "yes" : "no");
    DumpDescriptors("\t   ]", p_pmt->p_first_descriptor);
    printf("\t| type @ elementary_PID : Description\n");
    while(p_es)
    {
        printf("\t| 0x%02x @ pid 0x%x (%d): %s\n",
                 p_es->i_type, p_es->i_pid, p_es->i_pid,
                 GetTypeName(p_es->i_type) );
        DumpDescriptors("\t|  ]", p_es->p_first_descriptor);
        p_es = p_es->p_next;
    }
    dvbpsi_pmt_delete(p_pmt);
}
【小结】至此,PAT表的解析分析完成了。具体在代码调试过程中,可借助pes码流分析工具,详细的查看各个表的信息,并结合dvb规范,详细查询每个descriptor的描述,完成解码工作。
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!