由上一章
libdvbpsi源码分析(三)PSI decocder详细分析,我们知道了psi decoder的构建过程。本章将延续上文 以PAT表详细解析为例,由点及面的概述libdvbpsi的实现。
下面详细分析pat decoder解码器的创建过程:
a、首先通过dvbpsi_new创建一个通用的decoder,但还未真正的实例化为pat decoder,即pat decoder还未初始化。
dvbpsi_new是一个接口,创建抽象的decoder。但具体的要创建哪种specific decoder需要caller自己去初始化,赋予其实例化的属性。通过以下接口去实例化decoder。每个表的解析动作放置在tables/目录下的各个.c文件中。
创建pat decoder实例时,传入了callback函数dvbpsi_pat_sections_gather, 用于gather PAT表中的所有section。 只有等待表中所有section收集完整并进行CRC校验后,才开始进行表的解析或重建。
紧接着对pat table进行解析或重建。 解析的动作实质就是绑定到pat decoder的handle(callback)。
(两者含义其实一样,知道了table表的所有细节,可以说成解析了table,也可以说成重建了table)
PAT表的协议根据标准 ISO/IEC 13818-1 section 2.4.4.3如下表所示:
下面详细分析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的描述,完成解码工作。
来源:oschina
链接:https://my.oschina.net/u/941692/blog/175807