DVB-subtitle解析流程浅

参考ISO13818-1,EN-300743,从TS流解析开始,结合MS6328的code将DVB Subtitle的解析过程简单梳理了一下。

一、TS  PAT  PMT  PES (参考ISO13818-1)

  1. 由TS包中取出PAT表

从搜台开始pat 解析:

MW_DVB_SI_PSI_Parser ::_ScanStar()下m_pPatParser->Start(PAT_SCAN_TIMEOUT)

这里mapi_demux_section_filter *m_pSecFilter = m_pDemux->AllocateSectionFilter()返回一个demux section filter 指针

这个section指定了:

stSecConfig.u16PID = PID_PAT;

stSecConfig.au8MatchByte[0] = TID_PAS; //table id

指定pat相应的section

(//这里的section,用来分段传输psi中各个table信息的结构,是去掉了TS 包头以后的TS payload 数据)

SI以及MPEG-2 PSI tables在被插入TS 包之前会被分段成一个或多个section。Section是可变长度的,每个table的section被限制在1024 bytes,除了EIT 表的section为4096bytes。Refer to《en_300468》

这里是取TS中PSI数据,区别于PES数据)

注意:有效负载(payload):指包中跟在包头后面的bytes。比如:TS 包的有效负载包括一个PES_packet_header 和PES_packtet_data_bytes; 或者,一个指针域(pointer_field)和 PSI_sections 或者 私有数据。

一个PES 包的有效负载只包含PES_packtet_data_bytes。另外,TS 包的包头和调整区域(adaptation fields)不是有效负载。

m_pSecFilter->Init(stSecConfig)

m_pSecFilter->Start()

解析完pat,得到有用的信息,兵存储于Pat Table结构中:

mapi_si_PAT_parser::Parse()

{

PatTbl.ServiceIDInfo[u16ServiceIndex].u16ServiceID

PatTbl.ServiceIDInfo[u16ServiceIndex].u16PmtPID

u16ServiceIndex++;

PatTbl.u16ServiceCount = u16ServiceIndex

}
  1. 由PAT得到对应的PMT

在_PatReady()下会将对应的从pat解析时得到的PmtPID存储如pCurProg 信息中 (serviceIID,OriginalNetworkId,TransportStream_id,NetworkId),PMT 会根据curprog.u16PmtPID来解析。

_PMT_Monitor()下sm_pCurPmtParser->Start(xx, PMTPID, xx)

mapi_si_PMT_parser::Start()

stSecConfig.u16PID = u16PMTPid;

stSecConfig.au8MatchByte[0] = TID_PMS; //table id

底层就会解析出相应PID的section数据,存储到pmt table中。

每次pmt的刷新,会将信息存到curprog中,相应的信息会传递给subtitle service结构体,MW_DTV_Subtitle::m_Services,通过队列存储起来:u16Sub_Pid,u8subtitling_type。

  1. 根据PMT得出subtitle相关信息

有了PMT信息,就可以得出subtitle 的信息了,所以当打开一个相应的subtitle时,就可以通过索引找到存储起来的subtitle信息。

….

         if(m_pDVBSubtitle_decoder->Connect() == TRUE)

        {

            m_pDVBSubtitle_decoder->SetCA_PageID(u16composition_page_id,u16ancillary_page_id);    //composion_pageid   ancillary_pageid

            m_pDVBSubtitle_decoder->Open(u16PID, bFileIn);

            m_enOpenType = EN_OPEN_SUBTITLE_DVB;

            bSuccess = TRUE;

        }

注:ancillary_page_id只对于一个subtitle流中复合多个subtitle services(eg,不同语言,或者normal/hearing impaired)才有用,否则与composition_page_id一样

根据subtitle的PID(u16PID)就开始了subtitle的解析。

二、Subtitle 解析

  1. 下面是PES的处理

MW_DVBSubtitleDecoder::Connect()

这里创建subtitle的render相关,并且会create 一个线程(__sbt_thread())来处理subittle的数据。

__sbt_thread():

MW_DVBSubtitleDecoder::__process_PES()

<1>.在MW_DTV_Subtite::Init()下会有m_pDVBSubtitle_decoder->AllocatePESFilter()会分配一个PESFilter,注意与上面的SectionFilter区别。

<2>. m_pPES_Filter->Open(u16PID) 这里会传递需要解析的PID给相应的Pesfilter,然后再根据pes包头取得数据存处于buffer(一个完整的PES packet)。

