交叉編譯sip開源包案例幾則

  一直沒空仔細研究下oSIP,最近看到其版本已經到了3.x版本,看到網上的許多幫助說明手冊都過於陳舊,且很多文檔內容有點誤人子弟的嫌疑~~
  Linux下oSIP的編譯使用應該是很簡單的,其Install說明文檔裏也介紹的比較清楚,本文主要就oSIP在Windows平臺下VC6.0開發環境下的使用作出描述。
  雖然oSIP的開發人員也說明了,oSIP只使用了標準C開發庫,但許多人在Windows下使用oSIP時,第一步就被卡住了,得不到oSIP的LIB庫和DLL庫,也就沒有辦法將oSIP使用到自己的程序中去,所以第一步,我們將學習如何得到oSIP的靜態和動態鏈接庫,以便我們自己的程序能夠使用它們來成功編譯和執行我們的程序。


第一階段:
------------------------------------------------------
  先創建新工程,網上許多文檔都介紹創建一個Win32動態鏈接庫工程,我們這裏也一樣,創建一個空白的工程保存。
  同樣,將oSIP2版本3.0.1 src目錄下的Osipparser2目錄下的所有文件都拷到我們剛創建的工程的根目錄下,在VC6上操作:
       Project-Add To Project-Files
  將所有的源程序和頭文件都加入到工程內,保存工程。
  這時,我們可以嘗試編譯一下工程,你會得到許多錯誤提示信息,其內容無非是找不到osipparser2/xxxxx.h頭文件之類。
  處理:在Linux下,我們一般是將頭文件,lib庫都拷到/usr/inclue;/usr/lib之類的目錄下,c源程序裏直接寫#include <xxx.h>時,能直接去找到它們,在VC裏,同樣的,最簡單的方法就是將oSIP2源碼包中的Include目錄下的osipparser2目錄直接拷到我們的Windows下默認包含目錄即可,這個目錄在VC6的Tool-Options-Directories裏設置,(當然,如果你知道這一步,也可以不用拷貝文件,直接在這裏把oSIP源碼包所在目錄加進來就可以了),默認如果裝在C盤,目錄則爲C:\Program Files\Microsoft Visual Studio\VC98\Include。
  這時,我們再次編譯我們的工程,順利編譯,生成osipparser2.dll,這時,網上很多文檔裏可能直接就說,這一步也會生成libs目錄,裏面裏osipparser2.lib文件,但我們這裏沒有生成:)
  最簡單的方法,不用深究,直接再創建一個工程,同上述創建動態鏈接庫方法,創建一個Win32靜態鏈接庫工程,直接編譯,即可得到osipparser2.lib。
------------------------------------------------------
  上面,我們得到了Osip的解析器開發庫,下面再編譯完整的Osip協議棧開發庫,同樣照上述方法,分別創建動態鏈接庫工程和靜態鏈接庫工程,只是要拷的文件換成src下的osip目錄下文件和include下的osip目錄,得到osip2.dll和osip2.lib。
  在編譯osip2.dll這一步可能會再次得到錯誤,內容含義是找不到鏈接庫,所以,我們要把前面編譯得到的osipparser2.lib也拷到osip工程目錄下,並在VC6中操作:
  Project-Setting-Link中的Object/Library Modules:
       kernel32.lib user32.lib ... xxx.lib之類的內容最後增加: osipparser2.lib
  保存工程後再次編譯,即可成功編譯osip2.dll。
------------------------------------------------------
  至此,我們得到了完整的oSIP開發庫,使用時,只需在我們的程序裏包含oSIP的頭文件,工程的鏈接參數裏增加osipparser2.lib和osip2.lib即可。
------------------------------------------------------
  下面我們驗證一下我們得到的開發庫,並大概瞭解一下OSIP的語法規範。
  在VC裏創建win32控制檯程序工程,將libosip源碼包的SRC目錄下的Test目錄內的C源程序隨便拷一個到工程時,直接編譯(工程設置裏照前文方法在link選項裏增加osip2.lib,osipparser2.lib引用我們之前成功編譯得到的靜態庫文件)就可以運行(帶參數運行,參數一般爲一個文本文件,同樣從Test目錄的res目錄裏拷一個與源文件同名的純文本文件到工程目錄下即可)。
  該目錄下的若干文件基本上是測試了Osip的一些基本功能函數,例如URI解析之類,可以大概瞭解一下oSIP的語法規範和調用方法,同時也能校驗一下之前編譯的OSIP開發庫能否正常使用,成功完成本項工作後,可以進入下一步具體的oSIP的使用學習了。
------------------------------------------------------
  由於oSIP是比較底層的SIP協議棧實現,新手較難上手,而官方的示例大都是一些僞代碼,需要有實際的例子程序參考學習,而最好的例子就是同樣官方發佈的oSIP的擴展開發庫exosip2,使用exoSIP可以很方便地快速創建一個完整的SIP程序(只針對性地適用於SIP終端開發用,所以我們這裏只是用它快速開發一個SIP終端,用來更方便地學習oSIP,要想真正掌握SIP的開發,需要掌握oSIP並熟讀RFC文檔才行,exoSIP不是我們的最終學習目的),通過成功編譯運行一個自己動手開發出的程序,再由淺入深應該是初學都最好的學習方法通過對使用exosip開發庫的使用創建自己的SIP程序,熟悉後再一個函數一個函數地深入學習exosip提供的接口函數,就可以深入理解osip 了,達到間接學習oSIP的目的,同時也能從eXoSIP中學習到正確使用oSIP的良好的編程風格和語法格式。
  而要成功編譯ExoSIP,似乎許多人被難住了,直接在XP-sp2上,用VC6,雖然你使用了eXoSIP推薦的winsock2.h,但是會得到一個sockaddr_storage結構不能識別的錯誤,因爲vc6自帶的開發庫太古董了,需要升級系統的Platform SDK,下載地址如下:
http://www.microsoft.com/msdownl... PSP2FULLInstall.htm(VC6的支持已經停止,這是VC6能使用的最新SDK)
  成功安裝後編譯前需加OSIP_MT宏,以啓用線程庫,否則在程序中使用eXoSIP庫時會出錯,而編譯時也會得到許多函數未定義的Warning提示,編譯得到exosip2.lib供我們使用,當然,在此之前需要成功編譯了osip2和osipparser2,而在之後的實際使用時,發現oSIP也需要增加OSIP_MT宏,否則OSIP_MT調用oSIP的線程庫時會出錯,所以我們需要重新編譯oSIP了:),因爲eXosip是基於oSIP的(同上方式創建靜態和動態鏈接庫工程,並需在Link中手工添加oSIP和oSIPparser的lib庫)。
