多多色-多人伦交性欧美在线观看-多人伦精品一区二区三区视频-多色视频-免费黄色视屏网站-免费黄色在线

國內(nèi)最全I(xiàn)T社區(qū)平臺 聯(lián)系我們 | 收藏本站
阿里云優(yōu)惠2
您當(dāng)前位置:首頁 > 互聯(lián)網(wǎng) > EMIPLIB庫分析一

EMIPLIB庫分析一

來源:程序員人生   發(fā)布時間:2016-06-04 14:32:00 閱讀次數(shù):2425次

通過分析feedbackexample例程來了解EMIPLIB庫。feedbackexample例子可以實現(xiàn)在本機(jī)播放1個wav文件。但這類播放不是簡單的調(diào)用本機(jī)播放API來實現(xiàn),而是利用EMIPLIB庫提供的RTP框架來完成。在不了解細(xì)節(jié)前,可以將這個進(jìn)程簡單理解成這樣:讀取本地的1個wav文件,然后將其打包成RTP,再將這些RTP發(fā)送到feedbackexample監(jiān)聽的1個UDP端口,feedbackexample再解析這些RTP包,并還原成可以播放的語音數(shù)據(jù)提交給本機(jī)聲卡。我通常在這類場合下使用這個例程。其他程序通過SIP協(xié)議注冊到1個語音交換平臺,只實現(xiàn)了SIP協(xié)議交互部份并未實現(xiàn)RTP。這時候,就能夠借用feedbackexample例程,只需將feedbackexample例程監(jiān)聽的端口號和對真?zhèn)€IP地址和端口號都設(shè)置成另外一個程序中SIP協(xié)議交互得到的端口號便可。也就是說,通過這樣兩個簡單的程序可以實現(xiàn)1個非常簡易的SIP軟電話。1個程序負(fù)責(zé)SIP信令,另外一個程序負(fù)責(zé)RTP語音傳輸。

這個例程很簡單,但也包括了足夠的信息去了解EMIPLIB的運(yùn)作細(xì)節(jié)。接下來看看這些代碼究竟是如何做到的。

這個例程的代碼非常簡單,只有3個額外的輔助函數(shù),和1個類。這3個輔助函數(shù)完全可以不用去了解,這個類也只是為了輸出1些日志信息而已并沒有增加其他功能性方面的代碼。那關(guān)注點就能夠落在main函數(shù)里了。進(jìn)入到main函數(shù)內(nèi),去除那些注釋后,真正有用的代碼從變量定義開始。

MIPTime interval(0.020); // We'll use 20 millisecond intervals. MIPAverageTimer timer(interval); MIPWAVInput sndFileInput; MIPSamplingRateConverter sampConv, sampConv2; MIPSampleEncoder sampEnc, sampEnc2, sampEnc3; MIPULawEncoder uLawEnc; MIPRTPULawEncoder rtpEnc; MIPRTPComponent rtpComp; MIPRTPDecoder rtpDec; MIPRTPULawDecoder rtpULawDec; MIPULawDecoder uLawDec; MIPAudioMixer mixer; MyChain chain("Sound file player");

這些類型都是EMIPLIB庫提供的類。由于我們只是探究如何發(fā)送,所以這里列出來的這么多類型,我們只會關(guān)注1部份:

MIPTime MIPAverageTimer MIPWAVInput MIPSamplingRateConverter MIPSampleEncoder MIPULawEncoder MIPRTPULawEncoder MIPRTPComponent

未列出的那些類型都是解碼相干的類。還得再重點說說MyChain類。這是個繼承類,父類是MIPComponentChain。名稱中最后的單詞是Chain。Chain是鏈、鏈條的意思。這個類是EMIPLIB運(yùn)行時框架的核心類。是它將各個類組合在1塊,記錄像互關(guān)系,調(diào)和各個類的履行前后順序等。稍后我們將詳細(xì)研究這個類的源碼。

接下來的代碼就是初始化這些變量。中間會出現(xiàn)1些RTP開頭的類:

RTPSession RTPUDPv4TransmissionParams RTPSessionParams

這些是另外一個庫jrtplib提供的類。jrtplib庫也是EMIPLIB庫的底層基礎(chǔ)。這3個類的目的就是為了在網(wǎng)絡(luò)上傳輸接收RTP包。由于我們這次研究的是EMIPLIB,所以這些類就不細(xì)說了。
初始化完后,接著就是調(diào)用MIPComponentChain的addConnection操作這些變量。類似于這樣:

// Next, we'll create the chain returnValue = chain.setChainStart(&timer); checkError(returnValue, chain); returnValue = chain.addConnection(&timer, &sndFileInput); checkError(returnValue, chain);

注釋的意思是這是在創(chuàng)建鏈條。最后的啟動是調(diào)用MIPComponentChain的start。

chain.start();