这里 Stream_id必须是0xBD,表示private_stream_1(具体可查看iso13818-1 Table2-18)

<3>__compute_PTS(buffer)

取得PTS_DTS_flags(2bit)(pu8bff[7] & 0xc0 == 0x80 或者 0xc0)

当此值为‘10’,表示PTS fields存在与PES 包头

当此值为‘11’,表示PTS fields和DTS fields都存在与PES 包头

当此值为‘00’,表示PTS或者DTS fields都不会存在与PES包头

‘01’是禁止的

PTS   33bit,分3部分,DTS 也是33bit,同样是由3部分组成,这里我们只取PTS,用不到DTS(具体可以参考iso13818-1关于PES packet结构的spec)

if(((pu8bff[7] & 0xc0) == 0x80) || ((pu8bff[7] & 0xc0) == 0xc0))

    {

        u32tmp = pu8bff[9];

        u32PTSLow = (u32tmp & 0x06);

        u32PTSLow <<= 29;

        u32tmp = pu8bff[10];

        u32tmp <<= 22;

        u32PTSLow |= u32tmp;

         u32tmp = pu8bff[11] & 0xfe;

        u32tmp <<= 14;

        u32PTSLow |= u32tmp;

         u32tmp = pu8bff[12];

        u32tmp <<= 7;

        u32PTSLow |= u32tmp;

        u32tmp = pu8bff[13] & 0xfe;

        u32tmp >>= 1;

        u32PTSLow |= u32tmp;

}

(当然,还有PTS的特殊情况,这里就用到了system time clock(STC)的概念

理想情况下:如果解码器的时钟频率与编码器匹配,那么解码速率就与编码时一致。这时候任何正确的 PCR(由TS取得)就可以用作解码器的STC。但实际情况是不可能匹配的。一种方法是通过phase-locked loop(PLL)来强制解码器的clock与收到的数据流一致。)

然后会将取得的一个完整的PES:buffer数据以及PTS压入一个队列DataQueue,_push(pu8Buf, u16Len, u32PTS)

mapi_dvb_subtitle_decoder::Main(MAPI_U32 u32STC)

      

pSubtitle->__GetCurrentPTS(&u32STC);

mapi_interface::Get_mapi_dvb_subtitle()->Main(u32STC);

这个main函数就是处理DataQueue中的数据

void mapi_dvb_subtitle_decoder::Main(MAPI_U32 u32STC)

{

    if(DataQueue_CheckPTS(u32STC))    //查找DataQueue中所有PTS,若有一小于当前STC,则满足条件

    {

        QueueElement qeCur;

        if(_pop(&qeCur) == MAPI_TRUE)

        {
            __decode(&qeCur);

            _freeQueueElement(&qeCur);

        }

    }

}

到这里就进入了subtitle的decode部分了

  1. __decode()   

Mapi_dvb_subtitle_decoder.cpp (参考EN_300743)

从code中mapi_dvb_subtitle_decoder::__decode(QueueElement *qe){}可见解析流程:

__decode_PES_Header()->__decode_Segment()->__render_Display()->__resetObject()

(这段可参考pes_packet结构)

<1>pes  prefix,并取得pes length

m_sbt_stream.SkipLen(4);

m_sbt_stream.GetWord(&m_u16_PES_length);

指针向后移动6个字节

<2>__decode_PES_Header()

这里取得pes headerlen,做一些校验,并根据headerlen 将PES header剔除

找出PES header的界限?

<3> __decode_CheckStreamID()

这里开始就是剔除了PES header的数据,开始解析subtitling segment数据

当是DVB subtitle stream时,PES_packet_data_bytes的结构如下:

end_of_PES_data_field_marker 这个占不占空间呢?  貌似不占

data_identifier 必须是0x20,subtitle_steam_id必须是0x00

<4> __decode_Segment()

这里取得page_id,segment_length,

还记得上面open subtitle之前

m_pDVBSubtitle_decoder->SetCA_PageID(u16composition_page_id,u16ancillary_page_id)这个接口吗?

这是从PMT中取得的compositon_page_id,ancillary_page_id.

在这里需要判断此segment的pageid是否等于上面之一,否则就跳过这个segment

  if((u16_page_id != m_u16CPID) && (u16_page_id != m_u16APID))

        {

            // skip this segment, not whole PES

            m_sbt_stream.SkipLen(u16_segment_length);

            m_u16_PES_length -= u16_segment_length;

        }

Segment_length 是指segment_data_field()的数据bytes,其具体的数据结构是由segment_type指定

