国产 在线视频 一区 I 91丝袜美腿美女视频网站 I 韩国羞羞视频 I 国产精品va在线 I 国产精品亚洲成人 I 国产精品污www一区二区三区 I 日韩av电影在线免费播放 I 亚洲国产成人porn I 91精品国产日韩91久久久久久 I 免费看黄网站在线 I 久久精品资源 I 国产精品zjzjzj在线观看 I 国产精品日韩av I 国产亚洲免费的视频看 I 久久视频在线视频 I av最新版天堂资源在线 I 朴麦妮原版视频高清资源 I 亚洲欧美在线播放 I 中文字幕 国内精品 I 国产欧美日韩激情 I 轻轻草福利视频 I 午夜在线观看av I 日本扒衣摸双乳视频 I 在线午夜免费 I 国产偷亚洲偷欧美偷精品 I 97偷自拍亚洲综合二区 I 午夜毛片电影 I 日韩av中文字幕在线免费观看 I 91精品视频在线 I 亚洲福利精品在线 I 未满十八18勿进黄网站 I 亚洲人和日本人hd I 樱花草国产18久久久久

視頻會議協議相關

當前位置:首頁>視頻會議技術文章>視頻會議協議相關

視頻會議h323協議棧開發入門


pwlib是一套跨平臺的C++的開發庫,使基于pwlib上開發的應用能夠很少量的移植就可以跑在windows和unix的平臺上. 
Open323是澳洲的一家公司驅動的open source的是視頻會議的h323協議族實現, 還不夠十分的完整, 但是已經是非常的難得了. 
在windows上和linux下都能編譯使用, 我已經試過了. Windows上編譯他們比較麻煩, 注意的是一定要用batch building. 在VC7上編譯openh323的動態連接庫的時候, VS.net會崩潰, 注意避開, 不過也可以試試看看現象, 如果能夠解決, 請告訴我一下. 
在linux上編譯就沒有什么好說的了, 設好兩個環境變量(PWLIBDIR, OPENH323DIR), 就可以在展開的目錄下編譯了, 先編譯PWLIB, 再編譯OPENH323, 別忘了將相應xx/lib寫到/etc/ld.so.conf下. 我這里可能對安裝講的不夠詳細, openh323講的非常詳細, 大家可以去看. 

以linux平臺為例: 
使用pwlib, 在成功編譯之后, 到$(PWLIBDIR)/SAMPLES/ 
這里是一些例子, hello_world 是個非常簡單的工程, 從這里我們可以看到如何寫使用pwlib的Makefile: 
# Simple makefile for the hello world program 
PROG = hello 
SOURCES = hello.cxx 
ifndef PWLIBDIR 
PWLIBDIR=$(HOME)/pwlib 
endif 
include $(PWLIBDIR)/make/ptlib.mak 
關鍵是包含了一個ptlib.mak 

hello.cxx 
#include 
class Hello : public PProcess 

PCLASSINFO(Hello, PProcess) 
public: 
void Main(); 
}; 

PCREATE_PROCESS(Hello) 
void Hello::Main() 

cout << "Hello world!\n"; 

非常有代表性. Include $(PWLIBDIR)/make/ptlib.mak 這樣就可以make all, make debug的之類的進行編譯, 需要的頭文件庫都會替你安排好. 編譯的結果就會放在obj_linux_x86_xx, xx 表示你用的是debug編譯還是其他, 如果是debug, xx就是d. 

使用pwlib的程序, 必然要有一個PProcess的子類, 作為整個進程, 這是指在console模式下, gui模式的用PApplication這個我沒有用過. Pwlib里面的類大多都是P開頭, (可能是取其兼容的意思, 跨平臺的特性, 我瞎猜的), 在進程中如果想創建新的線程就創建PThread子類的對象, 對于這種關于過程的類,都有Main函數等待子類去實現. 
在使用所有的P類的時候, 注意使用兩個宏, 聲明類的時候PCLASSINFO(Hello, PProcess); 分號可以加, 也可不加. PProcess的子類的實現的時候要用PCREATE_PROCESS(Hello);, 這個東西把main()之類的系統入口封裝了, 由他來調用Main()成員函數. 在使用線程的時候, 如果想讓線程從線程的對象一創建就運行, 就應該在PThread子類中的構造函數中調用父類的Resume(). 關于pwlib先說這些, 在使用Openh323的時候到處都會用到pwlib的東西和概念. 