main函數(shù)內(nèi)的代碼基本情況就是這樣。分3個部份:初始化變量,構(gòu)建1個鏈條,啟動這個鏈條。現(xiàn)在雖然沒有查看MIPTime和MIPWAVInput等類的聲明,基于鏈條的概念,和MIPComponentChain類名的暗示,可以猜想出之條件到的那些MIP開頭的類應(yīng)當(dāng)都是類似于MIPComponent類的子類。還有1個可以解釋成鏈條、任務(wù)流的地方是這些類名。聲明的順序是MIPWAVInput、MIPSamplingRateConverter、MIPSampleEncoder、MIPULawEncoder、MIPRTPULawEncoder和MIPRTPComponent。根據(jù)這個順序我們分明看到了1個清晰的將本地wav文件打包成RTP數(shù)據(jù)包的任務(wù)流程。這也是1個左證,MIPComponentChain是1個任務(wù)流履行體、任務(wù)鏈條。但這些畢竟是猜想,實際情況是否是正如猜想的那樣,還得分析了實現(xiàn)源碼再說。

MIPComponentChain

feedbackexample例程中使用了MIPComponentChain3個成員函數(shù):setChainStart、addConnection和start。通過它們的名稱我們可以立即知曉這3個函數(shù)的意圖。第1個應(yīng)當(dāng)是設(shè)置1個出發(fā)點。最后1個是啟動。第2個有點模糊,加1個connection。輸入?yún)?shù)是兩個Component(基于我們之前的猜想)。connection的意思是連接、聯(lián)系。這么來理解的話,這第2個函數(shù)的作用就是建立兩個component的關(guān)系?,F(xiàn)在都是猜想,接下來看代碼。

setChainStart

這個函數(shù)確切很簡單。就是將輸入?yún)?shù)賦值給m_pInputChainStart成員變量。

bool MIPComponentChain::setChainStart(MIPComponent *startComponent) { if (startComponent == 0) { setErrorString(MIPCOMPONENTCHAIN_ERRSTR_COMPONENTNULL); return false; } m_pInputChainStart = startComponent; return true; }

注意到1點,輸入?yún)?shù)的類型是MIPComponent。猜想是對的,而且名稱完全猜對了。

addConnection

這個也很簡單,就是將兩個MIPComponent放入1個list內(nèi)。inputConnections的類型是個std::list。之所以說簡單,是由于暫時疏忽后3個參數(shù)。

bool MIPComponentChain::addConnection(MIPComponent *pPullComponent, MIPComponent *pPushComponent, bool feedback, uint32_t allowedMessageTypes, uint32_t allowedSubmessageTypes) { if (pPullComponent == 0 || pPushComponent == 0) { setErrorString(MIPCOMPONENTCHAIN_ERRSTR_COMPONENTNULL); return false; } m_inputConnections.push_back(MIPConnection(pPullComponent, pPushComponent, feedback, allowedMessageTypes, allowedSubmessageTypes)); return true; }

start