Segment的第二个字节表示segment_type,,指定了这个segment 数据的类型

1) case 0x14   display definition segment

这里是假定显示的宽度是720pixels 显示的高度是576lines

Segment 结构如下:

定义了display definition的一些相关 具体可参考(en300743 7.2.1)

2)case 0x10  page composition segment

这个类型的sgement结构如下:

__decode_Page():

取得regioncount = u16_segment_length / 6 (page segment的长度是6个字节)

通过循环,取得所有有效region的region id,以及该region显示的位置坐标(h,v)

存处于一个m_page的结构体中

page_time_out:表示page instance存在的时间。主要是为了防止错误发生时(比如page instance其time out没有被重新定义或没有删除)导致page instance 一直显示

page state:

0x00:normal case,用于page update,仅显page instance有改变的subtitle elements

此case下说明region_id已经存在,需要判断是否已经存在。当然一开始page state不会是nomal case的情况。????

0x01:acquisition point 用于page refresh ,显示下一个page instance要用到的所有的subtitle 元素

这里主要执行__resetObject()  重置object的索引,version等

0x10 :mode change,用于new  page,显示new page需要的所有subtitle 元素

这里需reset region,reset object,CLUT索引,screen buffer等

3)case 0x11  region compositon segment

__decode_Region()

Region_depth指定了pixel depth

CLUT_id  指定了用于这个region的CLUTS

Object_id  指定了这个region内显示的object(通过循环取得所有的object)

Region_fill_flag定义为1后,说明该region需要用背景色填充,填充的颜色由region_8-bit_pixel-coderegion_4-bit_pixel-coderegion_2-bit_pixel-code指定;

region_8-bit_pixel-code:指定8bit的CLUT表的入口,但仅仅当该region_depth是8 bit的时候才有效;

region_4-bit_pixel-code:指定4 bit的CLUT表入口。当region depth是4bit可用;或者region depth是8bit,且region_level_of_compatibility是4bit的时候才有用;

region_2-bit_pixel-code:指定2bit的CLUT表入口。单region depth为2bit,或者region_level_of_compatibility为2bit的时候可用。

region_level_of_compatibility:表示解码器满足的最小CLUT类型(2bit、4bit、8bit)

如果page state是normal case的情况,说明region_id并没有改变(与已经解析出的region信息作比较),无需重新创建region

(但是这里如果新来的region width/height 与已经解析出来的region的width/height不一样,需要替换并重新创建这个region)

如果 page state是mode change或acquisition point,需要重新解析region_id,重新创建region

这里解出的region_width region_height,不能超过上面DDS 段解除的display width,display height

根据取出的region_id,region_width,region_heigth,pixel_depth,来创建__createRegionBuffer(),最终通过DirectFB 来绘制一个window

__createRegionBuffer()最终是通过IDirectFB*做了两件事CreateWindow、SetPallete。

也就是说一个新的region,就对应了一个新的IDirectFBWindow*

大致情况如下:

 If (可以显示了) //这个应该是表示这种surface是最终要显示出来的。  一般只需要创建一个可以显示的surface就可以了

{

  1. 通过IDirectFB*取得IDirectFBDisplayLayer*,存为m_pDFBLayer;这样就可以用过m_pDFBLayer做进一步的操作了。
  2. m_pDFBLayer-> SetCooperativeLevel (m_pDFBLayer, DLSCL_ADMINISTRATIVE)//设置合作级别。这里应该是表示管理权限,枚举出window,并管理它们
  3. m_pDFBLayer->GetConfiguration(m_pDFBLayer,&config)//获得当前layer的配置信息,如width、height、pixelformat等(这里的width、height来自与region_width、region_height)

当然获取config是为了重新设置之,设置好后,需调用m_pDFBLayer->SetConfiguration()

  1. m_pDFBLayer->GetScreen(m_pDFBLayer,&p_screen);//取得screen指针

然后再根据screen的方法p_screen->GetSize(p_screen,&screenwidth,&screenheight),取得screen的宽和高。

获取screen的宽和高,主要是为了判断与当前video的宽和高的关系(MS6328这段代码没看懂?),然后通过

m_pDFBLayer-> SetScreenLocation (m_pDFBLayer,x,y,width,height)//来设置screen的位置

  1. m_pDFBLayer->Create(m_pDFBLayer,&desc,&m_pDFBWindow)//由layer来创建window,其中desc是一些配置信息
  2. m_pDFBWindow->GetSurface(m_pDFBWindow,&m_Windows[u8WinID].pSurface)//

每次创建一个window,都获取一个surface,并存储到window数组中,所以一个region就对应一个window的数组元素,也对应一个surface

  1. m_pDFBWindow->SetOpacity(m_pDFBWindow->,0/0xff)

0表示全透明,隐藏window,0xff表示不透明

}