------------------------------------------------------
  創建新工程,可以是任意工程,我們從最簡單的Win32控制檯程序開始,爲了成功使用oSIP,我們需要引用相關庫,調用相關頭文件,經過多次試驗,發現需要引用如下的庫:
         exosip2.lib osip2.lib osipparser2.lib WSock32.Lib IPHlpApi.Lib WS2_32.Lib Dnsapi.lib
  其中,除了我們上面編譯得到的三個oSIP庫外,其它庫都是系統庫,其中有一些是新安裝的Platform SDK所新提供的。
  至此,我們有了一個簡單的開發環境了,可以充分利用網上大量的以oSIP爲基礎的代碼片段和官方說明文檔開始具體函數功能的測試和使用了:)
------------------------------------------------------
  我們先進行一個簡單的純SIP信令(不帶語音連接建立)的UAC的SIP終端的程序開發的試驗(即一個只能作爲主叫不能作爲被叫的的SIP軟電話模型),我們創建一個MFC應用程序,對話框模式,照上面的說明,設置工程包含我們上面得到的oSIP的相關開發庫及SDK的一些開發庫,並且由於默認LIBC的衝突,需要排除MSVCRT[D]開發庫(其中D代表Debug模式下,沒有D表示Release模式下),直接使用eXosip的幾個主要函數就可以創建一個基本的SIP軟電話模型。

  其主要流程爲:
  初始化eXosip庫-啓動事件監聽線程-向SIP Proxy註冊-向某SIP終端(電話號碼)發起呼叫-建立連接-結束連接

  初始化代碼:
        int ret = 0;
        ret = eXosip_init ();
        eXosip_set_user_agent("##YouToo0.1");
        if(0 != ret)
        {
                AfxMessageBox("Couldn't initialize eXosip!\n");
                return false;
        }
        ret = eXosip_listen_addr (IPPROTO_UDP, NULL, 0, AF_INET, 0);
        if(0 != ret)
        {
                eXosip_quit ();
                AfxMessageBox("Couldn't initialize transport layer!\n");
                return false;
        }

  啓動事件監聽線程:
        AfxBeginThread(sip_uac,(void *)this);

  向SIP Proxy註冊:
        eXosip_clear_authentication_info();
        eXosip_add_authentication_info(uname, uname, upwd, "md5", NULL); 
        real_send_register(30);  /* 自定義函數代碼請見源碼 */

  發起呼叫(構建假的SDP描述,實際軟電話使用它構建RTP媒體連接):
        osip_message_t *invite = NULL;  /* 呼叫發起消息體 */
        int i = eXosip_call_build_initial_invite (&invite, dest_call, source_call, NULL, "## YouToo test demo!");
        if (i != 0)
        {
                AfxMessageBox("Intial INVITE failed!\n");
        }
        char localip[128];
        eXosip_guess_localip (AF_INET, localip, 128);
        snprintf (tmp, 4096,
                "v=0\r\n"
                "o=josua 0 0 IN IP4 %s\r\n"
                "s=conversation\r\n"
                "c=IN IP4 %s\r\n"
                "t=0 0\r\n"
                "m=audio %s RTP/AVP 0 8 101\r\n"
                "a=rtpmap:0 PCMU/8000\r\n"
                "a=rtpmap:8 PCMA/8000\r\n"
                "a=rtpmap:101 telephone-event/8000\r\n"
                "a=fmtp:101 0-11\r\n", localip, localip, "9900");
        osip_message_set_body (invite, tmp, strlen(tmp));
        osip_message_set_content_type (invite, "application/sdp");
        eXosip_lock ();
        i = eXosip_call_send_initial_invite (invite);
        eXosip_unlock ();                               

  掛斷或取消通話:
        int ret;
        ret = eXosip_call_terminate(call_id, dialog_id); 
        if(0 != ret)
        {
                AfxMessageBox("hangup/terminate Failed!");
        }

  可以看到非常簡單,再藉助於oRTP和Mediastreamer開發庫,來快速爲我們的SIP軟電話增加RTP和與系統語音API接口交互及語音編碼功能,即可以快速開發出一個可用的SIP軟電話,關於oRTP和Mediastreamer的相關介紹不是本文重點,將在有空的時候考慮增加相應使用教程,文章前提到的地方可以下載基本可用的完整SIP軟電話的VC源碼工程文件供參考使用,完全CopyLeft,歡迎轉載,但請在轉載時註明作者信息,謝謝!

第二階段:
---------------------------------------------------
  得到了一個SIP軟電話模型後,我們可以根據軟電話的實際運行表現(結合用Ethereal抓包分析)來進行代碼的分析,以達到利用eXoSIP來輔助我們學習oSIP的最終目的(如要快速開發一個可用的SIP軟電話,請至前面提到的論壇去下載使用oRTP和Mediastreamer快速搭建的一個基本完整可用的SIP軟電話##YouToo 0.1版本的VC源碼工程文件作參考)。

  現在從eXosip的初始化函數開始入手,來分析oSIP的使用,這是第二階段,第三階段就是深入學習oSIP的源碼了,但大多數情況下應該沒有必要了,因爲在第二階段就有部分涉及到第三階段的工作了,而且oSIP的源碼也就大多是一些SIP數據的語法解析和狀態機的實現,能深入理解了SIP協議後,這些只是一種實現方式,沒必要完全去接受,而是可以用自己的方式和風格來實現一套,比如,更輕量化更有適用目的性的方式,oSIP則只起參考作用了。

  eXosip_init()是eXosip的初始化函數,我們來看看它的內部實現:
  首行是定義的 osip_t *osip,這在oSIP的官方手冊裏我們看到,所有使用oSIP的程序都要在最開始處聲明一個osip_t的指針,並使用osip_init(&osip)來初始化這個指針,銷燬這個資源使用osip_release(osip)即可。
  我們可以在代碼中看到很多OSIP_TRACE,這是調試輸出宏調用了函數osip_trace,可以用ENABLE_TRACE宏來打開調試以方便我們開發調試。
  其它就是很多的eXosip_t的全局變量eXosip的一些初始化操作,包括最上面的memset (&eXosip, 0, sizeof (eXosip))完全清空和下面的類似eXosip.user_agent = osip_strdup ("eXosip/" EXOSIP_VERSION)的exosip變量的一些初始值設置,其中有一個eXosip.j_stop_ua = 0應該是一個狀態機開關,後面可以看到很多代碼檢測這個變量來決定是否繼續流程處理,默認置成了0表示現在exosip的處理流程是就緒的,即ua是not stop的。
  
  osip_set_application_context (osip, &eXosip)是比較有意思的,它讓下面的eXosip_set_callbacks (osip)給osip設置大量的回調函數時,能讓osip能訪問到eXosip這個全局變量中設置的大量程序運行時交互的信息,相當於我們在VC下開啓一個線程時,給線程傳入的一個void指針指向我們的MFC應用程序的當前dialog對象實例,可以用void *osip_get_application_context (osip_t * osip)這個函數來取出指針來使用,不過好象exosip中並沒有用到它,可能是留給個人自已擴展的吧:)
  
  還能看到初始化代碼前面有一段WIN32平臺下的SOCK的初始化代碼,可以知道eXosip是用的原生的winsock api函數,也就是我們可能以前學過的用VC和WINAPI寫sock程序時(不是MFC),用到的那段SOCK初始代碼,還有一段有意思的代碼,就是jpipe()函數,它們返回的是一個管道,一個有2個整型數值的數組(一個進一個出),查看其代碼發現,非WIN32平臺是直接使用的pipe系統函數,而WIN32下則是用一對TCP的本地SOCK連接來模擬的管道,一個SOCK寫一個SOCK讀,這段代碼是比較有參考價值的:)