bool MIPComponentChain::start() { if (JThread::IsRunning()) //如果已在運(yùn)行,不做其它操作,返回false。 {setErrorString(MIPCOMPONENTCHAIN_ERRSTR_THREADRUNNING);return false;} if (m_pInputChainStart == 0) //如果沒設(shè)置起始節(jié)點,不做其它操作,返回false。 {setErrorString(MIPCOMPONENTCHAIN_ERRSTR_NOSTARTCOMPONENT);return false;} std::list<MIPConnection> orderedList; std::list<MIPComponent *> feedbackChain; if (!orderConnections(orderedList)) return false; if (!buildFeedbackList(orderedList, feedbackChain)) return false; copyConnectionInfo(orderedList, feedbackChain); m_stopLoop = false; if (JThread::Start() < 0) {setErrorString(MIPCOMPONENTCHAIN_ERRSTR_CANTSTARTTHREAD);return false;} return true; }

起始處調(diào)用了JThread的IsRunning函數(shù)。難不成MIPComponentChain類繼承自JThread?查看這個類的頭文件。果然繼承自jthread類。JThread類也是由jrtplib庫提供。

class EMIPLIB_IMPORTEXPORT MIPComponentChain : private jthread::JThread, public MIPErrorBase

起始處的兩個判斷很容易理解。代碼中我已添加了注釋。接著是定義兩個std::list。然后是順次調(diào)用orderConnections、buildFeedbackLis和copyConnectionInfo這3個成員函數(shù)。最后是調(diào)用JThread::Start()函數(shù),這個函數(shù)很明顯目的就是啟動1個線程。在沒有具體分析orderConnections、buildFeedbackLis和copyConnectionInfo這3個成員函數(shù)前,對MIPComponnetChain::start函數(shù)的粗略認(rèn)識就是終究要啟動1個線程,但在啟動線程前要做1些準(zhǔn)備工作。那末接下來就看看到底在啟動線程前都做了些甚么。先看看orderConnections。

orderConnections

這個函數(shù)內(nèi)的第1步是遍歷m_inputConnections鏈表。之前這個鏈表在addConnection函數(shù)內(nèi)出現(xiàn)過?,F(xiàn)在可以回過頭再去看看,那是將兩個MIPComponent變量組合成1個MIPConnection變量,再放入m_inputConnections鏈表內(nèi)。在這里調(diào)用了MIPConnection的setMark函數(shù),應(yīng)當(dāng)是將內(nèi)部mark屬性置為false。具體有何目的現(xiàn)在還不知道。

for (it = m_inputConnections.begin() ; it != m_inputConnections.end() ; it++) (*it).setMark(false);

接著往下看。componentLayer是在函數(shù)起初處定義的,類型是std::list<MIPComponent *>。這是1個存儲MIPComponent指針的鏈表。m_pInputChainStart之前在setChainStart函數(shù)內(nèi)出現(xiàn)過,這是存儲起始節(jié)點的變量?,F(xiàn)在它被第1個放入了componentLayer鏈表內(nèi)。

componentLayer.push_back(m_pInputChainStart);

然后是1個while循環(huán)。為了看著方便,下面的代碼片斷去除源碼中具有的日志輸出語句。

while (!componentLayer.empty()) { std::list<MIPComponent *> newLayer; std::list<MIPComponent *>::const_iterator compit; for (compit = componentLayer.begin() ; compit != componentLayer.end() ; compit++) { for (it = m_inputConnections.begin() ; it != m_inputConnections.end() ; it++) { if (!(*it).isMarked()) // check that we haven't processed this connection yet { if ((*it).getPullComponent() == (*compit)) // check that this connection starts from the component under consideration { // mark the connection as processed (*it).setMark(true); // copy the connection in the ordered list orderedList.push_back(*it); // get the other end of the connection and add that // component to the new layer // we'll make sure that the component isn't already // in the list bool found = false; MIPComponent *component = (*it).getPushComponent(); std::list<MIPComponent *>::const_iterator compit2; for (compit2 = newLayer.begin() ; !found && compit2 != newLayer.end() ; compit2++) { if ((*compit2) == component) found = true; } if (!found) newLayer.push_back(component); } } } } componentLayer = newLayer; }

結(jié)束上面這個while循環(huán)的條件是componentLayer鏈表為空。這個循環(huán)之前的1句是將起始節(jié)點放入這個鏈表內(nèi)。也就是說,進(jìn)入循環(huán)前鏈表的初始狀態(tài)是有1個元素在鏈表內(nèi)。接著是兩個嵌套的for循環(huán)。循環(huán)的目的是將每一個componentLayer鏈表內(nèi)的元素取出后,再在m_inputConnections鏈表內(nèi)遍歷1次做些操作。第2層for循環(huán)的第1句的注釋很清楚表明了目的,檢查這個MIPConnection是不是被處理過。這也說明了這個函數(shù)內(nèi)第1步的目的:確保m_inputConnections鏈表內(nèi)所有的MIPConnection在處理前的處理標(biāo)志變量值都是“未處理”,也就是將mark置為false。

如果這個MIPConnection未被處理過,繼續(xù)下面的判斷,檢查這個MIPConnection的“pull componnet”是否是當(dāng)前正在檢查的MIPComponent。這個被檢查的MIPComponent是從componentLayer鏈表內(nèi)取出來的。由于初始時componentLayer內(nèi)只有初始節(jié)點,也就是說這兩個for循環(huán)的第1次迭代就是找到那個pull component是初始節(jié)點的MIPConnection。找到這個MIPConnection后將它的mark標(biāo)志置為true,也就是說這個MIPConnection已處理過了,后續(xù)再迭代時不要再處理它。然后將這個MIPConnection放入orderedList鏈表內(nèi)。orderedList也是在函數(shù)開始處定義的,類型是std::list<MIPConnection>,也是個鏈表,存儲的是MIPConnection變量。然后取出這個MIPConnection內(nèi)的“push component”,又進(jìn)入另外一個for循環(huán),目的是在newLayer鏈表內(nèi)尋覓是不是存在這個“push component”。如果newLayer內(nèi)不存在這個“push component”,那末將這個“push component”放入newLayer。履行完第1次兩個for循環(huán)的迭代后,再將newLayer賦值給componentLayer。此時,指的是第1次迭代,componentLayer內(nèi)應(yīng)當(dāng)只有1個元素,就是與起始節(jié)點組合成1個MIPConnection的“push component”。由于componentLayer不為空,所以while循環(huán)不會結(jié)束,繼續(xù)下1次迭代。第2次迭代的目的就是找到以這個“push component”為“pull component”的MIPConnection,然后設(shè)置mark標(biāo)志,再取出另外一個“push component”。至此,應(yīng)當(dāng)可以明白這個函數(shù)的目的了,就是以起始節(jié)點為開端找到這個任務(wù)流內(nèi)MIPConnection的履行順序,并順次放入orderedList內(nèi)。最后還有1個for循環(huán),目的是確保不再存在未被處理過的MIPConnection,如果有肯定是哪出錯了。最后將排過序的MIPConnection鏈表輸出到函數(shù)外。

由于最外圍的while循環(huán)肯定要在某種條件下結(jié)束。代碼里顯示結(jié)束的標(biāo)志是componentLayer鏈表為空,而每次循環(huán)最后1條語句是將newLayer變量賦值給componentLayer。也就是說while每次循環(huán)后如果newLayer為空則while循環(huán)結(jié)束。換種說法就是每次whille循環(huán)如果沒有找到下1個push component那末循環(huán)就結(jié)束了。依照這類思路倒推,在建立處理流時最后加入的MIPConnection變量里的push component1定應(yīng)當(dāng)是個空指針。這樣的1個MIPConnection就是在告知處理邏輯,處理流要結(jié)束了。

現(xiàn)在再回過頭去看start函數(shù)內(nèi)調(diào)用這1函數(shù)的目的就是排序m_inputConnections鏈表:從起始的MIPComponent節(jié)點開始,依照MIPConnection類給出的前后依承關(guān)系,理清這個任務(wù)流的順序?,F(xiàn)在已弄明白了start函數(shù)內(nèi)被調(diào)用的第1個函數(shù),接著看下1個buildFeedbackList。

buildFeedbackList

這個函數(shù)需要兩個參數(shù)。1個是之前分析過的orderConnections函數(shù)生成的排過序的MIPConnection鏈表。另外一個是輸出參數(shù),是個存儲MIPComponent指針的鏈表。
函數(shù)起始處是個for循環(huán)。目的依然是設(shè)置orderedList鏈表內(nèi)MIPConnection元素的已處理標(biāo)志。如果MIPConnection的成員函數(shù)giveFeedback返回false,那末設(shè)置MIPConnection已處理過,或說這個MIPConnection可以不用再處理了。想詳細(xì)看看這個giveFeedback的具體實現(xiàn)如何,所有就去看看了。MIPConnection類定義居然是在MIPComponentChain內(nèi)部。giveFeedback只是簡單的返回內(nèi)部成員變量m_feedback的值。這個成員變量值是在MIPConnection類生成時被賦的值。MIPConnection類都是在MIPComponentChain的addConnection函數(shù)內(nèi)被創(chuàng)建的。追溯到這里可以看到MIPConnection的m_feedback值的根源來自addConnection函數(shù)的feedback輸入?yún)?shù)。再檢查feedbackexample例程的代碼,幾近所有編碼進(jìn)程相干的MIPComponent的addConnection操作均沒有設(shè)置這個值,使用的是函數(shù)的缺省參數(shù)false。

chain.addConnection(&timer, &sndFileInput); chain.addConnection(&sndFileInput, &sampConv); chain.addConnection(&sampConv, &sampEnc); chain.addConnection(&sampEnc, &uLawEnc); chain.addConnection(&uLawEnc, &rtpEnc); chain.addConnection(&rtpEnc, &rtpComp);

但大部份所解碼進(jìn)程相干的MIPComponent的addConnection操作均設(shè)置了feedback輸入?yún)?shù),并且使用的都是true。