else  //这种方式创建的surface不可以显示到屏幕上 

{

//也就是创建一个region时,同时创建一个surface(由超级接口IDirectFB插口创建),并存储入数组里(宽高像素格式pitch等)

直接由IDirectFB创建一个surface,存储到数组中

m_pDFB->CreateSurface(m_pDFB, &surDesc,&m_Windows[u8WinID].pSurface)

surDesc指定了一些配置信息

在官网的例子中,看到surDesc.caps  = DSCAPS_PRIMARY | DSCAPS_FLIPPING;,设置为primary surface,应该同上面是一样的功能???

pixelformat:使用的是DSPF_LUT8

width:region_width

height:region_heigth

}

在取得surface之后,设置surface的调色板

  1. m_Windows[u8WinID].pSurface->GetPalette(m_Windows[u8WinID].pSurface,&palette)//这里为什么无需SetPalette?Surface创建后就能直接得到GetPalette??
  2. 接着palette->SetEntries(palette,DFBColor*color,num_colors,offset)

接着palette->Release(palette)//这个接口在官网的接口定义里没有找到?

Region segment解析完后得到了一系列索引信息,object索引,CLUT索引等;具体的内容

和颜色还需进一步解析相关的segment。

我们知道surface代表一块预留的内存,用来保存像素数据,那么像素数据从哪里来呢?

在解析object segment的时候,会将像素数据存储到surface对应的内存地址中。(具体见解析object)

4)case 0x12  CLUT definition segment

循环解析出所有的cult信息,用YCbCrT 颜色系统存储,需转换成ARGB

按公式将YCbCrT的信息转换成ARGB相关,T直接转换为A(透明度),Y=0表示透明

(略写)

5)case 0x13 object data segment

object_coidng_method ==‘0x00’  编码方式是像素编码,这种是graphical object(一般是这种情况)

object_coding_method==’0x01’ 编码方式是字符编码,这种是字符格式的字幕

一个object 是由top field 和bottom field交错组成。

每一个对象,用于top filed 的pixel-data sub-block 和用于 bottom field的 pixel-data sub-block必须存储在相同的object_data_segment内。 如果bottom field没有数据,则取top field的数据作为bottom field的数据。

对于pixel-data sub_block的结构可参考 EN_300743 7.2.5,详细定义了pixel data的编码规则。

在new region的时候创建了surface,那么这个时候可以获取到surface对应的存储像素数据的内存地址,往该内存地址写入数据即可。

m_Windows[u8WinID].pSurface->Lock(m_Windows[u8WinID].pSurface, DSLF_WRITE, (void **) pu8Buffer, &u32pitch)  //DSLF_WIRTE,表示该地址可写的权限、

pu8Buffer是数据指针,u32pitch是行距

写入像素数据的起始地址是这么计算的:pu8Buffer+ object_horizontal_position(region中取得)+ object_vertical_position*pitch

这里涉及到图像压缩算法 run length encoding(行程编码):将一扫描行中的颜色值相同的相邻像素用一个计算值和那些像素值的颜色值来代替。比如aaabccccccddeee,用3a1b6c2d3e来代替

比如

run_lenght_3-10指示了2—bit_pixel_code重复的次数。。

m_Windows[u8WinID].pSurface->Unlock(m_Windows[u8WinID].pSurface);//记得执行这步

到这一步,surface的内存中已经有像素数据了,但是并不会立即显示出来。

1.surface是单buffer的(这个怎么判断呢?)

将每个region对应的surface,用StretchBlit()输入到OFFSCREEN特殊的surface:

m_Windows[u8DstWinID].pSurface->StretchBlit(DstSurface,OFFSCREEN , pSrcDFBRect, pDstDFBRect)

当然要使用StretchBlit之前,还需先用SetBlittingFlags(sur*,DSBLIT_NOFX)、SetRenderOptions(sur*,DSRO_SMOOTH_UPSCALE)这两个接口设置一下。

  1. surface是double buffer

pSurface->GetCapabilities(m_Windows[u8DstWinID].pSurface, &surfaceCaps)//取得该surface的相关属性