j = 50;
while (aport++ && j-- > 0)
{
  raddr.sin_port = htons ((short) aport);
  if (bind (s, (struct sockaddr *) &raddr, sizeof (raddr)) < 0)
  {
    OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_WARNING, NULL,
    "Failed to bind one local socket %i!\n", aport));
  } else
  break;
}
含義即,依次檢測50個端口,從static int aport = 10500;即10500~10550端口找出一個可用的本地端口來綁定listen模擬pipe的一對sock。
  eXosip_set_callbacks (osip)沒有什麼好看的,無非是和oSIP官方文檔介紹的一樣,設置一大堆的回調函數,關鍵是回調函數的實現,這也是許多初學者使用oSIP被卡殼的主要原因,不知道oSIP構建的程序是怎樣跑起來的,隨便選幾個回調函數看一下eXosip是怎樣實現的,有許多是形如下文的函數:
static void
cb_sndbye (int type, osip_transaction_t * tr, osip_message_t * sip)
{
  OSIP_TRACE (osip_trace
  (__FILE__, __LINE__, OSIP_INFO3, NULL, "cb_sndbye (id=%i)\r\n",
  tr->transactionid));
}
  即,只是打印一下調試,並沒有完整實現什麼功能,我們學習時,完全可以用相同的方法,定義一大堆回調函數,並不忙想怎麼完全實現,先都是隻打印一下調試信息,看具體的應用邏輯根據抓包測試分析和看調試看程序走到了哪一步,調用了哪一個回調,來明白具體回調函數要實現什麼用途,再來實現代碼就方便多了,當然,如果看透了RFC文檔,應該從字面就能知道各個回調函數的用途了,這是後話,不是誰都能快速完全看懂RFC的,所以我們要參考eXosip:)
  
  我們對其中的重要的回調函數進行逐個的分析:
  ---------------------------
  osip_set_cb_send_message (osip, &cb_snd_message) SIP消息發送回調函數
  這個函數可能是最重要的回調函數之一,消息發送,包括請求消息和迴應消息,一般情況下,狀態機的狀態就是由它控制的,發起一個消息初始化一個狀態機,迴應一個消息對狀態機修改,終結消息發送結束狀態機……
  看cb_snd_message的函數實現,要以發現,其主要代碼是對參數中的要發送的消息osip_message_t * sip進行分析,找出消息要發送的真實char *host,int port的值(這些參數可以省略,但要發送消息肯定需要host和port,所以要從sip中解析),最後根據sip中解析出的傳輸方式是TCP還是UDP選擇最終進行消息發送處理的函數cb_udp_snd_message,cb_tcp_snd_message處理(它們的參數一致,即本函數只是補全一些省略的參數並對消息進行合法性檢查)。
  **畢竟eXosip是一個通用的開發庫,它考慮了要支持TCP,UDP,TCPs,IPV4,IPV6,WIN32,*nix,WINCE等等多樣化的複雜環境,所以,我們可以略過我們暫時不需要的部分,比如,IPV6相關的代碼實現等。
  
  由於我們大多數情況下SIP是用的UDP,所以先來看一下cb_udp_snd_message的實現,它從全局變量exosip中獲取可用的sock,並盡最大能力解析出host和port(??難道前面的函數還不夠解析徹底??如最終仍無port信息則默認設置爲5060),使用osip_message_to_str (sip, &message, &length)函數將要發送的格式化的SIP消息轉換成能用SOCK傳輸的簡單數據併發送即完成消息發送,代碼中有許多複雜的環境探測和錯誤控制等等等等,我們可以暫時不用過多關注,可以繼續向下,結尾處有一個keeplive相關代碼,從代碼字面分析,可能是SIP的Register消息的自動重發相關代碼,可以在後面再細化分析。
  cb_tcp_snd_essage的函數實現要比上文的udp的實現簡單很多,主要是環境探測錯誤控制方面,因爲畢竟tcp是穩定連接的,對比一下代碼,可以看到主要流程還是將SIP消息轉換後,發送到從SIP消息中解析出的host和port對應的目標。
  
  看完兩個函數,可以知道,eXosip需要有兩個sock,是一個數組,0是給UDP用的,1是給TCP用的,要用SOCK當然要初始化,就是下文要介紹的eXosip的網絡相關的初始化了,上面的exosip_init可以看成是這個開發庫的系統初始化吧:) 
  至些,我們應該知道了oSIP開發的SIP應用程序的消息是從哪裏發出的吧,對了,就是從這個回調函數裏,所謂萬事開頭難,就象開發WIN32應用程序時,找到了WIN32程序的main函數入口下面的工作就好辦了,下面就都是爲一些事件消息開發對應的處理函數而已了:)

  osip_set_kill_transaction_callback 事務終結回調函數
  對應ICT,IST,NICT,NIST客戶/服務器註冊/非註冊事務狀態機的終結,主要是使用osip_remove_transaction (eXosip.j_osip, tr)將當前tr事務刪除,再加上一系列的清理工作,其中,NICT即客戶端的非Invite事務的清理比較複雜一些,要處理的內容也比較多,可以根據實際應用的情況進行有必要的清理工作:)

  cb_transport_error 傳輸失敗處理回調
  對應於上面說到的四種事務狀態機,如果它們在處理時失敗,則在這時進行統一處理。
  從代碼可知,只是在NOTIFY,SUBSCRIBE,OPTION操作失敗才進行處理,其它錯誤可直接忽略。

  osip_set_message_callback 消息發送處理回調
  根據type不同,表示不同的消息發送狀態
  OSIP_XXX_AGAIN 重發相關消息
  OSIP_ICT_INVITE_SENT 發起呼叫
  OSIP_ICT_ACK_SENT ACK迴應
  OSIP_NICT_REGISTER_SENT 發起註冊
  OSIP_NICT_BYE_SENT BYE發出
  OSIP_NICT_CANCEL_SENT Cancel發出
  OSIP_NICT_INFO_SENT,OSIP_NICT_OPTIONS_SENT,OSIP_NICT_SUBSCRIBE_SENT,OSIP_NICT_NOTIFY_SENT,OSIP_NICT_UNKNOWN_REQUEST_SENT
  我們可以看到,eXosip沒有對它們作任何處理,我們可以根據自己需要,比如,重發2xx消息前記錄一下日誌之類的,擴展一下retransmission的處理方式,發起Invite前記錄一下通話日誌等等。

  OSIP_ICT_STATUS_1XX_RECEIVED uac收到1xx消息,一般是表示對端正在處理中,這時,主要是設置一下事務狀態機的狀態值,並對會話中的osip的一些參數根據返回值進行相應設置,裏面有許多條件判斷,但我們常用的一般是100,180,183的判斷而已,暫時可以忽略裏面複雜的判斷代碼。
  OSIP_ICT_STATUS_2XX_RECEIVED uac收到2xx消息,這裏主要跟蹤一下Register情況下的2xx,表示註冊成功,這時會更新一下exosip的註冊字段值,以便讓eXosip能自動維護uac的註冊,BYE的2xx迴應是終結消息,Invite的2xx迴應,則主要是初始化一下會話相關的數據,表示已成功建立連接。
  其它4xx,5xx,6xx則分別是對應的處理,根據實現情況進行概要的查看即可。
  report_event (je, sip)是代碼中用來進行事件處理的一個函數,跟蹤後發現,其最終是使用了我們上文提到的jpipe管道,以便在狀態機外實時觀測狀態機內的處理信息。
  
  OSIP_NIST_STATUS_XXX_SENT即對應於上面的uac的處理,這裏是uas的對應的消息處理,相比較於uac簡單一點。

  前面簡單介紹了一下大量的回調函數及它們的概要處理邏輯,可能會比較混亂,暫時不用管它,只需要記得一個大概的形象,知道一個SIP處理程序是通過osip_set_cb_send_message回調函數來實現真實地發送各種SIP消息,並且SIP的標準事務模型是由oSIP實現好了,我們只需要給不同的事務狀態設置不同的回調處理函數來處理事務,具體的狀態變化和內部邏輯不用管就可以了。

  下面來說一下消息處理回調函數用到的SOCK的初始化函數,即我們上面說的除了系統初始化外的網絡初始化函數eXosip_listen_addr:
  從上文知道了,系統將初始化兩個SOCK,一個UDP一個TCP,但查看代碼發現還有第三個,TCPs的,但好象還不能實用,現在不管它,代碼首先是根據傳輸是UDP還是TCP來設置對應的數組值,並且如果沒有提供IP地址和端口號,系統會自動取出本機網絡接口並創建可用的SOCK(http_port的方式暫不用考慮)。
  SOCK初始化後,如何開始SIP事務的呢?看到這個調用eXosip.j_thread = (void *) osip_thread_create (20000, _eXosip_thread, NULL),對的,這裏啓用了一個線程,即,eXosip是調用oSIP的線程函數(沒用系統提供的線程函數,是爲了跨平臺)進行事務處理的狀態機邏輯是在一個線程中處理的,這樣就明白了爲什麼一直沒能看到順序執行下來的程序啓動代碼了,接下去看,線程實際處理函數是_eXosip_thread,這裏面的代碼中,我們看到了上文提到的狀態機控制開關變量while (eXosip.j_stop_ua == 0),即,當j_stop_ua設置爲1時,osip_thread_exit ()結束事務處理即程序終結,再接下去看,_eXosip_execute是最終的處理函數了,而且它在程序未終結情況下是一直邏輯在執行,注意,要啓用oSIP的多線程宏OSIP_MT。
  
  看到_eXosip_execute的代碼中有很多時間函數和變量,仔細看,除去一些控制代碼,主要處理函數是eXosip_read_message (1, lower_tv.tv_sec, lower_tv.tv_usec),即取出消息,1表示只取出一條消息,其代碼量非常的大,但同樣的,其中也許多的控制代碼和錯誤檢測代碼,我們在查看時可以暫時忽略掉它們。
  eXosip_read_message讀取消息時,即沒有采用sock的block也沒有用非block方式,而是採用了select方式,具體應用可查詢fd_set相關文檔。
  根據jpipe_read (eXosip.j_socketctl, buf2, 499),我們可以估計,buf2中應該是保存的我們的控制管道的數據,具體作用至些還沒有表現出來,應該是用來反映一些狀態機內部的警示之類的信息,實際的SIP的處理的狀態機的數據是存放在buf中,使用_eXosip_recvfrom獲取的,獲取後sipevent = osip_parse (buf, i)解析,使用osip_find_transaction_and_add_event (eXosip.j_osip, sipevent)來查詢事件對應的事務狀態機,找到後就如同其註解所說明的,/* handled by oSIP ! */,即我們上文設置的那一大堆回調函數,至此,我們知道了整個SIP應用所處理的大概流程了。
  如果沒有找到事務狀態機呢?直接丟棄嗎?不是的,如果這是一個迴應消息,但沒有事務狀態機處理它,那它是一個錯誤的,要進行清理後才能丟棄,而如果是一個請求,那更不能丟棄了,因爲UAS事務狀態機要由它來啓動創建的(迴應消息表示本地發出了請求消息,即UAC行爲,事務狀態機應是由啓動UAC的代碼初始化啓動的),整個邏輯應該是很簡單的,但eXosip的實現代碼卻非常多,可見其花了非常多的精力在保證會話的穩定性和應付網絡複雜情況上,我們可以對其進行大量的精簡來構建滿足我們需求的代碼實現。
  先來看錯誤的迴應消息的處理函數eXosip_process_response_out_of_transaction,可以看到其代碼就是一大堆的賦值語句,XXX= NULL,即將一大堆的運行時變量清空,再調用osip_event_free清空事件,或者就是一些複雜的情況下,需要通過解析現在的運行時數據,從中分析出“可能”的正在等待迴應的對端,併發送相關終結通知消息等等,可以根據實際需要進行簡化。
  請求事件的處理eXosip_process_newrequest,首先是對事件進行探測,MSG_IS_INVITE、MSG_IS_ACK、MSG_IS_REQUEST……,對事件進行所屬狀態機分類,隨後使用_eXosip_transaction_init (&transaction,(osip_fsm_type_t) tx_type,eXosip.j_osip, evt->sip)根據探測結果進行狀態機初始化,實際調用的是osip_transaction_init,初始化後即將事件入狀態機osip_transaction_add_event (transaction, evt),由狀態機自動處理後調用相應回調函數處理邏輯了。當然,eXosip爲方便快速開發SIP終端應用,在下面又添加了許多自動化的處理代碼,來和我們在回調函數中設置的處理代碼相區分。

  線程調用的事件處理函數代碼最後是