chain.addConnection(&rtpComp, &rtpDec); // This is where the feedback chain is specified: we want // feedback from the mixer to reach the RTP audio decoder, // so we'll specify that over the links in between, feedback // should be transferred. chain.addConnection(&rtpDec, &uLawDec, true); chain.addConnection(&uLawDec, &sampEnc2, true); chain.addConnection(&sampEnc2, &sampConv2, true); chain.addConnection(&sampConv2, &mixer, true); chain.addConnection(&mixer, &sampEnc3); chain.addConnection(&sampEnc3, &sndCardOutput);

中間還夾著著1小段注釋。感覺是解釋feedback機(jī)制的,但看不太懂。到現(xiàn)在還是不知道什么時候和為什么設(shè)置feedback標(biāo)志。接著看下面的代碼。接下來是1大段while。

while (!done) { bool found = false; it = orderedList.begin(); while (!found && it != orderedList.end()) { if (!(*it).isMarked() && (*it).giveFeedback()) found = true; else it++; } ............ }

這段while的最前部是又1個while循環(huán)。遍歷檢查orderedList鏈表,找到第1個未被處理過、且giveFeedback返回true的MIPConnection。如果找到了,就立即結(jié)束這部份處理。從大的while循環(huán)角度看,每次都得再做這么1次處理。也就是說,每次大的循環(huán)開始時,都得在orderedList鏈表內(nèi)找出1個未被處理過、且giveFeedback返回true的MIPConnection。如果有1次遍歷找不到滿足條件的MIPConnection,那末遍歷結(jié)束。

