JiZzJiZZ国产免费A_国产午夜成人AV在线播放_亚洲欧美在线观看一区二区_91久久久久精品无嫩草影院_欧美高清一区二区三区_欧美日韩国产码高清_亚洲精品国产电影_亚洲AV无码专区亚洲AV桃
廣告招募

視頻會(huì)議h323協(xié)議棧開發(fā)入門

2025年03月10日 09:55:44      來源:南寧匯研科技有限公司 >> 進(jìn)入該公司展臺      閱讀量:12

分享:

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

以linux平臺為例:
使用pwlib, 在成功編譯之后, 到$(PWLIBDIR)/SAMPLES/
這里是一些例子, hello_world 是個(gè)非常簡單的工程, 從這里我們可以看到如何寫使用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
關(guān)鍵是包含了一個(gè)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的之類的進(jìn)行編譯, 需要的頭文件庫都會(huì)替你安排好. 編譯的結(jié)果就會(huì)放在obj_linux_x86_xx, xx 表示你用的是debug編譯還是其他, 如果是debug, xx就是d.

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

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

#Ifndef _SimpleH323_MAIN_H
#define _SimpleH323_MAIN_H
//避免頭文件重復(fù)包含

#Include

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

public:
SimpleH323EndPoint();
~SimpleH323EndPoint();

// overrides from H323EndPoint
// 重載H323EndPoint的函數(shù)

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

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

protected:
BOOL autoAnswer;
PString busyForwardParty;
};

class SimpleH323Connection : public H323Connection
{
PCLASSINFO(SimpleH323Connection, H323Connection);

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

//重載了兩個(gè)父類的函數(shù)

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

protected:
// 快速連接??
BOOL noFastStart;
};
class SimpleH323Process : public PProcess
{
//主進(jìn)程, 類似VC的用戶界面線程,
//他是整個(gè)程序的入口點(diǎn), 和結(jié)束點(diǎn)
//創(chuàng)建了EndPoint對象后會(huì)有好幾個(gè)線程啟動(dòng)
//這個(gè)就是主線程
PCLASSINFO(SimpleH323Process, PProcess)

public:
SimpleH323Process();
~SimpleH323Process();
//這個(gè)函數(shù)會(huì)被自動(dòng)調(diào)用, 是我們程序的入口了
void Main();
protected:

//這個(gè)H323端點(diǎn)對象
SimpleH323EndPoint * endpoint;
};

#Endif // _SimpleH323_MAIN_H

下面是main.cpp 所有的類的實(shí)現(xiàn)了

#Include

#Ifdef __GNUC__
#define H323_STATIC_LIB
#Endif

#Include "main.h"
#Include "../../version.h"


#define new PNEW

// 這個(gè)東西里邊可能封裝了標(biāo)準(zhǔn)的main函數(shù)
PCREATE_PROCESS(SimpleH323Process);


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

//幾個(gè)宏都在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.
// 分析命令行參數(shù), 略去數(shù)行
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)) {
//如果沒有參數(shù)或者參數(shù)是h, 就輸出如何使用, 此處略去數(shù)行
}
//這個(gè)東西暫時(shí)不管
#If PTRACING
#Endif

// Create the H.323 endpoint and initialise it
// H323 EndPoint 創(chuàng)建了, 并且把命令參數(shù)傳過去初始化, 初始化的時(shí)候做了一些事
endpoint = new SimpleH323EndPoint;
if (!endpoint->Initialise(args))
return;
//看看命令行里是不是想直接呼叫另一個(gè)H323的endpoint.有沒有l(wèi)(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
// 簡單的用戶界面, 會(huì)有一個(gè)提示>
// 取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就是怕別的線程把它給刪了
//因?yàn)檫@里正用著呢
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 函數(shù)結(jié)束

// 自己的Init函數(shù)
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();
// 設(shè)定改Endpoint的username
SetLocalUserName(aliases[0]);
// 設(shè)定Aliases 就是每個(gè)Endpoint可以有好多名字的意思
for (PINDEX i = 1; i < aliases.GetSize(); i++)
AddAliasName(aliases[i]);
}

// Set the various options
//設(shè)置檢測否

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();
//設(shè)定音頻抖動(dòng)的, 應(yīng)該影響到接收的緩存
if (jitter >= 20 && jitter <= 10000)
SetMaxAudioDelayJitter(jitter);
else {
cerr << "Jitter should be between 20 milliseconds and 10 seconds." << endl;
return FALSE;
}
}

//設(shè)定聲音設(shè)備
//也可以不用聲音設(shè)備, 比如Openh323工程的子項(xiàng)目 OpenAM和OpenMCU
//都使演示了如何不使用聲音物理設(shè)備的方法, 我想那里邊的東西會(huì)對某些朋友們
//的需求比較合適
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;

// 設(shè)定decode encode的能力
// H323 EndPoint在真正進(jìn)行數(shù)據(jù)通訊之前要進(jìn)行能力的交換, 說明自己能夠接收和發(fā)送什么標(biāo)準(zhǔn)的數(shù)據(jù), 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;

//啟動(dòng)一個(gè)來電的
//可以使用配置的端口, 也可以使用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相關(guān)的東西, 先不討論了
// 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;
}

//設(shè)定音頻設(shè)備, 沒什么可講的
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;
}