if (eXosip.keep_alive > 0)
{
  _eXosip_keep_alive ();
}
  這段代碼印證了上文提到了,keep_alive是用來設置是否自動重新註冊,由_eXosip_keep_alive函數來實現自動將eXosip全局變量中保存的註冊消息解析後自動根據需要重新向SIP服務器發起Register註冊。
  同樣,因爲註冊消息發起是UAC的行爲,將它放在這裏,可以看出來所有事件消息的事務狀態機處理都是在這裏,只不過這裏只創建UAS的事務狀態機,UAC的事務狀態機的創建則要繼續到下面找了,從我們的YouToo軟電話代碼中可知,發起呼叫和發起註冊分別調用了eXosip_call_send_initial_invite,eXosip_register_send_register這兩個函數(另外用到的兩個build函數則是分別構建這兩個send函數要發送的SIP消息),查看這兩個函數可知,UAC的事務處理狀態機是在這裏進行初始化的。
  eXosip_register_send_register中可以看到是_eXosip_transaction_init (&transaction, NICT, eXosip.j_osip, reg)初始化UAC狀態機,實際也同UAS是調用的osip_transaction_init函數,同樣使用osip_transaction_add_event (transaction, sipevent)將事件入狀態機,狀態機隨後將自動處理調用相應回調函數處理邏輯了。
  另有osip_new_outgoing_sipmessage(reg),表示發送消息,到這裏,我們應該可以理解,真實的發送操作,是要到由狀態機處理後,調用了消息發送回調函數才真正地將註冊消息發送出去的。
  同註冊消息發送,它是NICT狀態機,呼叫消息的發送是ICT,由eXosip_call_send_initial_invite處理,_eXosip_transaction_init (&transaction, ICT, eXosip.j_osip, invite)初始化了狀態機,之前還有一個eXosip_call_init是用來初始化eXosip的一些參數的,暫時不管它,同樣osip_new_outgoing_sipmessage (invite)發送呼叫消息,但實際還是要狀態機處理後調用消息發送回調函數真實發送呼叫請求函數的,osip_transaction_add_event (transaction, sipevent)則標準地,將事件入狀態機,狀態機將能處理隨後的應用邏輯調用相應的回調函數了。

  好了,作了這麼多的分析,我們瞭解了eXosip是怎樣調用oSIP來形成被我能方便地再次調用的了,可以看到,爲了實現最大限度的跨平臺和兼容性,代碼中有大量的測試代碼,宏定義和錯誤再處理代碼,看起來非常吃力,但瞭解了其主要的調用框架:
  初始化,回調函數設置,UAC和UAS事務處理狀態機的啓動,事件處理流程等,就可以基本明白了oSIP各個函數的主要作用和正確的用法了,下一步,可以參考eXosip來針對某個應用,去除掉大量暫時用不到的代碼,來構建一個簡單的SIP軟電話和SIP服務器,來進一步深入oSIP學習應用了。 