現(xiàn)在我們找到了1個需要處理的MIPConnection。下面列出的代碼去除1些注釋,重新做了些排布工作。首先是設(shè)置這個MIPConnection的處理標(biāo)志為true,下次就不會再找到它了。然后是清空subChain,這是個鏈表,存儲的是指向MIPComponent的指針。然后將這個正在處理的MIPConnection的“pull component”和“push component”順次放入這個subChain。

然后是另外一個while循環(huán)。這次遍歷是從剛才在orderedList中找到的那個MIPConnection的后1個MIPConnection開始。如果排在后面的MIPConnection的“pull component”等于這個被找到的MIPConnection的“push component”,這個排在后面的MIPConnection的處理標(biāo)志被置為true,且它的“push component”被放入subChain鏈表內(nèi)。但如果后面有兩個滿足這樣條件的MIPConnection,說明有錯。如果還需遍歷,會從在這次遍歷中找到的MIPConnection的后面1個MIPConnection開始,且startIt存的是這次遍歷中找到的MIPConnection。也就是說,subChain存的是那些MIPConnection feedback標(biāo)志為true的所有MIPComponent,且先放入“pull component”再放入“push component”。

while (!done) { ............ if (found) // ok, found a starting point, build the subchain { (*it).setMark(true); subChain.clear();subChain.push_back((*it).getPullComponent());subChain.push_back((*it).getPushComponent()); (*it).setMark(true); std::list<MIPConnection>::iterator startIt = it;bool done2 = false; while (!done2) { std::list<MIPConnection>::iterator nextStartIt; bool foundNextStartIt = false; it = startIt; it++; while (it != orderedList.end()) { if ( (*it).giveFeedback() ) { if ((*it).getPullComponent() == (*startIt).getPushComponent()) { if (foundNextStartIt) {setErrorString(MIPCOMPONENTCHAIN_ERRSTR_CANTMERGEFEEDBACK);return false;} foundNextStartIt = true; nextStartIt = it; subChain.push_back((*it).getPushComponent()); (*it).setMark(true); } } it++; } if ( !foundNextStartIt ) done2 = true; else startIt = nextStartIt; } // add the subchain to the feedbacklist in reverse if (!feedbackChain.empty()) feedbackChain.push_front(0); // mark new subchain std::list<MIPComponent *>::const_iterator it2; for (it2 = subChain.begin() ; it2 != subChain.end() ; it2++) feedbackChain.push_front(*it2); } else done = true;

在每次大的循環(huán)迭代處理過后,subChain存的是那些在這次迭代開始時在orderedList中找到的第1個feedback為true的MIPConnection的“pull component”,且以這個MIPComponent為出發(fā)點的子任務(wù)鏈。這個子任務(wù)鏈?zhǔn)侨咳蝿?wù)鏈的1小部份,它是整體的子集。這個子任務(wù)鏈的結(jié)束標(biāo)志有兩個,1個是到達(dá)了鏈尾,另外一個是遇到了1個feedback標(biāo)志為false的MIPConnection。我想這個處理進(jìn)程應(yīng)當(dāng)是想找到所有這樣的子鏈。這樣的存在有它本身的意義,現(xiàn)在還不得而知。

最后1部份有點意思。將某次迭代中找到的subChain內(nèi)元素按倒序方式放入feedbackChain鏈表內(nèi)。兩兩子鏈以1個0做為分割。最后將feedbackChain內(nèi)元素拷貝給函數(shù)的第2個參數(shù)feedbackComponentChain。

這個函數(shù)的目的略微清晰了點。找出1些子鏈,這些子鏈再按倒序放入1個鏈表內(nèi),子鏈間以0分隔。現(xiàn)在可以繼續(xù)start函數(shù)內(nèi)出現(xiàn)的最后1個函數(shù)了,copyConnectionInfo。

copyConnectionInfo 

可以說這個函數(shù)非常簡單1目了然。就是將之前兩個函數(shù)orderConnections和buildFeedbackList處理的結(jié)果保存在成員變量中。

void MIPComponentChain::copyConnectionInfo(const std::list<MIPConnection> &orderedList, const std::list<MIPComponent *> &feedbackChain) { std::list<MIPConnection>::const_iterator it; std::list<MIPComponent *>::const_iterator it2; m_chainMutex.Lock(); m_orderedConnections.clear(); m_feedbackChain.clear(); for (it = orderedList.begin() ; it != orderedList.end() ; it++) m_orderedConnections.push_back(*it); for (it2 = feedbackChain.begin() ; it2 != feedbackChain.end() ; it2++) m_feedbackChain.push_back(*it2); m_pInternalChainStart = m_pInputChainStart; m_chainMutex.Unlock(); }


 