Openh323: 
終于進入正題了, 先粗略的講點概念(多余了), H323是指協議族了, 包含了很多規范, 它來自ITU, 應會議的需要而產生, 信令相關的東西用H225 H245,類似Q931,用ASN1編碼后在tcp之上傳輸, 數據相關的就是編碼解碼的東西了(包括音頻視頻), 音頻g711(alaw, ulaw)了等等多了, 視頻h261, 好像h263還沒實現. 
在H323的系統里進行通訊的角色實體就是Endpoint, 每個Endpoint可以有很多的Connection, 每個Endpoint也可以擁有很多的邏輯角色, 這個不討論. 
Endpoint 在Openh323中就是類H323Endpoint的實例 
Connection 在Openh323中就是 H323Connection的實例 
當Endpoint接收了一個遠程的連接請求, Endpoint就會創建一個H323Connection; 
當Endpoint發出一個連接的請求, Endpoint也會創建一個H323Connection 
Connection 就會進入一個狀態機, 在各個狀態中, Connetcion會相應的執行相應的方法, 這些方法, 大多都是Onxxxxx(), 是虛函數, 我們可以自己通過繼承H323Connection創建其子類, 并且在我們想做事的時機去重載相應的虛函數. 這是使用Openh323的一個基本的思路. 
現在我們可以看看如何寫一個自己H323的Endpoint, 讓它能夠和netmeeting互操作.成功編譯Openh323后在它的samples的目錄下面有幾個例子, mfc是指在windows下如何使用MFC和Openh323一起開發, 還有simple, 這是個簡單的H323的Endpoint的實現, 作為理解OpenH323的庫如何使用和開發的技巧方法已經足夠了. 
程序運行主線: 
PWLIB(PCREATE_PROCESS(SimpleH323Process))--?SimpleH323Process:: SimpleH323Process()--?SimpleH323Process::Main(); 
Main()如果結束, 這個程序就結束了, 可是Main()里面有個死循環, 寫過圖形程序的朋友們都知道, 這就是在等消息來呀. 在VC中稱之為Interface thread. 
程序注解: 
main.h 
這個文件包含了程序用到的所有類的聲明, 一般應該至少有三個類: 
來自PProcess的一個主進程的, 或者說作為界面線程的;(只有一個對象) 
來自H323Endpoint的, 標識這個H323端點的;(只有一個對象) 
來自H323Connection的, 標識所有和這個H323端點相關的連接;(可以有多個) 

#ifndef _SimpleH323_MAIN_H 
#define _SimpleH323_MAIN_H 
//避免頭文件重復包含 

#include 

class SimpleH323EndPoint : public H323EndPoint 

//使用Pwlib的要求, 就像使用MFC, 有n多的宏, 可以看看pwlib的源碼, 
//宏展開都干了什么 
PCLASSINFO(SimpleH323EndPoint, H323EndPoint); 

public: 
SimpleH323EndPoint(); 
~SimpleH323EndPoint(); 

// overrides from H323EndPoint 
// 重載H323EndPoint的函數 

// 當收到一個遠程的呼入和發出呼出的請求的時候 
virtual H323Connection * CreateConnection(unsigned callReference); 
// 有遠程的請求來到, 這是在CreateConnection之后的 
virtual BOOL OnIncomingCall(H323Connection &, const H323SignalPDU &, H323SignalPDU &); 
//應答遠程的呼入 
virtual H323Connection::AnswerCallResponse OnAnswerCall(H323Connection &, const PString &, const H323SignalPDU &, H323SignalPDU 
&); 
//當連接被Forward 
virtual BOOL OnConnectionForwarded(H323Connection &, const PString &, const H323SignalPDU &); 
//當連接建立 
virtual void OnConnectionEstablished(H323Connection & connection, const PString & token); 
//當連接撤銷 
virtual void OnConnectionCleared(H323Connection & connection, const PString & clearedCallToken); 
//當連接需要打開聲音的通道 
virtual BOOL OpenAudioChannel(H323Connection &, BOOL, unsigned, H323AudioCodec &); 

// New functions 
// 自己添加的新函數, 父類中不存在 
BOOL Initialise(PArgList &); 
BOOL SetSoundDevice(PArgList &, const char *, PSoundChannel::Directions); 
// 每個連接會有一個Token來唯一標識 
PString currentCallToken; 

protected: 
BOOL autoAnswer; 
PString busyForwardParty; 
}; 

class SimpleH323Connection : public H323Connection 

PCLASSINFO(SimpleH323Connection, H323Connection); 

public: 
//創建連接對象的時候將Endpoint的對象以引用傳進來 
//引用的概念就是將整個對象暴露給你的意思, 不是復制了一份的意思, 
//對象還是原來的對象, 所以在Connection中修改了EndPoint的某些屬性后 
//就是在操作著傳進來的對象, 這是C++的基本概念, OpenH323大量的使用 
//引用傳遞對象, 對引用的概念要理解 
SimpleH323Connection(SimpleH323EndPoint &, unsigned); 

//重載了兩個父類的函數 

// 當打開邏輯通道的時候(等于沒說) 
virtual BOOL OnStartLogicalChannel(H323Channel &); 
// 處理用戶輸入, 這個不是之運行這個程序的用戶,而是這個連接上的用戶輸入 
// 一般應該是撥號了之類的, 
virtual void OnUserInputString(const PString &); 

protected: 
// 快速連接?? 
BOOL noFastStart; 
}; 
class SimpleH323Process : public PProcess 

//主進程, 類似VC的用戶界面線程, 
//他是整個程序的入口點, 和結束點 
//創建了EndPoint對象后會有好幾個線程啟動 
//這個就是主線程 
PCLASSINFO(SimpleH323Process, PProcess) 