//這個(gè)函數(shù)很簡單但是非常關(guān)鍵, 是從EndPoint中重載過來的.
//本來是return new H323Connection()的, 現(xiàn)在改成Simplexxx
//自己實(shí)現(xiàn)的一個(gè)Connection, 這樣當(dāng)Endpoint里面調(diào)用
//Connection的一些東西的時(shí)候, 實(shí)際上運(yùn)行的是Simplexxx
//的實(shí)現(xiàn), 看到C++的好處了吧, C里用函數(shù)指針也可以實(shí)現(xiàn), 沒有
//C++這么native.
H323Connection * SimpleH323EndPoint::CreateConnection(unsigned callReference)
{
return new SimpleH323Connection(*this, callReference);
}
//沒什么東西, 關(guān)鍵是看看這個(gè)東西的調(diào)用的時(shí)機(jī)
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);
}


//這個(gè)東西, 很有用, H323Connection的類里也有這個(gè)虛函數(shù)
//返回的值決定告訴遠(yuǎn)程的連接者是否接收這份連接請求
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;
}


//連接建立時(shí)候
void SimpleH323EndPoint::OnConnectionEstablished(H323Connection & connection,
const PString & token)
{
currentCallToken = token;
cout << "In call with " << connection.GetRemotePartyName() << endl;
}

//連接斷開時(shí)候
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;
}

//打開聲音設(shè)備時(shí)候
//isEncoding 表示編碼嗎
//編碼表示向外發(fā)送數(shù)據(jù), 從聲音設(shè)備讀
//解碼表示從網(wǎng)絡(luò)讀出數(shù)據(jù), 寫到聲音設(shè)備上
//不同的方向的codec是不同的, 所以在這里有好多文章可以做
//可以給codec attach上不同的channel根據(jù)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的實(shí)現(xiàn)分析完畢.


H323Connection的實(shí)現(xiàn), 這個(gè)Connection的實(shí)現(xiàn)太簡單了.可能不足以說明問題
我也沒什么好說的了
///////////////////////////////////////////////////////////////

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 ///////////////////////////////////////////////////////////////

總結(jié)一下基本的過程就是創(chuàng)建一個(gè)H323Endpoint的對象endpoint, 創(chuàng)建對象后這個(gè)程序就有好多個(gè)小的線程被創(chuàng)建了.然后EndPoint開始來電, 之后判斷是否直接呼叫另一個(gè)h323的Endpoint. 然后就是一個(gè)for循環(huán), 判斷標(biāo)準(zhǔn)的輸入, 并通過當(dāng)前的token來lock一個(gè)Connection, 每個(gè)連接會(huì)有的一個(gè)token, lock的意思是說, 在被lock的期間是不能被釋放的. 根據(jù)輸入的字符決定對得到的連接做什么.

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


這是main.h

#Ifndef _Voxilla_MAIN_H
#define _Voxilla_MAIN_H

#Include
#Include
#Include
#Include
#Include

#Include
#Include

//主進(jìn)程
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 端點(diǎn)
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
//就是發(fā)送語音的通道
//即是讀文件的通道
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;
};
//這個(gè)是之讀的文件是個(gè)g723編碼的文件, 暫時(shí)不研究這個(gè)類相關(guān)的一切
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);
};

//連接,都是從這個(gè)類實(shí)例出來的
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
};
//錄的結(jié)果是個(gè)g723文件, 我們暫時(shí)不考慮這個(gè)類相關(guān)的一切
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 錄音時(shí)間
#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";

//關(guān)于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_


推薦文章:
版權(quán)與免責(zé)聲明:
1.凡本網(wǎng)注明"來源:中美貿(mào)易網(wǎng)"的所有作品,版權(quán)均屬于中美貿(mào)易網(wǎng),轉(zhuǎn)載請必須注明中美貿(mào)易網(wǎng)。違反者本網(wǎng)將追究相關(guān)法律責(zé)任。
2.企業(yè)發(fā)布的公司新聞、技術(shù)文章、資料下載等內(nèi)容,如涉及侵權(quán)、違規(guī)遭投訴的,一律由發(fā)布企業(yè)自行承擔(dān)責(zé)任,本網(wǎng)有權(quán)刪除內(nèi)容并追溯責(zé)任。
3.本網(wǎng)轉(zhuǎn)載并注明自其它來源的作品,目的在于傳遞更多信息,并不代表本網(wǎng)贊同其觀點(diǎn)或證實(shí)其內(nèi)容的真實(shí)性,不承擔(dān)此類作品侵權(quán)行為的直接責(zé)任及連帶責(zé)任。其他媒體、網(wǎng)站或個(gè)人從本網(wǎng)轉(zhuǎn)載時(shí),必須保留本網(wǎng)注明的作品來源,并自負(fù)版權(quán)等法律責(zé)任。 4.如涉及作品內(nèi)容、版權(quán)等問題,請?jiān)谧髌钒l(fā)表之日起一周內(nèi)與本網(wǎng)聯(lián)系。

JiZzJiZZ国产免费A_国产午夜成人AV在线播放_亚洲欧美在线观看一区二区_91久久久久精品无嫩草影院_欧美高清一区二区三区_欧美日韩国产码高清_亚洲精品国产电影_亚洲AV无码专区亚洲AV桃