if(DSCAPS_DOUBLE == (surfaceCaps & DSCAPS_DOUBLE))//来判断是否是double buffer

pSurface->Flip(m_Windows[u8DstWinID].pSurface, NULL, (DFBSurfaceFlipFlags)DSFLIP_NONE)//用Flip将数据强行送到屏幕显示

最后别忘了pSurface->ReleaseSource(pSurface)//把该surface可能的引用全部释放掉

尤其在用了StretchBlit()方法后

PS:

MS6328创建了两个特殊的surface:

  1. CreateWindow(ONSCREEN_GWIN_ID, &rect, MAPI_TRUE, m_pCallbackArgument);//表示这个surface是可以显示出来的,具体的步骤可见上述region 解析部分
  2. CreateWindow(OFFSCREEN_GWIN_ID, &rect, MAPI_FALSE, m_pCallbackArgument);//这个surface是不可以显示出来的作为转存数据使用。(上面每次new region创建的surface也是无需显示的,可显示的只要一个即可)

6)0x80 end of display set segment

提供一种模糊的指示给解码器,表示display set已经传输完成。虽然没有什么实际用处,但这个segment是一定需要的。

上面解析出来的相关数据,通过DirectFB就可以显示出subtitle了。

三、 DVB subtitle system Overview

CLUT   color look up table 颜色索引表,CLUT_id存在于每个region中,用于将object中的颜色伪码转换成实际显示的颜色。

objects 的使用和配置是由region compositon segment定义的

regions的使用和配置 是由 page compositon segment定义的,page compositon segment 包含了一系列供显示的region,每个都有特定的位置。此外,region可以被定义而不被使用。

同一时间可以显示不止一个region。

一个subtitle stream可以传输多个subtitle service(eg,不同语言,或者normal/hearing impaired)。

在这种情况下,每个特定的subtitle service是用page-id来区分的。

在同一个subtitle stream中不同subtitle service之间可以共享subtitling data。

当然更多的情况是用不同的PID区分不同的subtitle service。

Figure 2: Example of two ways of conveying dual language subtitles (one using shared data)

图表2的情况是没有ancillary page的,看来只有同一个stream中有多个service时才会用到此辅助页面。

注意:

一个单独sbutitle stream是不能同时有高清(包含display_definition_segment)和标清的sbutitle service.

在一个subtitle stream中,一个page id 是分配给每个segment。Segments可以只包含特定的service  的数据,也可以包含用于共享service的数据。故,一个stream中的数据段最多有两种paige id values;

一种page id value 指定的数据段用于特定数据 (composioning page id) 这是必选

另一种paage id value 指定共享数据段 (ancillary page id),可选(包含log数据)。  可参考上图。

Display set

一个subtitle service中具有相同PTS的整套segments就成为 display set。 Display set中的最后一个段必须跟着一个 end_of-display-set 段,表示没有更多的与此PTS相关的subtitle 数据了。

Segment type

1.display definition segment    可选,用户定义display size  针对高清subtitle

2.page composiont segment     定义接下来显示的pages,每个page包含的regions,time-out信息 和页面状态

3.region composition segment    一个region里有一个或多个objects,但只有一个CLUT(由CLUT-id指定);带有region的配置,region的属性:水平,垂直的size,背景色,

4.CLUT definition segment

5.object data segment  有两种类型的object:绘图objects和文本objects

6.end of display set segment

段数据中的 page id value应当与 subtitle descriptor中的compositon_page_id 或者ancillary_page_id相等。

Page compostions 是不能由复合subtitle services所共享的

每个page composition 段中的page id 应当与compositon_page_id相等

总的来说,subtitle system中的数据层次结构如下:

  1. Transport Stream  (TS)
  2. Transport packets with the same PID
  3. PES packets,with PTSs providing timing information
  4. Subtitle service
  5. Segments signalled by the compositon page id and optionally the ancillary page id
  6. Where appropriate,a display definition segment

Subtitle data,containing information on page composition,region composition,CLUTs,objects and end of display set.

时间: 2024-11-10 04:21:46

DVB-subtitle解析流程浅的相关文章

HTML页面加载和解析流程详细介绍

浏览器加载和渲染html的顺序.如何加快HTML页面加载速度.HTML页面加载和解析流程等等,在本文将为大家详细介绍下,感兴趣的朋友不要错过 浏览器加载和渲染html的顺序 1. IE下载的顺序是从上到下,渲染的顺序也是从上到下,下载和渲染是同时进行的. 2. 在渲染到页面的某一部分时,其上面的所有部分都已经下载完成(并不是说所有相关联的元素都已经下载完). 3. 如果遇到语义解释性的标签嵌入文件(JS脚本,CSS样式),那么此时IE的下载过程会启用单独连接进行下载. 4. 样式表在下载完成后,