ortp的編譯:
 
假如你現在採用的是D盤(本人採用ortp-0.9.1,VC6.0,Windows XP):
1.在D盤上新建一個ortp文件夾D:\ortp;

2.把ortp-0.9.1文件夾下的include文件夾整個拷到D盤的ortp文件夾裏;

3.新建VC工程,選Win32 Dynamic-Link Library,Project name:ortp,
  Location:D:\ortp\,然後選An empty DLL project,
  接着在  Tools\options\Directories\include files設置如下:
  C:\PROGRAM FILES\MICROSOFT PLATFORM SDK\INCLUDE
  C:\Program Files\Microsoft Visual Studio\VC98\INCLUDE
  C:\Program Files\Microsoft Visual Studio\VC98\MFC\INCLUDE
  C:\Program Files\Microsoft Visual Studio\VC98\ATL\INCLUDE
  D:\ortp\INCLUDE
  D:\ortp\ortp
  按如上設置即可。
  注:以上Project\Setting按默認設置。
     platform sdk一定要放到最上面,否則會出錯。

4.刪除新建的vc工程的默認的source files和header files,將ortp-0.9.1\src\ortp\
  裏面的所有*.c和*.h文件拷到D:\ortp\ortp\目錄下,即和*.dsw和*.dsp文件放一起。

5.在vc工程中加入這些文件(.c,.h)
 
6.還要在D:\ortp\ortp,也就是當前文件夾中添加ortp-config.h,ortp-config-win32.h,glib.h,glibconfig.h這四個頭文件。
其中有些地方有重定義,我做了一些修改。可以到如下地址下載:
http://www.citiy.com/mainfrm/viewthread.php?tid=3294&fpage=1
這四個文件也可以從網上下載,google一下便可找到!
 
 
 
 
 
編譯出現的問題的解決方法:
 
1.在posixtimer.c中加上mmsystem.h
在port.c中加上winsock2.h
並且連接頭文件winmm.lib和ws2_32.lib

2.可能會遇到long long類型錯誤,可以改成long或者unsigned long,相應的1LL也要改成1L或者1UL
不過這樣只是解決了編譯的問題,變量如果可以爲負那麼unsigned long就會不可行,需要具體的去看程序
而且long的容量有限和作者初衷還是有差別的,不知道大家有什麼更好的方法,請聯繫我,也歡迎討論!