public: 
SimpleH323Process(); 
~SimpleH323Process(); 
//這個函數會被自動調用, 是我們程序的入口了 
void Main(); 
protected: 

//這個H323端點對象 
SimpleH323EndPoint * endpoint; 
}; 

#endif // _SimpleH323_MAIN_H 

下面是main.cpp 所有的類的實現了 

#include 

#ifdef __GNUC__ 
#define H323_STATIC_LIB 
#endif 

#include "main.h" 
#include "../../version.h" 


#define new PNEW 

// 這個東西里邊可能封裝了標準的main函數 
PCREATE_PROCESS(SimpleH323Process); 


/////////////////////////////////////////////////////////////// 

//幾個宏都在version.h里面定義 
SimpleH323Process::SimpleH323Process() 
: PProcess("OpenH323 Project", "SimpleH323", 
MAJOR_VERSION, MINOR_VERSION, BUILD_TYPE, BUILD_NUMBER) 

endpoint = NULL; 


SimpleH323Process::~SimpleH323Process() 

delete endpoint; 

void SimpleH323Process::Main() 

cout << GetName() 
<< " Version " << GetVersion(TRUE) 
<< " by " << GetManufacturer() 
<< " on " << GetOSClass() << << GetOSName() 
<< " (" << GetOSVersion() << - << GetOSHardware() << ")\n\n"; 

