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

本文详细分析了PAT表的解码过程及libdvbpsi库的实现原理。从PAT解码器创建、PAT表section聚集、PAT表解析等方面进行了深入探讨,并介绍了如何根据PAT表进一步解析PMT表。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

        由上一章 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的描述,完成解码工作。

转载于:https://my.oschina.net/mavericsoung/blog/175807

一、 编译安装VLC 使用vlc-1.0.5.tar.bz2 + live555-2010.01.tar.gz export MYDIR=/root/lf 将live555-2010.01.tar.gz和vlc-1.0.5.tar.bz2放置在$MYDIR下 cd $MYDIR () 编译liveMedia tar xvfz live555-2010.01.tar.gz cd live ./genMakefiles linux make () 安装libdvbpsi rpm -hiv libdvbpsi-0.1.7-1.el4.rf.i386.rpm rpm -hiv libdvbpsi-devel-0.1.7-1.el4.rf.i386.rpm 需要什么rpm可到http://apt.sw.be/redhat/el4/en/i386/rpmforge/RPMS/中下载 () 编译VLC 1. configure ./configure --prefix=/usr --enable-debug --disable-avcodec --disable-hal --disable-avformat --disable-swscale --disable-a52 --disable-x11 --disable-sdl --disable-fribidi --disable-dbus --disable-libgcrypt --disable-gnutls --disable-remoteosd --disable-skins2 --disable-qt4 --disable-libxml2 --disable-lua --disable-xcb --disable-alsa --disable-v4l2 --disable-qt --enable-run-as-root --enable-live555 --with-live555-tree=$MYDIR/live --disable-mad --disable-postproc --enable-dvbpsi --enable-x264 2. make 3. 出现错误error: syntax error before "vlc_spinlock_t": ../include/vlc_threads.h:240: error: syntax error before "vlc_spinlock_t" …… make[4]: *** [control/libvlc_la-vlm.lo] Error 1 make[4]: Leaving directory `/root/lf/vlc-1.0.5/src' make[3]: *** [all-recursive] Error 1 make[3]: Leaving directory `/root/lf/vlc-1.0.5/src' make[2]: *** [all] Error 2 make[2]: Leaving directory `/root/lf/vlc-1.0.5/src' make[1]: *** [all-recursive] Error 1 make[1]: Leaving directory `/root/lf/vlc-1.0.5' make: *** [all] Error 2 通过以下命令解决: sed -e 's/define\s*_POSIX_SPIN_LOCKS\s*/define _POSIX_SPIN_LOCKS -1 \/\/ wrong: /g' -i /usr/include/bits/posix_opt.h 4. 重试make出现error: `AFS_SUPER_MAGIC' undeclared: file.c:52:28: linux/magic.h: No such file or directory file.c: In function `IsRemote': file.c:141: error: `AFS_SUPER_MAGIC' undeclared (first use in this function) file.c:141: error: (Each undeclared identifier is reported only once file.c:141: error: for each function it appears in.) file.c:142: error: `CODA_SUPER_MAGIC' undeclared (first use in this function) file.c:143: error: `NCP_SUPER_MAGIC' undeclared (first use in this function) file.c:144: error: `NFS_SUPER_MAGIC' undeclared (first use in this function) file.c:145: error: `SMB_SUPER_MAGIC' undeclared (first use in this function) make[5]: *** [libaccess_file_plugin_la-file.lo] Error 1 make[5]: Leaving directory `/root/lf/vlc-1.0.5/modules/access' make[4]: *** [all-recursive] Error 1 make[4]: Leaving directory `/root/lf/vlc-1.0.5/modules/access' make[3]: *** [all] Error 2 make[3]: Leaving directory `/root/lf/vlc-1.0.5/modules/access' make[2]: *** [all-recursive] Error 1 make[2]: Leaving directory `/root/lf/vlc-1.0.5/modules' make[1]: *** [all-recursive] Error 1 make[1]: Leaving directory `/root/lf/vlc-1.0.5' make: *** [all] Error 2 将下面的magic.h文件复制到/usr/include/linux/目录下 5. make install 6. 检查 vlc -l | grep access_output access_output_udp UDP stream output access_output_http HTTP stream output access_output_file File stream output access_output_dummy Dummy stream output access_output_rtmp RTMP stream output vlc -l | grep dvbpsi mux_ts TS muxer (libdvbpsi) () 进行RTSP To TS转码:  不经视音频重编码的直接转码: vlc rtsp://172.17.0.219:8554/1 --sout '#standard{access=http, mux=ts, dst=172.17.0.100:1234}' 注:黑体部分为本机IP和端口,不能是127.0.0.1 vlc rtsp://172.17.0.219:8554/1 --sout '#duplicate{dst="std{access=udp, mux=ts, dst=172.17.1.116:1234}"}' vlc rtsp://172.17.0.219:8554/1 --sout '#duplicate{dst="std{access=http, mux=ts, dst=172.17.0.100:1234}"}' 二、 视音频重编码的VLC编译安装 () 需要编译安装ffmpeg ./configure --prefix=/usr --enable-libfaac --enable-libx264 --enable-pthreads --disable-ffserver --disable-ffplay --enable-gpl --enable-nonfree [root@localhost ffmpeg-0.6.7]# make common.mak:28: *** unterminated call to function `foreach': missing `)'. Stop. 出现上面错误需要更新Make版本,使用make-3.81.tar.gz ./configure --prefix=/usr;make;make install In file included from libavdevice/v4l.c:33: /usr/include/linux/videodev.h:56: error: syntax error before "ulong" /usr/include/linux/videodev.h:72: error: syntax error before '}' token 编辑/usr/include/linux/videodev.h第56行,把ulong改为 unsigned long () 编译VLC ./configure --prefix=/usr --enable-debug --enable-avcodec --disable-hal --disable-avformat --disable-swscale --disable-a52 --disable-x11 --disable-sdl --disable-fribidi --disable-dbus --disable-libgcrypt --disable-gnutls --disable-remoteosd --disable-skins2 --disable-qt4 --disable-libxml2 --disable-lua --disable-xcb --disable-alsa --disable-v4l2 --disable-qt --enable-run-as-root --enable-live555 --with-live555-tree=$MYDIR/live --disable-mad --disable-postproc --enable-dvbpsi --enable-x264 没有--enable-avcodec运行VLC转码会出现"cannot find video decoder"的错误。 () 进行RTSP To TS转码: vlc tcp://172.17.0.199:7799 --sout '#duplicate{dst="transcode{venc=x264{profile=baseline},vcodec=h264,acodec=mpga,vb=644,ab=56}:std{access=udp,mux=ts,dst=172.17.0.219:1234}"}' 使用VLC转时,不要激活转码 可正常工作的选项: :sout=#http{mux=ts,dst=:8080/1} :sout-keep :sout=#udp{mux=ts,dst=172.17.1.116:1234} :sout-keep http://www.videolan.org/doc/videolan-howto/en/ch09.html ffmpeg -i "rtsp://172.17.0.219:8554/1" -acodec libfaac -vcodec libx264 -vpre default -f mpegts -async 1 udp://127.0.0.1:3344 ./configure --prefix=/usr --enable-libfaac --enable-libx264 --enable-pthreads --disable-ffserver --disable-ffplay --enable-gpl --enable-nonfree 转码 ffmpeg -i "rtsp://172.17.0.219:8080/1" -acodec libfaac -vcodec libx264 -vpre default -f mpegts -async 1 udp://172.17.0.219:3344
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值