至此,start函數(shù)內(nèi)牽涉到的3個成員函數(shù)都看了1遍。大致了解了他們的作用。這些都只是為了啟動線程前的準(zhǔn)備工作。我們知道start函數(shù)的終究目的是啟動1個線程。那末真正線程內(nèi)的處理都是些甚么呢,哪一個函數(shù)是做這件事的呢?掃1遍mipcomponentchain.cpp文件后發(fā)現(xiàn)Thread函數(shù)有點像。再進(jìn)入這個函數(shù)內(nèi)部看到起始處有這么1句日志輸出:

#ifdef MIPDEBUG std::cout << "MIPComponentChain::Thread started" << std::endl; #endif // MIPDEBUG

這應(yīng)當(dāng)毫無疑問就是線程的入口函數(shù)了。接下來就看看它到底做了些甚么。

 

Thread

大致看了1眼,再加上之前有過線程編碼的經(jīng)歷,可以推測出這個函數(shù)內(nèi)最主要的部份應(yīng)當(dāng)就是1個循環(huán)。但不多是個無窮循環(huán),肯定有退出機(jī)制。進(jìn)入循環(huán)前應(yīng)當(dāng)有1些初始化操作。

bool done = false;//感覺這句賦值沒必要,下面有1句賦值語句。 bool error = false;//初始值為false。 int64_t iteration = 1; std::string errorComponent, errorString; m_loopMutex.Lock(); done = m_stopLoop;//在啟動線程前,這個值被賦值為false。 m_loopMutex.Unlock(); JThread::ThreadStarted();//調(diào)用父類的函數(shù),這應(yīng)當(dāng)是JThread類的使用規(guī)范。 MIPSystemMessage startMsg(MIPSYSTEMMESSAGE_TYPE_WAITTIME);

上面這些初始化操作大部份都很好理解。只是又多了1個之前未遇到過的類:MIPSystemMessage。既然出現(xiàn)了,那就看看他的定義。這個類的頭文件里有1段描寫這個類作用的文字。

/** A system message. * This kind of message is used to instruct a component to wait until messages can be * distributed in the chain or to inform a component that an interval has elapsed. */

大致的意思是說,這個類的作用是消耗掉特定時長。有點類似于多線程編程中常常使用的Sleep函數(shù),只不過在這將其封裝成了類。同時還發(fā)現(xiàn),MIPSystemMessage繼承自MIPMessage。MIPMessage類也有1段解釋文字。從中可以看出,MIPMessage是個基類。MIPMessage消息將會從1個MIPComponent傳遞給另外一個MIPComponent。

/** Base class of messages passed in a MIPComponentChain instance. * This is the base class of messages distributed in a MIPComponentChain * instance. Messages are distributed from component to component using * the MIPComponent::pull and MIPComponent::push functions. The * message type numbers can be found in mipmessage.h */

這同時也說明了鏈條中各個MIPComponent之間是如何通訊的。同時,也應(yīng)當(dāng)注意到MIPComponentChain會生成1個子類型是MIPSYSTEMMESSAGE_TYPE_WAITTIME的MIPSystemMessage對象,并提供給鏈條中的第1個MIPComponent。也就是說,任何1個可以充當(dāng)鏈條中第1個元素的MIPComponent必須可以處理子類型是MIPSYSTEMMESSAGE_TYPE_WAITTIME的MIPSystemMessage對象。以上就是循環(huán)前的初始化操作。接下來進(jìn)入循環(huán)內(nèi)部。此循環(huán)大致由3部份組成。1,針對鏈條初始節(jié)點的操作。2,遍歷m_orderedConnections鏈表。3,遍歷m_feedbackChain鏈表。這3步中任何1步履行完后都會檢查毛病標(biāo)志,用來判斷是不是需要立即結(jié)束循環(huán)。

第1階段。

第1步是調(diào)用初始節(jié)點的push函數(shù)。向其傳入初始化階段生成的startMsg變量。其他加鎖操作就不做分析了。

MIPTime::wait(MIPTime(0,0)); m_chainMutex.Lock(); m_pInternalChainStart->lock(); if (!m_pInternalChainStart->push(*this, iteration, &startMsg)) { error = true; errorComponent = m_pInternalChainStart->getComponentName(); errorString = m_pInternalChainStart->getErrorString(); m_pInternalChainStart->unlock(); m_chainMutex.Unlock(); break; } m_pInternalChainStart->unlock();

push操作如果失敗就結(jié)束全部循環(huán),也就是說結(jié)束線程。這1步最關(guān)鍵的1點就是push操作都做了哪些事。必須以1個具體的實例來做說明才能對這1步有清晰的認(rèn)識。正好再回到feedbackexample例程中代碼結(jié)合著來分析。在例程中設(shè)置的初始節(jié)點類型是MIPAverageTimer。打開mipaveragetimer.h文件看看這個類是如何定義的。下面這段內(nèi)容很有幫助。

/** A simple timing component. * This is a simple timing component which accepts MIPSYSTEMMESSAGE_WAITTIME system * messages. It generates a MIPSYSTEMMESSAGE_ISTIME system message each time the * specified interval has elapsed. Note that this is only on average after each interval: * fluctuation will be present. */