// Get and parse all of the command line arguments. 
// 分析命令行參數, 略去數行 
PArgList & args = GetArguments(); 
args.Parse( 
"a-auto-answer." 
"b-bandwidth:" 
"B-forward-busy:" 
"D-disable:” FALSE); 
if (args.HasOption(h) || (!args.HasOption(l) && args.GetCount() == 0)) { 
//如果沒有參數或者參數是h, 就輸出如何使用, 此處略去數行 

//這個東西暫時不管 
#if PTRACING 
#endif 

// Create the H.323 endpoint and initialise it 
// H323 EndPoint 創建了, 并且把命令參數傳過去初始化, 初始化的時候做了一些事 
endpoint = new SimpleH323EndPoint; 
if (!endpoint->Initialise(args)) 
return; 
//看看命令行里是不是想直接呼叫另一個H323的endpoint.有沒有l(listen)的option 
//如果是就MakeCall, 
// See if making a call or just listening. 
if (args.HasOption(l)) 
cout << "Waiting for incoming calls for \"" << endpoint->GetLocalUserName() << "\"\n"; 
else { 
cout << "Initiating call to \"" << args[0] << "\"\n"; 
endpoint->MakeCall(args[0], endpoint->currentCallToken); 

cout << "Press X to exit." << endl; 

// Simplest possible user interface 
// 簡單的用戶界面, 會有一個提示> 
// 取pid是我加的 
for (;;) { 
pid_t thispid; 
char prom[20]; 

thispid = getpid(); 
sprintf(prom, "H323 %d >", thispid); 

cout << prom << flush; 
PCaselessString cmd; 
cin >> cmd; 
if (cmd == "X") 
break; 

if (cmd.FindOneOf("HYN") != P_MAX_INDEX) { 
H323Connection*connection; 
//使用lock就是怕別的線程把它給刪了 
//因為這里正用著呢 
connection=endpoint->FindConnectionWithLock(endpoint->currentCallToken); 
if (connection != NULL) { 
if (cmd == "H") 
connection->ClearCall(); 
else if (cmd == "Y") 
connection->AnsweringCall(H323Connection::AnswerCallNow); 
else if (cmd == "N") 
connection->AnsweringCall(H323Connection::AnswerCallDenied); 
connection->Unlock(); 




cout << "Exiting " << GetName() << endl; 

// Main 函數結束 

// 自己的Init函數 
BOOL SimpleH323EndPoint::Initialise(PArgList & args) 

// Get local username, multiple uses of -u indicates additional aliases 
if (args.HasOption(u)) { 
PStringArray aliases = args.GetOptionString(u).Lines(); 
// 設定改Endpoint的username 
SetLocalUserName(aliases[0]); 
// 設定Aliases 就是每個Endpoint可以有好多名字的意思 
for (PINDEX i = 1; i < aliases.GetSize(); i++) 
AddAliasName(aliases[i]); 


// Set the various options 
//設置靜音檢測否 

SetSilenceDetectionMode(args.HasOption(e) ? H323AudioCodec::NoSilenceDetection 
: H323AudioCodec::AdaptiveSilenceDetection); 
//快速連接? 
DisableFastStart(args.HasOption(f)); 
//H245通道 
DisableH245Tunneling(args.HasOption(T)); 

autoAnswer = args.HasOption(a); 
busyForwardParty = args.GetOptionString(B); 

if (args.HasOption()) { 
initialBandwidth = args.GetOptionString().AsUnsigned()*100; 
if (initialBandwidth == 0) { 
cerr << "Illegal bandwidth specified." << endl; 
return FALSE; 



if (args.HasOption(j)) { 
unsigned jitter = args.GetOptionString(j).AsUnsigned(); 
//設定音頻抖動的, 應該影響到接收的緩存 
if (jitter >= 20 && jitter <= 10000) 
SetMaxAudioDelayJitter(jitter); 
else { 
cerr << "Jitter should be between 20 milliseconds and 10 seconds." << endl; 
return FALSE; 



//設定聲音設備 
//也可以不用聲音設備, 比如Openh323工程的子項目 OpenAM和OpenMCU 
//都使演示了如何不使用聲音物理設備的方法, 我想那里邊的東西會對某些朋友們 
//的需求比較合適 
if (!SetSoundDevice(args, "sound", PSoundChannel::Recorder)) 
return FALSE; 
if (!SetSoundDevice(args, "sound", PSoundChannel::Player)) 
return FALSE; 
if (!SetSoundDevice(args, "sound-in", PSoundChannel::Recorder)) 
return FALSE; 
if (!SetSoundDevice(args, "sound-out", PSoundChannel::Player)) 
return FALSE; 

// 設定decode encode的能力 
// H323 EndPoint在真正進行數據通訊之前要進行能力的交換, 說明自己能夠接收和發送什么標準的數據, g.711是必須支持的. 
// Set the default codecs available on sound cards. 
AddAllCapabilities(0, 0, "GSM*{sw}"); 
AddAllCapabilities(0, 0, "G.711*{sw}"); 
AddAllCapabilities(0, 0, "LPC*{sw}"); 
AddAllUserInputCapabilities(0, 1); 

RemoveCapabilities(args.GetOptionString(D).Lines()); 
ReorderCapabilities(args.GetOptionString(P).Lines()); 

cout << "Local username: " << GetLocalUserName() << "\n" 
<< "Silence compression is " << (GetSilenceDetectionMode() == H323AudioCodec::NoSilenceDetection ? "Dis" : "En") << "abled\n" 
<< "Auto answer is " << autoAnswer << "\n" 
<< "FastConnect is " << (IsFastStartDisabled() ? "Dis" : "En") << "abled\n" 
<< "H245Tunnelling is " << (IsH245TunnelingDisabled() ? "Dis" : "En") << "abled\n" 
<< "Jitter buffer: " << GetMaxAudioDelayJitter() << " ms\n" 
<< "Sound output device: \"" << GetSoundChannelPlayDevice() << "\"\n" 
"Sound input device: \"" << GetSoundChannelRecordDevice() << "\"\n" 
<< "Codecs (in preference order):\n" << setprecision(2) << GetCapabilities() << endl; 

//啟動一個來電的監聽 
//可以使用配置的端口, 也可以使用default的端口 
// Start the listener thread for incoming calls. 
H323ListenerTCP * listener; 
if (args.GetOptionString(i).IsEmpty()) 
listener = new H323ListenerTCP(*this); 
else { 
PIPSocket::Address interfaceAddress(args.GetOptionString(i)); 
listener = new H323ListenerTCP(*this, interfaceAddress); 

if (!StartListener(listener)) { 
cerr << "Could not open H.323 listener port on " 
<< listener->GetListenerPort() << endl; 
delete listener; 
return FALSE; 


//這是連接GateKeeper相關的東西, 先不討論了 
// Initialise the security info 
if (args.HasOption(p)) { 
SetGatekeeperPassword(args.GetOptionString(p)); 
cout << "Enabling H.235 security access to gatekeeper." << endl; 


// Establish link with gatekeeper if required. 
if (args.HasOption(g) || !args.HasOption( )) { 
H323TransportUDP * rasChannel; 
if (args.GetOptionString(i).IsEmpty()) 
rasChannel = new H323TransportUDP(*this); 
else { 
PIPSocket::Address interfaceAddress(args.GetOptionString(i)); 
rasChannel = new H323TransportUDP(*this, interfaceAddress); 


if (args.HasOption(g)) { 
PString gkName = args.GetOptionString(g); 
if (SetGatekeeper(gkName, rasChannel)) 
cout << "Gatekeeper set: " << *gatekeeper << endl; 
else { 
cerr << "Error registering with gatekeeper at \"" << gkName << \" << endl; 
return FALSE; 


else { 
cout << "Searching for gatekeeper..." << flush; 
if (DiscoverGatekeeper(rasChannel)) 
cout << "\nGatekeeper found: " << *gatekeeper << endl; 
else { 
cerr << "\nNo gatekeeper found." << endl; 
if (args.HasOption( )) 
return FALSE; 




return TRUE; 


//設定音頻設備, 沒什么可講的 
BOOL SimpleH323EndPoint::SetSoundDevice(PArgList & args, 
const char * optionName, 
PSoundChannel::Directions dir) 

if (!args.HasOption(optionName)) 
return TRUE; 

PString dev = args.GetOptionString(optionName); 

if (dir == PSoundChannel::Player) { 
if (SetSoundChannelPlayDevice(dev)) 
return TRUE; 

else { 
if (SetSoundChannelRecordDevice(dev)) 
return TRUE; 


cerr << "Device for " << optionName << " (\"" << dev << "\") must be one of:\n"; 

PStringArray names = PSoundChannel::GetDeviceNames(dir); 
for (PINDEX i = 0; i < names.GetSize(); i++) 
cerr << " \"" << names[i] << "\"\n"; 

return FALSE; 


//這個函數很簡單但是非常關鍵, 是從EndPoint中重載過來的. 
//本來是return new H323Connection()的, 現在改成Simplexxx 
//自己實現的一個Connection, 這樣當Endpoint里面調用 
//Connection的一些東西的時候, 實際上運行的是Simplexxx 
//的實現, 看到C++的好處了吧, C里用函數指針也可以實現, 沒有 
//C++這么native. 
H323Connection * SimpleH323EndPoint::CreateConnection(unsigned callReference) 

return new SimpleH323Connection(*this, callReference); 

//沒什么東西, 關鍵是看看這個東西的調用的時機 
BOOL SimpleH323EndPoint::OnIncomingCall(H323Connection & connection, 
const H323SignalPDU &, 
H323SignalPDU &) 

if (currentCallToken.IsEmpty()) 
return TRUE; 

if (busyForwardParty.IsEmpty()) { 
cout << "Incoming call from \"" << connection.GetRemotePartyName() << "\" rejected, line busy!" << endl; 
return FALSE; 


cout << "Forwarding call to \"" << busyForwardParty << "\"." << endl; 
return !connection.ForwardCall(busyForwardParty); 



//這個東西, 很有用, H323Connection的類里也有這個虛函數 
//返回的值決定告訴遠程的連接者是否接收這份連接請求 
H323Connection::AnswerCallResponse 
SimpleH323EndPoint::OnAnswerCall(H323Connection & connection, 
const PString & caller, 
const H323SignalPDU &, 
H323SignalPDU &) 

currentCallToken = connection.GetCallToken(); 

if (autoAnswer) { 
cout << "Automatically accepting call." << endl; 
return H323Connection::AnswerCallNow; 


cout << "Incoming call from \"" 
<< caller 
<< "\", answer call (Y/n)? " 
<< flush; 

return H323Connection::AnswerCallPending; 


BOOL SimpleH323EndPoint::OnConnectionForwarded(H323Connection & /*connection*/, 
const PString & forwardParty, 
const H323SignalPDU & /*pdu*/) 

if (MakeCall(forwardParty, currentCallToken)) { 
cout << "Call is being forwarded to host " << forwardParty << endl; 
return TRUE; 


cout << "Error forwarding call to \"" << forwardParty << \" << endl; 
return FALSE; 



//連接建立時候 
void SimpleH323EndPoint::OnConnectionEstablished(H323Connection & connection, 
const PString & token) 

currentCallToken = token; 
cout << "In call with " << connection.GetRemotePartyName() << endl; 


//連接斷開時候 
void SimpleH323EndPoint::OnConnectionCleared(H323Connection & connection, 
const PString & clearedCallToken) 

if (currentCallToken == clearedCallToken) 
currentCallToken = PString(); 

PString remoteName = \" + connection.GetRemotePartyName() + \"; 
switch (connection.GetCallEndReason()) { 
case H323Connection::EndedByRemoteUser : 
cout << remoteName << " has cleared the call"; 
break; 
case H323Connection::EndedByCallerAbort : 
cout << remoteName << " has stopped calling"; 
break; 
case H323Connection::EndedByRefusal : 
cout << remoteName << " did not accept your call"; 
break; 
case H323Connection::EndedByNoAnswer : 
cout << remoteName << " did not answer your call"; 
break; 
case H323Connection::EndedByTransportFail : 
cout << "Call with " << remoteName << " ended abnormally"; 
break; 
case H323Connection::EndedByCapabilityExchange : 
cout << "Could not find common codec with " << remoteName; 
break; 
case H323Connection::EndedByNoAccept : 
cout << "Did not accept incoming call from " << remoteName; 
break; 
case H323Connection::EndedByAnswerDenied : 
cout << "Refused incoming call from " << remoteName; 
break; 
case H323Connection::EndedByNoUser : 
cout << "Gatekeeper could find user " << remoteName; 
break; 
case H323Connection::EndedByNoBandwidth : 
cout << "Call to " << remoteName << " aborted, insufficient bandwidth."; 
break; 
case H323Connection::EndedByUnreachable : 
cout << remoteName << " could not be reached."; 
break; 
case H323Connection::EndedByHostOffline : 
cout << remoteName << " is not online."; 
break; 
case H323Connection::EndedByNoEndPoint : 
cout << "No phone running for " << remoteName; 
break; 
case H323Connection::EndedByConnectFail : 
cout << "Transport error calling " << remoteName; 
break; 
default : 
cout << "Call with " << remoteName << " completed"; 

cout << ", duration " 
<< setprecision(0) << setw(5) 
<< (PTime() - connection.GetConnectionStartTime()) 
<< endl; 


//打開聲音設備時候 
//isEncoding 表示編碼嗎 
//編碼表示向外發送數據, 從聲音設備讀 
//解碼表示從網絡讀出數據, 寫到聲音設備上 
//不同的方向的codec是不同的, 所以在這里有好多文章可以做 
//可以給codec attach上不同的channel根據isEncoding的值 
BOOL SimpleH323EndPoint::OpenAudioChannel(H323Connection & connection, 
BOOL isEncoding, 
unsigned bufferSize, 
H323AudioCodec & codec) 

if (H323EndPoint::OpenAudioChannel(connection, isEncoding, bufferSize, codec)) 
return TRUE; 

cerr << "Could not open sound device "; 
if (isEncoding) 
cerr << GetSoundChannelRecordDevice(); 
else 
cerr << GetSoundChannelPlayDevice(); 
cerr << " - Check permissions or full duplex capability." << endl; 

return FALSE; 

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 
EndPoint的實現分析完畢. 


H323Connection的實現, 這個Connection的實現太簡單了.可能不足以說明問題 
我也沒什么好說的了 
/////////////////////////////////////////////////////////////// 

SimpleH323Connection::SimpleH323Connection(SimpleH323EndPoint & ep, unsigned ref) 
: H323Connection(ep, ref) 




BOOL SimpleH323Connection::OnStartLogicalChannel(H323Channel & channel) 

if (!H323Connection::OnStartLogicalChannel(channel)) 
return FALSE; 

cout << "Started logical channel: "; 

switch (channel.GetDirection()) { 
case H323Channel::IsTransmitter : 
cout << "sending "; 
break; 

case H323Channel::IsReceiver : 
cout << "receiving "; 
break; 

default : 
break; 


cout << channel.GetCapability() << endl; 

return TRUE; 




void SimpleH323Connection::OnUserInputString(const PString & value) 

cout << "User input received: \"" << value << \" << endl; 

// End of File /////////////////////////////////////////////////////////////// 

總結一下基本的過程就是創建一個H323Endpoint的對象endpoint, 創建對象后這個程序就有好多個小的線程被創建了.然后EndPoint開始監聽來電, 之后判斷是否直接呼叫另一個h323的Endpoint. 然后就是一個for循環, 判斷標準的輸入, 并通過當前的token來lock一個Connection, 每個連接會有唯一的一個token, lock的意思是說, 在被lock的期間是不能被釋放的. 根據輸入的字符決定對得到的連接做什么. 

OpenAM: 
是個answer machine, 自動應答機, 或者是留言機. 實現的很簡單, 里面對OpenH323使用的思路很有價值. 
./openam –n –-g711message sample_message.wav 
這樣運行, 用netmeeting 連接一下這個IP, netmeeting就會放一段簡單的英語, 測測你的英語聽力, 他在講什么? 
這個程序是一個支持多連接和并發連接的Endpoint, 但是他沒有使用真正的聲音設備, 放出的音從一個已有的wav文件中讀出來, 遠程用戶的留言被錄到一個文件里, 文件的名字表示了是什么時間錄制的. 
主要的思路是給在連接打開聲音通道的時候, 根據isEncoding的值區別是錄音還是放音,如果是錄音, 將讀文件的Channel附加在codec上, 相反寫文件的Channel附件在codec上,注意這是兩個codec. 
這個東西給了我們一個方法, 如何使用文件IO來代替聲音設備的IO來使用OpenH323. 


這是main.h 

#ifndef _Voxilla_MAIN_H 
#define _Voxilla_MAIN_H 

#include 
#include 
#include 
#include 
#include 

#include 
#include 

//主進程 
class OpenAm : public PProcess 

PCLASSINFO(OpenAm, PProcess) 

public: 
OpenAm(); 
~OpenAm(); 

void Main(); 
void RecordFile(PArgList & args); 
void PlayFile(PArgList & args); 

protected: 
long GetCodec(const PString & codecname); 
OpalLineInterfaceDevice * GetDevice(const PString & device); 
}; 

//H323 端點 
class MyH323EndPoint : public H323EndPoint 

PCLASSINFO(MyH323EndPoint, H323EndPoint); 

public: 
MyH323EndPoint(unsigned callLimit, 
const PString & runCmd, 
const PDirectory & dir, 
int flags); 

// overrides from H323EndPoint 
virtual H323Connection * CreateConnection(unsigned callReference); 
BOOL OnIncomingCall(H323Connection &, const H323SignalPDU &, H323SignalPDU &); 

// new functions 
BOOL Initialise(PConfigArgs & args); 

PString GetGSMOGM() const { return gsmOgm; } 
void SetGSMOGM(const PString & s) { gsmOgm = s; } 

PString GetG711OGM() const { return g711Ogm; } 
void SetG711OGM(const PString & s) { g711Ogm = s; } 

PString GetLPC10OGM() const { return lpc10Ogm; } 
void SetLPC10OGM(const PString & s) { lpc10Ogm = s; } 

#ifdef SPEEX_CODEC 
PString GetSPEEXOGM() const { return speexOgm; } 
void SetSPEEXOGM(const PString & s) { speexOgm = s; } 
#endif 

PString GetG7231OGM() const { return g7231Ogm; } 
void SetG7231OGM(const PString & s) { g7231Ogm = s; } 

unsigned GetCallLimit() const { return callLimit; } 
PString GetRunCmd() const { return runCmd; } 
PDirectory GetDirectory() const { return dir; } 

void SetRecordWav(const BOOL rec){ recordWav = rec; } 
BOOL GetRecordWav() const { return recordWav; } 

enum { 
DeleteAfterRecord = 0x01, 
NoRecordG7231 = 0x02, 
HangupAfterPlay = 0x04 
}; 

BOOL GetDeleteAfterRecord() const { return flags & DeleteAfterRecord; } 
BOOL GetNoRecordG7231() const { return flags & NoRecordG7231; } 
BOOL GetHangupAfterPlay() const { return flags & HangupAfterPlay; } 

protected: 
unsigned callLimit; 
PString pcmOgm, g711Ogm, gsmOgm, lpc10Ogm, g7231Ogm, runCmd; 
#ifdef SPEEX_CODEC 
PString speexOgm; 
#endif 
PDirectory dir; 
int flags; 
BOOL recordWav; 
}; 

class PCM_RecordFile; 
class MyH323Connection; 
PQUEUE(PStringQueue, PString); 
// Out Going Channel OGM 
//就是發送語音的通道 
//即是讀文件的通道 
class PCM_OGMChannel : public PIndirectChannel 

PCLASSINFO(PCM_OGMChannel, PIndirectChannel); 

public: 
PCM_OGMChannel(MyH323Connection & conn); 

BOOL Read(void * buffer, PINDEX amount); 
void PlayFile(PFile * chan); 

BOOL Close(); 

void QueueFile(const PString & cmd); 
void FlushQueue(); 

void SetRecordTrigger(); 
void SetHangupTrigger(); 

void SetPlayOnce() { playOnce = TRUE; } 

protected: 
virtual BOOL ReadFrame(PINDEX amount); 
virtual void CreateSilenceFrame(PINDEX amount); 
virtual void Synchronise(PINDEX amount); 
virtual BOOL IsWAVFileValid(PWAVFile *chan); 

BOOL AdjustFrame(void * buffer, PINDEX amount); 

PStringQueue playQueue; 

MyH323Connection & conn; 
PMutex chanMutex; 
int silentCount; 
int totalData; 
BOOL recordTrigger, hangupTrigger; 
BOOL closed; 
BOOL playOnce; 

PAdaptiveDelay ogm_delay; 

PBYTEArray frameBuffer; 
PINDEX frameLen, frameOffs; 
}; 
//這個是之讀的文件是個g723編碼的文件, 暫時不研究這個類相關的一切 
class G7231_OGMChannel : public PCM_OGMChannel 

PCLASSINFO(G7231_OGMChannel, PCM_OGMChannel); 
public: 
G7231_OGMChannel(MyH323Connection & conn); 

protected: 
BOOL ReadFrame(PINDEX amount); 
void CreateSilenceFrame(PINDEX amount); 
void Synchronise(PINDEX amount); 
BOOL IsWAVFileValid(PWAVFile *chan); 
}; 

//連接,都是從這個類實例出來的 
class MyH323Connection : public H323Connection 

PCLASSINFO(MyH323Connection, H323Connection); 

public: 
MyH323Connection(MyH323EndPoint &, unsigned); 
~MyH323Connection(); 

// overrides from H323Connection 
BOOL OpenAudioChannel(BOOL, unsigned, H323AudioCodec & codec); 
AnswerCallResponse OnAnswerCall(const PString &, const H323SignalPDU &, H323SignalPDU &); 
BOOL OnStartLogicalChannel(H323Channel & channel); 
void OnUserInputString(const PString & value); 

// new functions 
void StartRecording(); 
void Hangup(); 

void SetE164Number(const PString & _num) 
{ e164Number = _num; } 

PString GetE164Number() const 
{ return e164Number; } 

protected: 
void OnUserInputChar(char ch); 
BOOL StartMenu(int menuNumber); 
BOOL ProcessMenuCmd(const PString & cmdStr); 

const MyH323EndPoint & ep; 
PString product; 
PTime callStartTime; 
PTime recordStartTime; 
PString basename; 
PFilePath recordFn; 
PString transmitCodecName, receiveCodecName; 
BOOL recordTrigger; 
PMutex connMutex; 

PCM_RecordFile * recordFile; 
PCM_OGMChannel * ogmChannel; 

PString digits, lastDigits; 
int currentMenu; 
PStringList menuNames; 

PString securityToken, e164Number; 
}; 

//是錄音 
class PCM_RecordFile : public PIndirectChannel 

PCLASSINFO(PCM_RecordFile, PIndirectChannel) 

public: 
PCM_RecordFile(MyH323Connection & conn, const PFilePath & fn, unsigned callLimit); 
~PCM_RecordFile(); 

BOOL Write(const void * buf, PINDEX len); 
BOOL Close(); 
void StartRecording(); 

virtual void DelayFrame(PINDEX len); 
virtual BOOL WriteFrame(const void * buf, PINDEX len); 

BOOL WasRecordStarted() const { return recordStarted; } 

protected: 
MyH323Connection & conn; 
PTime finishTime; 
PFilePath fn; 
unsigned callLimit; 
BOOL recordStarted; 
BOOL timeLimitExceeded; 
BOOL closed; 
BOOL isPCM; 
BOOL dataWritten; 
PAdaptiveDelay delay; 
PMutex pcmrecordMutex; 
PFile *fileclass; // will point to a PWAVFile or PFile class 
}; 
//錄的結果是個g723文件, 我們暫時不考慮這個類相關的一切 
class G7231_RecordFile : public PCM_RecordFile 

PCLASSINFO(G7231_RecordFile, PCM_RecordFile); 

public: 
G7231_RecordFile(MyH323Connection & conn, const PFilePath & fn, unsigned callLimit); 
void DelayFrame(PINDEX len); 
BOOL WriteFrame(const void * buf, PINDEX len); 
}; 


#endif // _Voxilla_MAIN_H 


// End of File /////////////////////////////////////////////////////////////// 


這是main.cxx 
#include 
#include 

#include "version.h" 
#include "lpc10codec.h" 

#ifdef SPEEX_CODEC 
#include "speexcodec.h" 
#endif 

#include "mscodecs.h" 
#include "opalvxml.h" 
#include "main.h" 

PCREATE_PROCESS(OpenAm); 

#define new PNEW 

//default 錄音時間 
#define DEFAULT_MSG_LIMIT 30 
#define DEFAULT_CALL_LOG "call_log.txt" 

#define G7231_SAMPLES_PER_BLOCK 240 

#define CHECK_PCM 1 
#define CHECK_G7231 2 

#define MENU_PREFIX "UserMenu-" 

static PMutex logMutex; 
static PTextFile logFile; 
static PFilePath logFilename = DEFAULT_CALL_LOG; 

PString G7231Ext = ".g723"; 
PString WAVExt = ".wav"; 
PString PCMExt = ".sw"; 

//關于log的一切先不用看 
static void LogMessage(const PString & str) 

PTime now; 
PString msg = now.AsString("hh:mm:ss dd/MM/yyyy") & str; 
logMutex.Wait(); 

if (!logFile.IsOpen()) { 
logFile.Open(logFilename, PFile::ReadWrite); 
logFile.SetPosition(0, PFile::End); 


logFile.WriteLine(msg); 

logFile.Close(); 

logMutex.Signal(); 


static void LogCall(const PFilePath & fn, 
const PString & from, 
const PString & user, 
unsigned len, 
const PString & codec, 
const PString & product) 

PString addr = from; 
LogMessage(addr & "\"" + user + "\"" & PString(PString::Unsigned, len) & codec & "\"" + product + "\"" & "\"" + fn + "\""); 



/////////////////////////////////////////////////////////////// 

OpenAm::OpenAm() 
: PProcess("OpenH323 Project", "OpenAM", 
MAJOR_

在線咨詢

點擊這里給我發消息 售前咨詢專員

點擊這里給我發消息 售后服務專員

在線咨詢

銷售電話

24小時免費咨詢

0771-5523920

銷售電話

微信掃一掃

微信聯系
返回頂部
主站蜘蛛池模板: 亚洲精品无码成人a片蜜臀 国产乱码一区二区三区爽爽爽 | 国产精品无码一本二本三本色 | 国产精品国产三级国产av中文 | 丰满的少妇xxxxx青青青 | 久久天天躁夜夜躁狠狠躁综合 | 国产精品午睡沙发系列 | 蕾丝av无码专区在线观看 | 强插女教师av在线 | 免费观看性欧美大片无片 | 伊伊人成亚洲综合人网香 | 东北老女人高潮大喊舒服死了 | 久视频精品线在线观看 | 国产成人精品无码一区二区 | 亚洲欧美乱综合图片区小说区 | 久久国产劲暴∨内射新川 | 精品丝袜国产自在线拍小草 | 无码一区二区三区亚洲人妻 | 欧洲美女粗暴牲交免费观看 | 亚洲国产成人片在线观看 | 成av人片在线观看www | 亚洲一区二区三区影院 | 欧美va天堂在线电影 | 中文字幕无码专区一va亚洲v专区在线 | 人妻熟妇乱又伦精品视频中文字幕 | 欧美日韩中文国产一区发布 | 精品无码av无码专区 | 97人妻碰碰视频免费上线 | 亚洲一区二区三区乱码在线欧洲 | 人禽交 欧美 网站 | 日韩欧美一中文字暮专区 | 毛茸茸厕所偷窥xxxx | 在线成人国产天堂精品av | 国产偷人激情视频在线观看 | 亚洲精品成人片在线播放 | 人妻熟女一二三区夜夜爱 | 最新欧美精品一区二区三区 | 亚洲国产精品隔壁老王 | 国产高清色高清在线观看 | 国产99久60在线视频 | 传媒 | 肉体裸交137日本大胆摄影 | 久久青青草原国产毛片 |