3.以下兩個函數沒有返回值,加上return 0;
WIN_cond_init()
WIN_cond_wait()
 
4.再有就是一些版本輸出信息,如ortp.c中
ortp_message("oRTP-" ORTP_VERSION " initialized.");
我的做法是改爲ortp_message("oRTP-0.9.1 initialized.");
ortp_min_version_required中的版本信息也沒有定義,自己手工加上
#define ORTP_MAJOR_VERSION 0
#define ORTP_MINOR_VERSION 9
#define ORTP_MICRO_VERSION 1
 
5.對於integral size mismatch in argument錯誤是因爲程序定義的參數類型和windows定義的不一樣,
在調用windows庫函數的時候要將變量用windows的類型強制轉換一下,
如:將uint16_t類型的變量轉換爲WORD類型的(其實都是unsigned short)

按照如上的方法可以最終編譯得到.dll庫文件 (照理說應該可以得到.lib和.dll兩個文件的,爲什麼這裏得不到.lib文件,希望大蝦告之)
至於要得到.lib文件的話,編譯第3步要選擇Win32 Static Library就可以了!

1. 抓取 Source Code :

Library for SIP :

libosip2-2.2.1.tar.gz
http://ftp.gnu.org/gnu/osip/
libeXosip-0.9.0.tar.gz
http://download.savannah.nongnu.org/releases/exosip/

A easy program of SIP_call on oSIP library :
test.cpp
http://huisetalage.nl/sip/test.cpp

2. 安裝 oSIP
tar zxvf libosip2-2.2.1.tar.gz
cd ./libosip2-2.2.1
./configure
make
make install

預設安裝路徑 :
/usr/local/lib
-libosip2.a
-libosip2.la
-libosip2.so -> libosip2.so.3.0.0
-libosip2.so.3 -> libosip2.so.3.0.0
-libosip2.so.3.0.0
-libosipparser2.a
-libosipparser2.la
-libosipparser2.so -> libosipparser2.so.3.0.0
-libosipparser2.so.3 -> libosipparser2.so.3.0.0
-libosipparser2.so.3.0.0
/-pkgconfig
-libosip2.pc

3. 編譯範例 - Parser Via Header

--- 自己編 ---

cd ./src/test

範例程式都在 ./src/test 下
而 ./src 為 oSIP 的 source code

gcc tvia.c -o tvia

error : tvia.c:28:28: osip2/internal.h: No such file or directory

餵給他 gcc -I[include path] 代表增加 #include 時 search 路徑

gcc tvia.c -o tvia -I../../include

error : /tmp/cc1oGZ3o.o(.text+0x5b):
In function `main': : undefined reference to `osip_malloc_func' /tmp/cc1oGZ3o.o(.text+0x6b):
In function `main': : undefined reference to `osip_malloc_func' /tmp/cc1oGZ3o.o(.text+0xd1):
In function `main': : undefined reference to `osip_strncpy' /tmp/cc1oGZ3o.o(.text+0xfd):
In function `main': : undefined reference to `osip_via_init' /tmp/cc1oGZ3o.o(.text+0x131):
In function `main': : undefined reference to `osip_via_parse' /tmp/cc1oGZ3o.o(.text+0x14c):
In function `main': : undefined reference to `osip_via_to_str' /tmp/cc1oGZ3o.o(.text+0x173):
In function `main': : undefined reference to `osip_free_func' /tmp/cc1oGZ3o.o(.text+0x181):
In function `main': : undefined reference to `osip_free_func' /tmp/cc1oGZ3o.o(.text+0x1b6):
In function `main': : undefined reference to `osip_via_free' /tmp/cc1oGZ3o.o(.text+0x1f3):
In function `main': : undefined reference to `osip_free_func' /tmp/cc1oGZ3o.o(.text+0x201):
In function `main': : undefined reference to `osip_free_func' collect2:
ld returned 1 exit status

可以得知是 linker error
驗證 :
先編成 .o 檔
gcc tvia.c -c -o tvia.o -I../../include
發現沒有錯誤,但在 linker 就出錯了
ld tvia.o -o tvia –lc
所以,老樣子的餵給他
gcc -l[library name] -L[library path]

gcc tvia.c -o tvia -I../../include -losipparser2 -L/usr/local/lib

./tvia

error : ./tvia: error while loading shared libraries: libosipparser2.so.3: cannot open shared object file: No such file or directory
哭說找不到 libosipparser2.so.3 這 library
這裡基本上可以用複製的(/usr/lib),但我都用以下這招 :
vi /etc/ld.so.conf
加入 oSIP 預設安裝路徑 /usr/local/lib
ldconfig
更新

./tvia

error : Failed to open (null) file.
Usage: tvia vias.txt

哭說沒有這檔案
find / -name vias.txt
/usr/src/osip/libosip2-2.2.1/src/test/res/vias.txt
得到一點,各範例程式需要匯入的 *.txt 都放在 ./src/test/res 下

./tvia ./res/vias.txt

就可以看到 sipparser 的測試


--- 安裝時編 ---
./configure --enable-test

make

即可在 ./src/test 下看到已經編譯好的各個檔案

--- 靜態編譯 ---
gcc tvia.c -o tvia -I../../include -losipparser2 -L/usr/local/lib --static


4. 安裝 eXoSIP
tar zxvf libeXosip-0.9.0.tar.gz
cd ./libeXosip-0.9.0
./configure

這裡不加入任何參數,在 make 時會一直哭
所以我們加參數去 disable 一些沒有用到的

./configure \
--disable-phapi \
--disable-miniua \
--disable-josua \
--disable-glib \
--disable-ms \
--disable-ortp \
--disable-gsm \
--disable-ilbc

miniua : example
josua : example
phapi : softphone 面板上的按鍵
glib : 圖形 lib
ortp : rtp
ms : media streaming
ilbc : codec
gsm : codec

make

make install

預設安裝路徑 :
/usr/local/lib
-libeXosip.a
-libeXosip.la
-libeXosip.so -> libeXosip.so.3.0.0
-libeXosip.so.3 -> libeXosip.so.3.0.0
-libeXosip.so.3.0.0


5. 編譯範例 - 註冊

--- 自己編譯 ---

cd ./tools

與 oSIP 不同,eXoSIP 範例程式都放在 ./tools 下

gcc sip_reg.c -o sip_reg -leXosip -L/usr/local/lib

這裡一樣會哭說找不到某個 library
所以我們用 ldconfig 更新一下

./sip_reg