意思是接收MIPSYSTEMMESSAGE_WAITTIME消息,產(chǎn)生MIPSYSTEMMESSAGE_ISTIME消息。產(chǎn)生消息前必須是經(jīng)過了多少秒后。再看看push和pull兩個函數(shù)的實現(xiàn)。由于這個類也是繼承自MIPComponent。所謂的“接收MIPSYSTEMMESSAGE_WAITTIME消息”是在push函數(shù)內(nèi)實現(xiàn)的。push函數(shù)內(nèi)會判斷輸入的MIPMessage變量類型是不是滿足要求。其次,push函數(shù)內(nèi)還實現(xiàn)了“經(jīng)過了多少秒后”,可以在源碼中看到下面這句:

MIPTime::wait(MIPTime(diff));

diff的值由下面方法生成:

MIPTime curTime = MIPTime::getCurrentTime(); real_t diff = (m_startTime.getValue()+((real_t)iteration)*m_interval.getValue())-curTime.getValue();

m_interval的值在創(chuàng)建MIPAverageTimer時指定,例程中給出的值是0.02。m_startTime的值在創(chuàng)建MIPAverageimer時指定,實際值是創(chuàng)建時的系統(tǒng)時間。iteration是迭代值,每次Thread的for循環(huán)履行1次累加這個值,并傳給所有的MIPComponent對象。curTime是MIPAverageTimer的push函數(shù)被調(diào)用時的時間。diff值的含義是,如果每次迭代都消耗了0.02秒,那末diff值就是這次push被調(diào)用時的時間與理想情況下應(yīng)當(dāng)被消耗的時間的差值。如果這個值大于零,說明實際情況是之前的處理有些快需要減慢,所以就休眠1段時間。
現(xiàn)在再回到第1部份的處理場景中。加上之前針對MIPAverageTimer類的分析。現(xiàn)在能夠明白這第1部份都做了些甚么:履行了MIPAverageTimer的push函數(shù)。startMsg變量正是push函數(shù)所需要的類類型。由于滿足了這些條件,所以push函數(shù)內(nèi)還履行了休眠操作。也就是說,這第1部份的真正作用就是休眠了固定時長。也就是說每次迭代的第1步都是休眠固定時長。

第2階段/
接著看第2步:遍歷m_orderedConnections。現(xiàn)在我們知道這個鏈表存儲的是1個有序的MIPConnectiont集合。其實也是有序的MIPComponent集合。由于前1個MIPConnection的pull component就是后1個MIPConnection的push component。

for (it = m_orderedConnections.begin() ; !error && it != m_orderedConnections.end() ; it++) { MIPComponent *pPullComp = (*it).getPullComponent(); MIPComponent *pPushComp = (*it).getPushComponent(); uint32_t mask1 = (*it).getMask1(); uint32_t mask2 = (*it).getMask2(); pPullComp->lock(); pPushComp->lock(); MIPMessage *msg = 0; do { if (!pPullComp->pull(*this, iteration, &msg)) {error = true;errorComponent = pPullComp->getComponentName();errorString = pPullComp->getErrorString();} else { if ( msg ) { uint32_t msgType = msg->getMessageType();uint32_t msgSubtype = msg->getMessageSubtype(); if ( ( msgType&mask1 ) && ( msgSubtype&mask2 ) ) { if ( !pPushComp->push(*this, iteration, msg) ) {error = true;errorComponent = pPushComp->getComponentName();errorString = pPushComp->getErrorString();} } } } } while (!error && msg); pPullComp->unlock(); if (pPushComp->getComponentPointer() != pPullComp->getComponentPointer()) pPushComp->unlock(); }

每次遍歷都會取出1個MIPConnection,目的是得到這個MIPConnection的pull component和push component。然后每次從pull component中取出1個MIPMessage再提供給push component。結(jié)束某1個MIPConnection的處理條件是沒法再從pull component取出MIPMessage,或出錯了。我們模糊能感覺到消息在全部處理鏈條中被傳遞的進(jìn)程。

第3階段。
現(xiàn)在進(jìn)入第3步,feedback鏈條。處理feedback鏈條會用到1個類MIPFeedback。應(yīng)當(dāng)還記得,m_feedbackChain存儲的是多個feedback鏈條,鏈條間以空指針分隔。所以在遍歷處理進(jìn)程中也看到了針對這個情況的處理。每次重新1個新的子鏈條的處理前要重置feedback變量。除此以外每次遍用時,都是調(diào)用鏈條中MIPComponent的processFeedback函數(shù)。processFeedback函數(shù)接收的參數(shù)中就包括MIPFeedback類型的變量。之前已提到過,每次1個新的子鏈條處理前都會重置MIPFeedback變量。也就是說,每一個子鏈條內(nèi)MIPComponent間傳遞消息是通過MIPFeedback類。目前為止還只是猜想。接著看MIPFeedback類的說明。