webrtc音视频解析流程分析

webrtc音视频解析流程包括多个线程: 1. rtp网络流接收线程(rtp stream reciever thread) 2. 音视频解码线程(decode thread) 3. 渲染线程(render thread) rtp网络流接收线程(rtp stream reciever thread): 接收网络rtp包,解析rtp包,得到音视频数据包.将解析出的rtp包,加入到RtpStreamReceiver::frame_buffer_中或最终加入VCMReceiver::jitter_bu

用户访问网页流程、DNS 解析流程

一.用户访问流程 二.DNS解析流程 DNS( Domain Name System)是"域名系统"的英文缩写,是一种组织成域层次结构的计算机和网络服务命名系统,它用于 TCP/IP 网络,它所提供的服务是用来将主机名和域名转换为 IP 地址的工作.俗话说,DNS 就是将网址转化为对外的 IP 地址. 第一步:浏览器会检查缓存中有没有这个域名对应的解析过的IP地址,如果有,该解析过程将会结束.浏览器缓存域名也是有限制的,包括缓存的时间.大小,可以通过 TTL 属性来设置. 第二步:如果

HTML页面加载和解析流程

浏览器加载和渲染html的顺序1. IE下载的顺序是从上到下,渲染(就是把请求的内容显示到浏览器屏幕上)的顺序也是从上到下,下载和渲染是同时进行的. 2. 在渲染到页面的某一部分时,其上面的所有部分都已经下载完成(并不是说所有相关联的元素都已经下载完). 3. 如果遇到语义解释性的标签嵌入文件(JS脚本,CSS样式),那么此时IE的下载过程会启用单独连接进行下载. 4. 样式表在下载完成后,将和以前下载的所有样式表一起进行解析,解析完成后,将对此前所有元素(含以前已经渲染的)重新进行渲染. 5.

Android中measure过程、WRAP_CONTENT详解以及xml布局文件解析流程浅析(上

                                                                                                                                               本文原创, 转载请注明出处:http://blog.csdn.net/qinjuning 在之前一篇博文中<<Android中View绘制流程以及invalidate()等相关方法分析>>,简单的阐述

Android中measure过程、WRAP_CONTENT详解以及 xml布局文件解析流程浅析

转自:http://www.uml.org.cn/mobiledev/201211221.asp 今天,我着重讲解下如下三个内容: measure过程 WRAP_CONTENT.MATCH_PARENT/FILL_PARENT属性的原理说明 xml布局文件解析成View树的流程分析. 希望对大家能有帮助.- - 分析版本基于Android 2.3 . 1.WRAP_CONTENT.MATCH_PARENT/FILL_PARENT 初入Android殿堂的同学们,对这三个属性一定又爱又恨.爱的是使

【转】Android中measure过程、WRAP_CONTENT详解以及xml布局文件解析流程浅析(下)

转载请注明出处:http://blog.csdn.net/qinjuning 上篇文章<<Android中measure过程.WRAP_CONTENT详解以及xml布局文件解析流程浅析(上)>>中,我们 了解了View树的转换过程以及如何设置View的LayoutParams的.本文继续沿着既定轨迹继续未完成的job. 主要知识点如下:                 1.MeasureSpc类说明                 2.measure过程详解(揭秘其细节);   

模拟构建DNS解析流程

背景 DNS初步认识 ??DNS(Domain Name Server,域名系统)是互联网上作为域名和IP地址相互映射的一个分布式数据库,能够使用户方便的访问互联网,而不用去费力的记忆IP字符串.通过域名,最终可以得到该域名对应的IP地址的过程叫做域名解析.DNS协议运行在UDP洗衣上,使用的是53号端口.???? DNS的重要性以及高可用(DNS冗余) 重要性 ????DNS解析是目前互联网大多数应用的实际的寻址方式:域名技术的不断发展,和基于域名技术出现的新技术和新思路极大的丰富了互联网的应

BeanDefinition 解析流程【解析所有配置类】

BeanDefinition 解析流程 BeanDefinition 解析入口:ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry ConfigurationClassPostProcessor# /** * 从主配置类开始递归解析所有配置类,并注册相关的 BeanDefinition 到 BeanDefinitionRegistry 中 */ @Override public void postProcessBea