跳出 help內容
表示要輸入參數
Usage:
sipreg [required_options] [optional_options]
[required_options]
-r --proxy sip:proxyhost[:port]
-u --from sip:user@host[:port]
-c --contact sip:user@host[:port]
[optional_options]
-d --debug (log to stderr and do not fork
-e --expiry number (default 3600)
-f --firewallip N.N.N.N
-h --help
-l --localip N.N.N.N (force local IP address)
-p --port number (default 5060)
-U --username authentication username
-P --password authentication password

./sip_reg \
--proxy sip:140.125.33.240 \
--from sip:[email protected] \
--contact sip:[email protected] \
--username 240100 \
--password 240100 \
--expiry 180

抓出註冊封包來看
注意 : 執行後,程式會在 background 執行,所以需要改一下 expiry time 方便觀察


--- 安裝時編 ---

./configure --enable-tools

make

--- 靜態編譯 ---

gcc sip_reg.c -o sip_reg -leXosip -losip2 -losipparser2 -lpthread -L/usr/local/lib --static


PS. gcc 參數說明
http://mis.im.tku.edu.tw/~zbwei12b/program/gcc1.html
http://mis.im.tku.edu.tw/~zbwei12b/program/gcc-2.html

6. 建立 SIP_call
g++ test.cpp -o test

檔名為 *.cpp 且 include header 有 stdafx.h 檔
所以可以得知發展平臺是在 winodows 的 c++ ( vc 之類的)
所以我們要用 g++ 編譯
test.cpp:23:20: stdafx.h: No such file or directory
test.cpp:29:19: conio.h: No such file or directory
test.cpp: In function `void __exit(int)':
test.cpp:103: `exit' undeclared (first use this function)
test.cpp:103: (Each undeclared identifier is reported only once for each function it appears in.)
test.cpp: In function `int main(int, char**)':
test.cpp:520: `_kbhit' undeclared (first use this function)
test.cpp:522: `_getch' undeclared (first use this function)
說找不到和看不懂一些檔案和程式,修改一下原始碼 :
vi test.cpp
刪除 _kbhit
將 _getch() 改為 getchar()
//#include "stdafx.h"
//#include ( DOS 用 - _getch())

g++ test.cpp -o test

又跳出 error :
test.cpp: In function `void __exit(int)':
test.cpp:103: `exit' undeclared (first use this function)
test.cpp:103: (Each undeclared identifier is reported only once for each function it appears in.)
應該是有些 header 檔沒有餵
利用之前有編譯過其他可以動的程式,如 tvia.c 和 sip_reg.c 的 include header

tvia.c
#include "stdio.h"
#include "stdlib.h"
#include "osip2/internal.h"
#include "osipparser2/osip_message.h"

sip_reg.c
#include "stdio.h"
#include "stdlib.h"
#include "unistd.h"
#include "netdb.h"
#include "syslog.h"
#include "pthread.h"
#include "eXosip/eXosip.h"

所以我新增 header :
vi test.cpp
#include "stdio.h"
#include "stdlib.h"
#include "unistd.h"
#include "netdb.h"
#include "syslog.h"
#include "pthread.h"
因為 #include "eXosip/eXosip.h" 重覆,所以不加

g++ test.cpp -o test

/tmp/ccMdnyE9.o(.text+0x14): In function `__exit(int)':
: the `gets' function is dangerous and should not be used.
/tmp/ccMdnyE9.o(.text+0x56): In function `josua_event_get()':
: undefined reference to `eXosip_event_wait'
.
.
. /tmp/ccMdnyE9.o(.gnu.linkonce.t._ZN5jcall5buildEP12eXosip_event+0xef)
: In function `jcall::build(eXosip_event*)': : undefined reference to `osip_strncpy' /tmp/ccMdnyE9.o(.gnu.linkonce.t._ZN5jcall5buildEP12eXosip_event+0x11a)
: more undefined references to `osip_strncpy'
follow collect2: ld returned 1 exit status
大概可以猜到是 linker 出錯

g++ test.cpp -o test -leXosip -losip2

利用之前編譯 sip_reg 的概念
餵給他 library
執行後會出現
: /tmp/ccCRNT0p.o(.text+0x14): In function `__exit(int)':
: the `gets' function is dangerous and should not be used.
基本上已經成功了 compiler
只是在哭說最好不要用 gets 這 function
如果真要去除這 warning
可以將原始碼內的函式 - __exit(int){} 殺掉
並將有呼叫到 __exit 改為 exit(1)

./test
沒有動作
只有出現 Hello World!
所以應該是編譯成功了
看一下 code

vi test.cpp
送 INVITE 位址
修改以下 :
i = eXosip_build_initial_invite(&invite,
"sip:130.139.45.174:5060", //to
"sip:130.139.44.249:5060", //from
NULL, "hello");

進degub mode
修改以下 :
//FILE* logfile = fopen( "logfile.txt", "w");
//osip_trace_initialize( (_trace_level)8, logfile );
osip_trace_initialize( (_trace_level)8, stdout );

g++ test.cpp -o test -leXosip -losip2

./test

看一下 code 就知道這支程式是用一些 hot key 來對應動作,如下 :
a - answering call
h - hangup
r - ringing
c - call
q - quit

LINPHONE ON ARM-LINUX (cross-compiling on host for target architecture)
編譯linphone 需要庫的支持,這其中就需要osip2,ogg,speex,ortp庫的支持,這幾種庫分別支持各種通訊協議,如osip2支持的爲sip協議,ortp支持的爲rtp協議(即rtp協議的軟件版)。

   其中speex庫另需要ogg庫的支持,在編譯過程中可以看到。

廢話少說,下面開始交叉編譯


軟件包: (在網上下載如下軟件包)
1) linphone-1.2.0
2) libosip2-2.2.2
3) libogg-1.1.0
4) speex-1.1.11.1
5) oRTP ( linphone 包中自帶,如果沒有可以下載ortp-0.7.0版本)
到網站下載http://www.gnu.org/software/
其他工具見:http://telestarnotes.blogspot.com/2004_12_01_archive.html

A) 編譯環境設置:
arm交叉編譯工具::
    下載 arm-linux-gcc-3.4.1.tar.bz2 
    直接解壓到usr/local 中,或鏈接到/usr/local中
   1)把arm-linux-gcc-3.4.1.tar.bz2 文件copy到usr/local 下,右擊解壓即可

  2)任意目錄下解壓arm-linux-gcc-3.4.1.tar.bz2
   #tar -xvjf arm-linux-gcc-3.4.1.tar.bz2                        //解壓
   #export PATH=$PATH:/root/usr/local/arm/3.4.1/bin       //環境變量設置:路徑,鏈接,庫
   #export LD=/root/usr/local/arm/3.4.1/bin/arm-linux-ld
   #export LDFLAGS=-L/root/usr/local/arm/3.4.1/arm-linux/lib

 

B) 交叉編譯 libosip2-2.2.2
   #cd libosip2-2.2.2            //到解壓後的 libosip 目錄下,以下同
   #./configure --prefix=/root/armbuild -host=arm-linux --target=arm-linux --disable-static   //配置文件
   #make                   //編譯
   #make install         //安裝

// --prefix=/...       :指定文件編譯安裝目錄
//--host=.....         :指定編譯工具,默認的爲gcc,此處爲arm-linux(編譯到arm上用的)
//--disable-static   :禁止靜態庫(.a)鏈接,編譯生成動態庫(.so)

C) 交叉編譯 libogg-1.1.0
   #cd ../libogg-1.1.0
   #./configure --prefix=/root/armbuild --host=arm-linux --target=arm-linux --disable-static --enable-fixed-point
   #make
   #make install

D) 交叉編譯speex-1.1.11.1
   #cd ../speex-1.1.11.1
   #./configure --prefix=/root/armbuild --host=arm-linux --target=arm-linux --disable-static --enable-fixed-point --enable-arm-asm --with-ogg=/root/armbuild  --with-ogg-libraries=/roo/armbuild/lib  --with-ogg-headers=/root/armbuild/include/ogg
//要把/root/armbuild/lib 下的生成的ogg相應的庫copy到交叉編譯工具/usr/local/arm-linux/lib 下面,speex的編譯需要ogg庫的支持。
   #make
   #make install
//--with-ogg                  : 指定ogg生成的庫的目錄
// --with-ogg-libraries    : ogg庫,若沒有,把生成的文件copy目錄下面
//--with-ogg-headers    :指定ogg頭文件

E) 將libspeex編譯成功的庫文件copy到編譯工具下的庫中
   #cp /root/armbuild/usr/lib/libspeex.so.2.0.0
/root/usr/local/arm/3.4.1/arm-linux/lib

   #cd /root/usr/local/arm/3.4.1/arm-linux/lib     //建立鏈接
   #ln -s libspeex.so.2.0.0 libspeex.so
   #ln -s libspeex.so.2.0.0 libspeex.so.2
//以上的 步驟 E),可以直接手動把libspeex.so.2.0.0 , libspeex.so, libspeex.so.2三個文件複製到加查編譯工具下的庫(lib)目錄下即可

F) 交叉編譯 linphone-1.2.0:
   在編譯linphone之前先將ortp複製到 linphone-1.2.0目錄下
   #cd /root/arm/linphone-1.2.0
   #cd oRTP
   ##./configure --prefix=/root/armbuild --host=arm-linux --target=arm-linux --disable-static --enable-fixed-point --disable-glib
//需要加上 --disable-glib(禁止庫),否則會應爲缺少gthread 庫而無法編譯。
   #make
   #make install

G)將生成的庫文件及鏈接複製到交叉編譯工具的庫中(同步驟E)
   ##cp /root/armbuild/usr/lib/libortp.so.2.0.0
/root/usr/local/arm/3.4.1/arm-linux/lib

   #cd /root/usr/local/arm/3.4.1/arm-linux/lib
   #ln -s libortp.so.2.0.0 libortp.so.2
   #ln -s libortp.so.2.0.0 libortp.so

以此,就可以在沒有圖形界面(GUI)支持情況下編譯linphone

H)  #cd /root/arm/linphone-1.2.0
   #./configure --prefix=/root/armbuild --host=arm-linux --target=arm-linux  --disable-static --disable-glib --enable-gnome_ui=no --disable-manual
--enable-ipv6 --enable-alsa --with-osip=/root/armbuild  --with-speex=/root/armbuild
   #make
   #make install
//--enable-alsa   :使能alsa語音編解碼方式,語音傳輸的另種方式爲oss
//--enable-ipv6   :使能ipv6

以上的--disable-static 語句使編譯不能生成靜態庫文件,即不能使庫和可執行文件和爲一體,而生成了另外單獨的庫支持文件。如果需要把庫和可執行文件合成一個文件,那麼不要使用這項。

就此編譯完畢,在/root/armbuild/bin 文件中就可以找到可執行文件linphonec,把改文件及相應的庫下載到開發板中,
在sch中相應的目錄下輸入./linphonec,即可以運行程序
如不能運行,則缺少庫支持,把剛生成的相應的  *.so.*文件複製到開發板的lib目錄下即可。
arm-linux編譯osip,ortp-0.7.0,exosip2:

對osip2,ortp,exosip2 的編譯可以生成josua軟件

1)osip2-2.2.1
2)ortp-0.7.0
3)exosip2-1.9.1-pre16

準備:
在/root下新建文件夾josua


1)交叉編譯osip:
在/home/libosip2-2.2.1文件夾下編譯:
$CC=arm-linux-gcc CFLAGS=-O2 ./configure  --prefix=/root/josua  --disable-trace --disable-debug --disable-josua --host=arm-linux
$make
$make  install

 

2)交叉編譯ortp:
 #cd /home/ortp-0.7.0
   ##./configure --prefix=/root/josua --host=arm-linux --target=arm-linux --disable-static --enable-fixed-point --disable-glib
//需要加上 --disable-glib(禁止glib庫),否則會應爲缺少gthread 等庫而無法編譯。  changed by Myth Wu
   #make clean
   #make
   #make install

3)交叉編譯exosip:

  #cd  /home/libeXosip2-1.9.1-pre16
   #./configure --prefix=/root/josua  --host=arm-linux --target=arm-linux  --disable-static --disable-glib --enable-gnome_ui=no --disable-manual
--enable-ipv6 --enable-alsa --with-osip=/root/josua  --disable-josua
   #make
   #make install
//--enable-alsa   :使能alsa語音編解碼方式,語音傳輸的另種方式爲oss(一般爲oss)       changed by Myth Wu
//--enable-ipv6   :使能ipv6


至此交叉編譯osip成功,生成可執行文件在/root/josua/bin中,鏈接庫文件在/root/josua/lib中,下載可執行文件及相應的庫文件到開發板即可以運行

發佈了59 篇原創文章 · 獲贊 17 · 訪問量 23萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章