/** Message passed through a feedback chain. * A MIPFeedback object is used in a feedback chain of a MIPComponentChain instance. * Each object in the same chain can inspect and/or modify the information in the * MIPFeedback instance. */

這段關(guān)于類用處的注釋左證了我們之前的猜想。第3部份的框架其實很簡單。處理m_feedbackChain內(nèi)保存的每一個feedback鏈條。鏈條內(nèi)MIPComponent間通過MIPFeedback傳遞消息。但是,EMIPLIB庫設(shè)計這個feedback鏈條的目的是甚么依然不清晰。

通過分析例程代碼,和庫源碼,大致了解了該如何使用EMIPLIB庫和EMIPLIB庫內(nèi)部實現(xiàn)。現(xiàn)在知道了為了向網(wǎng)絡(luò)的1個端點發(fā)送語音,該使用哪些MIPCompnent組件,該如何建立這些組件間在運(yùn)行期間的關(guān)系。同時,也知曉在運(yùn)行期EMIPLIB將會創(chuàng)建1個線程在后臺履行語音數(shù)據(jù)的轉(zhuǎn)換和發(fā)送?;镜倪M(jìn)程是這樣的。先創(chuàng)建1些語音數(shù)據(jù)轉(zhuǎn)換和發(fā)送用的MIPCompnent組件。然后初始化這些組件實例。接著創(chuàng)建1個MIPComponentChain類實例。指定1個初始MIPComponent節(jié)點。接下來依照順序加入MIPComponeng組件實例。最后1步就是調(diào)用MIPComponentChain類的start函數(shù)。

經(jīng)過上述的分析,現(xiàn)在知道了該如何啟動1個將本地文件打包成RTP并發(fā)送給特定網(wǎng)絡(luò)地址端口的進(jìn)程??梢栽谌问裁磿r候間暫停或停止這樣1個進(jìn)程嗎?我們再看看MIPComponentChain類的源碼,是不是存在1個這樣的成員函數(shù)。發(fā)現(xiàn)還有這樣幾個成員函數(shù)未仔細(xì)分析:

<pre class="cpp" name="code"><pre class="cpp" name="code">stop() rebuild() clearChain() deleteConnection


從函數(shù)名稱中可以大致知曉這幾個成員函數(shù)的用處。沒有找到暫停這樣1個進(jìn)程的函數(shù),只是找到了結(jié)束這樣1個進(jìn)程的函數(shù),stop。這4個函數(shù)的內(nèi)部邏輯都非常簡單,就不逐一羅列了。除stop函數(shù)外,其他3個函數(shù)都未在類內(nèi)部被調(diào)用過。

 

現(xiàn)在已了解到,MIPComponentChain類在EMIPLIB庫中所處的核心位置。這個類實現(xiàn)了EMIPLIB庫的處理框架。它負(fù)責(zé)協(xié)同調(diào)用各個MIPComponent組件。MIPComponent組件間通過MIPMessage消息類傳遞信息。這篇分析文章只是揭露了EMIPLIB在高層是如何運(yùn)作的。接下來有必要深入到具體的MIPComponent類源碼中1探究竟。





 

生活不易,碼農(nóng)辛苦
如果您覺得本網(wǎng)站對您的學(xué)習(xí)有所幫助,可以手機(jī)掃描二維碼進(jìn)行捐贈
程序員人生
------分隔線----------------------------
分享到:
------分隔線----------------------------
關(guān)閉
程序員人生
主站蜘蛛池模板: 午夜视频免费观看 | 成人免费视频网 | 国产69精品久久久久999 | 免费在线观看成年人视频 | 亚洲视频免费在线播放 | freexxxhd性| 性欧美18videos| 久久久久综合国产 | 亚洲 欧美 精品 | 欧美亚洲另类在线 | 尤物免费在线视频 | 中文字幕123 | 欧洲一区 | 视频一区二区精品的福利 | 亚洲国产成人久久 | 久久免费视频一区 | 欧美日韩一区二区三区视频播 | 国内自拍 亚洲系列 欧美系列 | 国产精品一区二区三区免费视频 | 手机福利视频一区二区 | 亚洲综合激情另类小说区 | 亚洲图片一区二区三区 | 亚洲www在线 | 韩国av片永久免费 | 久久性生活视频 | 亚洲视频自拍 | 99欧美在线 | 免费一看一级欧美 | 欧欧美18videosex性哦欧美美 | 国产成人影院在线观看 | 亚洲欧美天堂网 | 影院成人区精品一区二区婷婷丽春院影视 | freexx性| 天天拍久久 | 小说 都市 欧美 亚洲 | 在线亚洲日产一区二区 | 伊人365| 日韩精品欧美激情亚洲综合 | 亚洲精品久久99久久一区 | 成人男女啪啪免费观看网站 | 91精品欧美综合在线观看 |