TAPIEventProcess.cpp
上传用户:fast168168
上传日期:2010-01-05
资源大小:337k
文件大小:35k
- #include "StdAfx.h"
- #include <tapi3.h>
- #include "tapieventprocess.h"
- const int Ver_Win2000 = 1;
- const int Ver_WinXP = 2;
- const int Timer_ID = 1;
- const int Max_Rec_Time = 60000;
- const DWORD ADDRESSLENGTH = 128;
- ///////////////////////////////////////////////////////////////////
- // CallEventNotification::Event
- //
- // 1、这是ITCallEventNotification接口中的唯一的方法,当有事件被触发时,
- // TAPI 3.0 就会调用这个函数。
- // 2、这个函数里仅仅向用户界面线程(在本程序中可以理解为主对话框)发送一个消息,
- // 这样做的目的是在 TAPI 的 回调(callback)线程中包含尽量少的处理过程
- //
- ///////////////////////////////////////////////////////////////////
- HRESULT
- STDMETHODCALLTYPE
- CTAPIEventNotification::Event(
- TAPI_EVENT TapiEvent,
- IDispatch * pEvent
- )
- {
- //
- // 调用AddRef,计数器加1.
- //
- pEvent->AddRef();
- //
- // 向 UI 线程发送消息
- //
- PostMessage(
- m_hWnd,
- WM_PRIVATETAPIEVENT,
- (WPARAM) TapiEvent,
- (LPARAM) pEvent
- );
- return S_OK;
- }
- /*
- void CTAPIEventNotification::gg()
- {
- PostMessage(
- m_hWnd,
- WM_PRIVATETAPIEVENT,
- 0,
- 0
- );
- }
- */
- //////////////////////////////////////////////////////////////////////////////////////////////////////
- //
- // calss CTapi 实现代码
- //
- // 说明:
- // 1. 本类基于 TAPI3.1, 对 TAPI 的功能进行定制,满足特定需要
- // 功能:
- // 1. 对给定的电话号码呼叫
- // 2. 当有电话呼入时,给对方播放本地事先指定的声音文件
- // 3. 接受电话另一方的按键,分别对不同按键作出响应
- // 4. 具有来录音功能
- // 注意:
- // 1. 对 CTapi 类初始化时需要传入当前 UI(用户界面)的句柄,通过这个句柄向 UI 线程发送消息,在 UI
- // 线程中对 TAPI EVENT 进行响应。
- // 2. 需要 class CTAPIEventNotification 辅助,完成对事件的注册
- //////////////////////////////////////////////////////////////////////////////////////////////////////
- CTapi::CTapi()
- {
- m_pTapi = NULL;
- m_pAddress = NULL;
- m_pCall = NULL;
- m_AutoAnswer = false;
- m_pPlayFileTerm = NULL;
- m_pRecordFileTerm = NULL;
- m_Advise = 0;
- m_hWnd = 0;
- m_dwMessages = 0;
- m_Version = 0;
- m_WelcomeFileName = "Welcome.wav";
- m_RecFileName = "Message";
- m_RecFileExt = "avi";
- }
- CTapi::~CTapi()
- {
- }
- //////////////////////////////////////////////////////////////
- // InitializeTapi
- //
- // TAPI 初始化
- ///////////////////////////////////////////////////////////////
- HRESULT CTapi::InitializeTapi()
- {
- HRESULT hr;
- //
- // 初始化 TAPI object
- //
- hr = CoCreateInstance(
- CLSID_TAPI,
- NULL,
- CLSCTX_INPROC_SERVER,
- IID_ITTAPI,
- (LPVOID *)&m_pTapi
- );
- if ( FAILED(hr) )
- {
- AfxMessageBox("初始化 TAPI 失败!");
- return hr;
- }
- //
- // 调用 initialize.
- // 本函数必须在其它所有 TAPI 函数被调用之前调用
- //
- hr = m_pTapi->Initialize();
- if ( FAILED(hr) )
- {
- AfxMessageBox("初始化 TAPI 失败!");
- m_pTapi->Release();
- m_pTapi = NULL;
- return hr;
- }
- //
- // 创建我们自己的事件处理实例并注册之,参见 CTAPIEventNodtification 的定义
- //
- CTAPIEventNotification *pTAPIEventNotification = new CTAPIEventNotification;
- //
- //将 CTapi中的窗口句柄传给 CTAPIEventNotification,由 CTAPIEventNotification 向UI进程发送消息
- //
- pTAPIEventNotification->m_hWnd = m_hWnd;
- hr = RegisterTapiEventInterface(pTAPIEventNotification);
- //
- // 这里已经不再需要 CALLBACK 对象,因为 TAPI 本身会在其内部创建并保留这个对象
- //
- pTAPIEventNotification->Release();
- OSVERSIONINFOEX osvi;
- BOOL bOsVersionInfoEx;
- //
- // 因为 TE_FILETERMINAL 只有在 Windows XP 下才支持,因此这里需要先判断操作系统的版本
- // 如果在 Windows XP 以下注册 TE_FILETERMINAL 消息时,这是 TAPI 将无法接受到任何消息!
- //
- // 获取操作系统版本,使用到结构体 OSVERSIONINFOEX
- // 如果调用失败,再尝试用 OSVERSIONINFO
- //
- ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
- osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
- if( !(bOsVersionInfoEx = GetVersionEx ((OSVERSIONINFO *) &osvi)) )
- {
- // 如果使用 OSVERSIONINFOEX 失败, 尝试 OSVERSIONINFO.
- osvi.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
- if (! GetVersionEx ( (OSVERSIONINFO *) &osvi) )
- return FALSE;
- }
- if(osvi.dwPlatformId == VER_PLATFORM_WIN32_NT)
- {
- // 获得操作系统版本,Win2000以下忽略
- if ( osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0 )
- {
- // Windows 2000
- m_Version = Ver_Win2000;
- }
- if ( osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 1 )
- {
- // Windows XP
- m_Version = Ver_WinXP;
- }
- }
- else
- {
- AfxMessageBox("您的操作系统版本太低,本系统至少要求在 Win2000 环境下");
- return E_FAIL;
- }
- //
- // 对我们所希望处理的事件设置事件进行事件过滤设置
- //
- if(m_Version == Ver_Win2000)
- {
- hr = m_pTapi->put_EventFilter(TE_CALLNOTIFICATION |
- TE_CALLSTATE |
- TE_CALLMEDIA |
- TE_DIGITEVENT );
- }
- else if(m_Version == Ver_WinXP)
- {
- hr = m_pTapi->put_EventFilter(TE_CALLNOTIFICATION |
- TE_CALLSTATE |
- TE_CALLMEDIA |
- TE_DIGITEVENT |
- TE_FILETERMINAL); // TE_FILETERMINAL 只有在 WinXP 下才支持
- }
-
- if( FAILED(hr) )
- {
- AfxMessageBox("注册消息失败,系统无法接收到任何消息,但可以对其它号码呼叫");
- }
- //
- // 查找我们需的的 Address (本程序中为 Modem)并在其上开始侦听
- //
- hr = ListenOnAddresses();
- if ( FAILED(hr) )
- {
- //
- //如果失败,释放所之前所审请的资源
- //在 ListenOnAddresses 中已有错误信息输出,此处不需要再次输出 Errmsg
- //
- m_pTapi->Release();
- m_pTapi = NULL;
- return hr;
- }
- return S_OK;
- }
- ///////////////////////////////////////////////////////////////
- // ShutdownTapi
- //
- // 功能:关闭 Tapi
- ///////////////////////////////////////////////////////////////
- void CTapi::ShutdownTapi()
- {
- //
- // 如果仍有一个 Call 存在的话,则挂断并释放之
- //
- DisconnectTheCall();
- ReleaseTheCall();
-
- //
- // 调用 Sleep 目的是为了留出一段时间让 TAPI 来挂断电话。
- // 因为对话框虽然会立即消失,但 TAPI 进程仍有可能未结束
- //
- Sleep(5000);
- //
- // 释放主要的对象
- //
- if (NULL != m_pTapi)
- {
- m_pTapi->Shutdown();
- m_pTapi->Release();
- }
- }
- ///////////////////////////////////////////////////////////////////////////
- // RegisterTapiEventInterface
- //
- // 功能:注册 Tapi 事件接口
- ///////////////////////////////////////////////////////////////////////////
- HRESULT CTapi::RegisterTapiEventInterface(CTAPIEventNotification *pTAPIEventNotification)
- {
- HRESULT hr = S_OK;
- IConnectionPointContainer * pCPC;
- IConnectionPoint * pCP;
- //
- // 获得连接点容器
- //
- hr = m_pTapi->QueryInterface(
- IID_IConnectionPointContainer,
- (void **)&pCPC
- );
- if ( FAILED(hr) )
- {
- return hr;
- }
- //
- // 获得连接点
- //
- hr = pCPC->FindConnectionPoint(
- IID_ITTAPIEventNotification,
- &pCP
- );
- pCPC->Release();
- if ( FAILED(hr) )
- {
- return hr;
- }
- // m_Advise 用于取消注册之用
- hr = pCP->Advise(
- pTAPIEventNotification,
- &m_Advise
- );
- pCP->Release();
- return hr;
- }
- //////////////////////////////////////////////////////////////
- // AddressSupportsMediaType
- //
- // 功能:验证给定的 Address 是否支持 给定的 MediaType
- // 返回:TRUE——支持;FALSE——不支持
- //////////////////////////////////////////////////////////////
- BOOL CTapi::AddressSupportsMediaType(
- ITAddress * pAddress,
- long lMediaType
- )
- {
- VARIANT_BOOL bSupport = VARIANT_FALSE;
- ITMediaSupport * pMediaSupport;
-
- if ( SUCCEEDED( pAddress->QueryInterface( IID_ITMediaSupport,
- (void **)&pMediaSupport ) ) )
- {
- //
- // 通过 pMediaSupport 验证 pAddress 是否支持 lMediaType
- //
- pMediaSupport->QueryMediaType(
- lMediaType,
- &bSupport
- );
- pMediaSupport->Release();
- }
- return (bSupport == VARIANT_TRUE);
- }
- ////////////////////////////////////////////////////////////////////////
- // ListenOnAddresses
- //
- // 功能:找到 Modem 所在的 Address,判断是否支持 AudioIn and AudioOut,
- // 如果都支持,则调用 ListenOnThisAddress 开始在此 Address 上侦听
- ////////////////////////////////////////////////////////////////////////
- HRESULT CTapi::ListenOnAddresses()
- {
- HRESULT hr = S_OK;
- IEnumAddress * pEnumAddress;
- ITAddress * pAddress;
- //
- // 列举所有的 Address
- //
- hr = m_pTapi->EnumerateAddresses( &pEnumAddress );
- if ( FAILED(hr) )
- {
- return hr;
- }
- BOOL bModem; //当前 Address 是否为 Modem 所有
- BOOL bState; //当前 Address 状态是否启用
- BOOL bListen; //当前 Address 上侦听是否成功
- while ( TRUE )
- {
- //
- // 获得下一个 Address
- //
- bModem = bState = bListen = FALSE;
- hr = pEnumAddress->Next( 1, &pAddress, NULL );
- if (S_OK != hr)
- {
- break;
- }
- BSTR bstrName; //当前 Address 名称
- ADDRESS_STATE state; //当前 Address 状态
- CString strName; //当前 Address 名称的 ASSIC 表示
- //
- // 获得当前 Address 名称,并判断是否为 Modem 所有,如果不是,结束本次循环,遍历一下 Address
- //
- pAddress->get_AddressName(&bstrName);
- strName = bstrName;
- SysFreeString(bstrName);
- strName.MakeLower();
- if( strName.Find("modem") == -1 )
- continue;
- //
- //找到了modem
- //
- bModem = TRUE;
- pAddress->get_State( &state);
- if( state != AS_INSERVICE)
- continue;
- //
- //上面找到的Modem目前处于启用状态
- //
- bState = TRUE;
- //
- // 当前 Address 是否支持声音
- if ( AddressSupportsMediaType(pAddress, TAPIMEDIATYPE_AUDIO) )
- {
- //
- // 如果支持声音,开始侦听
- //
- hr = ListenOnThisAddress( pAddress );
- if( SUCCEEDED(hr) )
- {
- //
- // 找到一个 Modem 设备,并且监听成功,退出函数
- // 检测当前 Address 能力,是否支持监视所有种类形式的按键
- //
- ITAddressCapabilities *pAddressCaps = NULL;
- long lType = 0;
- hr = pAddress->QueryInterface(IID_ITAddressCapabilities, (void**)&pAddressCaps);
- if ( SUCCEEDED(hr) )
- {
- hr = pAddressCaps->get_AddressCapability( AC_MONITORDIGITSUPPORT, &lType );
- if( SUCCEEDED(hr) )
- {
- if( !(lType & LINEDIGITMODE_DTMF) ) //LINEDIGITMODE_DTMFEND
- AfxMessageBox("您的 Modem 不支持音频拨号检测功能,无法获得音频拨号的电话机的按键");
- else if( !(lType & LINEDIGITMODE_PULSE) )
- AfxMessageBox("您的 Modem 不支持脉冲拨号检测功能,无法获得脉冲拨号的电话机的按键");
- }
- pAddressCaps->Release();
- m_pAddress = pAddress;
- pAddress->Release();
- pEnumAddress->Release();
- return S_OK;
- }
- }
- }
- pAddress->Release();
- }
-
- pEnumAddress->Release();
- //
- // 以下为对应的错误信息提示
- //
- if( !bModem )
- {
- AfxMessageBox("没有找到Modem,请检查您计算机那是否装有Modem");
- return E_FAIL;
- }
- if( !bState )
- {
- AfxMessageBox("系统监测到您机器内有Modem,但当前没又启用,请手工启用Modem之后在运行本程序");
- return E_FAIL;
- }
- if( !bListen )
- {
- AfxMessageBox("使用Modem监听失败,可能是您的 Modem 不支持语音,详情请于服务商联系");
- return E_FAIL;
- }
- return S_OK;
- }
- ///////////////////////////////////////////////////////////////////
- // ListenOnThisAddress
- //
- // 功能:在调用这个函数之前,我们已经对所希望处理的事件进行了过滤设置,
- // 这里通过调用 RegisterCallNotifications 告诉 TAPI 我们将要在这个
- // Address 上触发那些已经设置的事件
- //
- ///////////////////////////////////////////////////////////////////
- HRESULT CTapi::ListenOnThisAddress(
- ITAddress * pAddress
- )
- {
- long lMediaTypes = TAPIMEDIATYPE_AUDIO;
- if ( AddressSupportsMediaType(pAddress, TAPIMEDIATYPE_VIDEO) )
- {
- lMediaTypes |= TAPIMEDIATYPE_VIDEO;
- }
- HRESULT hr;
- long lRegister;
- hr = m_pTapi->RegisterCallNotifications(
- pAddress,
- VARIANT_TRUE,
- VARIANT_TRUE,
- lMediaTypes,
- 0,
- &lRegister
- );
- return hr;
- }
- /////////////////////////////////////////////////////////////////
- // MakeTheCall
- //
- // 功能:设置并呼叫号码
- /////////////////////////////////////////////////////////////////
- HRESULT
- CTapi::MakeTheCall(
- IN DWORD dwAddressType,
- IN PWCHAR szAddressToCall
- )
- {
- HRESULT hr = S_OK;
- BSTR bstrAddressToCall;
- //
- // 检测当前地址是否有效
- //
- if( m_pAddress == NULL)
- return E_FAIL;
- //
- // 这里我们只要求支持声音即可
- //
- long lMediaTypes = TAPIMEDIATYPE_AUDIO;
- //
- // 因为我们在对线路侦听时,已经确定 m_Address 至少支持声音,
- // 已经满足我们要求,不用另做检测
- //
- //
- // 建立呼叫
- //
- bstrAddressToCall = SysAllocString( szAddressToCall );
- hr = m_pAddress->CreateCall( bstrAddressToCall,
- dwAddressType,
- lMediaTypes,
- &m_pCall);
- SysFreeString ( bstrAddressToCall );
- if ( FAILED(hr) )
- {
- AfxMessageBox("建立呼叫失败!");
- return hr;
- }
- //
- // 为当前呼叫选择终端
- //
- hr = SelectTerminalsOnCall(m_pAddress, m_pCall );
- //
- // 正式发出呼叫
- //
- hr = m_pCall->Connect( VARIANT_TRUE );
- if ( FAILED(hr) )
- {
- m_pCall->Release();
- m_pCall = NULL;
- AfxMessageBox("无法连接!");
- return hr;
- }
- return S_OK;
- }
- /////////////////////////////////////////////////////////
- // GetTerminal
- //
- // 为输入 stream 创建默认的 terminal,并通过 ppTerminal 返回
- //
- /////////////////////////////////////////////////////////
- HRESULT CTapi::GetTerminal(
- ITAddress * pAddress,
- ITStream * pStream,
- ITTerminal ** ppTerminal
- )
- {
- //
- // 确定当前传入 stream 的 media type 和 direction
- //
- HRESULT hr;
- long lMediaType;
- TERMINAL_DIRECTION dir;
- hr = pStream->get_MediaType( &lMediaType );
- if ( FAILED(hr) ) return hr;
- hr = pStream->get_Direction( &dir );
- if ( FAILED(hr) ) return hr;
- //
- // 这里我们人处理 Audio 流,对于 Video 流,我们不令做处理
- // 因此只调用 GetDefaultStaticTerminal.
- // 首先,获得 ITTerminalSupport 接口
- //
- ITTerminalSupport * pTerminalSupport;
- hr = pAddress->QueryInterface( IID_ITTerminalSupport,
- (void **)&pTerminalSupport);
- if ( SUCCEEDED(hr) )
- {
- //
- // 为这种 MediaType 和 Direction 获得默认的 Terminal
- //
- hr = pTerminalSupport->GetDefaultStaticTerminal(lMediaType,
- dir,
- ppTerminal);
- pTerminalSupport->Release();
- }
- return hr;
- }
- /////////////////////////////////////////////////////////////////
- // SelectTerminalOnCall
- //
- // 在给定的 call 上中的 stream 中选择一个适当的 terminal
- /////////////////////////////////////////////////////////////////
- HRESULT CTapi::SelectTerminalsOnCall(
- ITAddress * pAddress,
- ITBasicCallControl * pCall
- )
- {
- HRESULT hr;
- //
- // 为当前 call 获得 ITStreamControl 接口
- //
- ITStreamControl * pStreamControl;
- hr = pCall->QueryInterface(IID_ITStreamControl,
- (void **) &pStreamControl);
- if ( SUCCEEDED(hr) )
- {
- //
- // 列举所有的 stream
- //
- IEnumStream * pEnumStreams;
- hr = pStreamControl->EnumerateStreams(&pEnumStreams);
- pStreamControl->Release();
- if ( SUCCEEDED(hr) )
- {
- //
- // 遍历所有的 stream
- //
- ITStream * pStream;
- while ( S_OK == pEnumStreams->Next(1, &pStream, NULL) )
- {
- ITTerminal * pTerminal;
- //
- // 找出当前 stream 的 media type 和 direction,
- // 并且为这种 media type 和 direction 创建默认的 terminal
- //
- hr = GetTerminal(pAddress,
- pStream,
- &pTerminal);
- if ( SUCCEEDED(hr) )
- {
- //
- // 对当前的 Stream 选择上面获得的 Terminal
- //
- hr = pStream->SelectTerminal(pTerminal);
- pTerminal->Release();
- }
- pStream->Release();
- }
- pEnumStreams->Release();
- }
- }
- return hr;
- }
- /////////////////////////////////////////////////////////////////
- // PreparePlay
- //
- // 功能:播放给定的声音文件
- /////////////////////////////////////////////////////////////////
- HRESULT
- CTapi::PreparePlay(CString strFileName)
- {
- //
- // 检测当前的 call object 是否有效
- //
- if (NULL == m_pCall)
- {
- AfxMessageBox("AnswerTheCall: Call object 无效");
- return E_UNEXPECTED;
- }
- //
- // 如果 m_pPlayFileTerm 不是NULL,先释放之
- //
- if(NULL != m_pPlayFileTerm)
- {
- m_pPlayFileTerm->Release();
- m_pPlayFileTerm = NULL;
- }
- //
- // 为请求终端作准备 - 需要 ITBasicCallControl2 接口,只有在 TAPI3.1 中才支持
- //
- ITBasicCallControl2* pITBCC2 = NULL;
- HRESULT hr = m_pCall->QueryInterface( IID_ITBasicCallControl2, (void**)&pITBCC2 );
- if(FAILED(hr))
- {
- AfxMessageBox("AnswerTheCall: QI ITBasicCallControl2 failed");
- m_pCall->Disconnect(DC_NORMAL);
- return hr;
- }
- //
- // 为请求终端作准备 - need CLSID BSTR
- //
- BSTR bstrCLSID = NULL;
- hr = StringFromCLSID(CLSID_FilePlaybackTerminal, &bstrCLSID);
- if(FAILED(hr))
- {
- pITBCC2->Release();
- AfxMessageBox("AnswerTheCall: StringFromCLSID failed");
- m_pCall->Disconnect(DC_NORMAL);
- return hr;
- }
- //
- //正式请求终端,需要媒体流类型和媒体流方向
- //
- hr = pITBCC2->RequestTerminal(bstrCLSID,
- TAPIMEDIATYPE_AUDIO,
- TD_CAPTURE,
- &m_pPlayFileTerm);
- //
- //释放内存
- //
- ::CoTaskMemFree(bstrCLSID);
- if(FAILED(hr))
- {
- pITBCC2->Release();
- AfxMessageBox("AnswerTheCall: RequestTerminal failed");
- m_pCall->Disconnect(DC_NORMAL);
- return hr;
- }
- //
- // 为设置文件名作准备
- //
- BSTR bstrFileName = strFileName.AllocSysString();
- if(NULL == bstrFileName)
- {
- m_pPlayFileTerm->Release();
- m_pPlayFileTerm = NULL;
- pITBCC2->Release();
- AfxMessageBox("AnswerTheCall: SysAllocString for play list failed");
- m_pCall->Disconnect(DC_NORMAL);
- return E_OUTOFMEMORY;
- }
- //
- // 为 m_pPlayFileTerm 设置文件名
- //
- hr = PutPlayList(m_pPlayFileTerm, bstrFileName);
- //
- // 释放内存
- //
- ::SysFreeString(bstrFileName);
- if(FAILED(hr))
- {
- m_pPlayFileTerm->Release();
- m_pPlayFileTerm = NULL;
- pITBCC2->Release();
- AfxMessageBox("AnswerTheCall: PutPlayList failed");
- m_pCall->Disconnect(DC_NORMAL);
- return hr;
- }
- //
- // 用 ITBasicCallControl2 选择终端
- //
- hr = pITBCC2->SelectTerminalOnCall(m_pPlayFileTerm);
- if(FAILED(hr))
- {
- m_pPlayFileTerm->Release();
- m_pPlayFileTerm = NULL;
- pITBCC2->Release();
- AfxMessageBox("AnswerTheCall: SelectTerminalOnCall failed");
- m_pCall->Disconnect(DC_NORMAL);
- return hr;
- }
- pITBCC2->Release();
- return S_OK;
- }
- /////////////////////////////////////////////////////////////////////
- // Answers the call
- //
- // 功能:应答电话,创建并选择播放的终端
- /////////////////////////////////////////////////////////////////////
- HRESULT
- CTapi::AnswerTheCall()
- {
- HRESULT hr;
- hr = PreparePlay(m_WelcomeFileName);
- if( FAILED(hr) )
- {
- AfxMessageBox("无法找到" + m_WelcomeFileName + "文件或该文件不是合法的语音文件!");
- return E_FAIL;
- }
- //
- // 现在可以正式应答电话了
- //
- hr = m_pCall->Answer();
- if(FAILED(hr))
- {
- m_pPlayFileTerm->Release();
- m_pPlayFileTerm = NULL;
- AfxMessageBox("AnswerTheCall: Answer failed");
- m_pCall->Disconnect(DC_NORMAL);
- return hr;
- }
- //
- // 到此正常结束
- //
- // pITBCC2->Release();
- return S_OK;
- }
- //////////////////////////////////////////////////////////////////////
- // DisconnectTheCall
- //
- // 功能:挂断电话
- //////////////////////////////////////////////////////////////////////
- HRESULT CTapi::DisconnectTheCall()
- {
- HRESULT hr = S_OK;
- if (NULL != m_pCall)
- {
- hr = m_pCall->Disconnect( DC_NORMAL );
-
- //
- // 暂时不要释放 m_pCall,因为这样做的话我们无法得到 Disconnected 消息
- //
- return hr;
- }
- return S_FALSE;
- }
- //////////////////////////////////////////////////////////////////////
- // ReleaseTheCall
- //
- // 释放通话过程中所用到的 object
- //
- //////////////////////////////////////////////////////////////////////
- void
- CTapi::ReleaseTheCall()
- {
- // KillTimer(m_hWnd, TIMER_ID);
- //
- // 释放终端
- //
- if(NULL != m_pPlayFileTerm)
- {
- m_pPlayFileTerm->Release();
- m_pPlayFileTerm = NULL;
- }
- if(NULL != m_pRecordFileTerm)
- {
- m_pRecordFileTerm->Release();
- m_pRecordFileTerm = NULL;
- }
- //
- // 释放 call object - 这样会同时释放所有的被选择过得终端
- //
- if (NULL != m_pCall)
- {
- m_pCall->Release();
- m_pCall = NULL;
- }
- }
- //////////////////////////////////////////////////////////////////
- // PutPlayList
- //
- // 功能:将要播放的声音文件放入播放列表。调用了 put_PlayList
- //////////////////////////////////////////////////////////////////
- HRESULT CTapi::PutPlayList(
- IN ITTerminal *pITTerminal,
- IN BSTR bstrFileName)
- {
- //
- // 检测输入的终端是否有效
- //
- if(NULL == pITTerminal)
- {
- AfxMessageBox("PutPlayList: playback terminal NULL");
- return E_UNEXPECTED;
- }
- //
- // 获得 ITMediaPlayback 接口
- //
- ITMediaPlayback* pMediaPlayback = NULL;
- HRESULT hr = pITTerminal->QueryInterface(
- IID_ITMediaPlayback,
- (void**)&pMediaPlayback);
- if(FAILED(hr))
- {
- AfxMessageBox("PutPlayList: QI ITMediaPlayback failed");
- return hr;
- }
- //
- // put_PlayList 函数需要用到的 VARIANT 变量
- //
- VARIANT varPlaylist;
- VariantInit(&varPlaylist);
- //
- // 设置 SAFEARRAYBOUND
- //
- SAFEARRAYBOUND DimensionBounds;
- DimensionBounds.lLbound = 1;
- //
- // 设置 playlist 中的文件数
- //
- DimensionBounds.cElements = 1;
- //
- // 把文件名置于 playlist 中的 index = 1处
- //
- long lArrayPos = 1;
- //
- // 用于存放文件名 - 用于把文件名加入到 SafeArray
- //
- VARIANT* pvarArrayEntry = new VARIANT;
- if( pvarArrayEntry == NULL)
- {
- AfxMessageBox("PutPlayList: new VARIANT failed");
- return E_OUTOFMEMORY;
- }
- VariantInit(pvarArrayEntry);
- //
- // 创建 SAFEARRAY
- //
- SAFEARRAY *pPlayListArray = NULL;
- pPlayListArray = SafeArrayCreate( VT_VARIANT, 1, &DimensionBounds);
- if( pPlayListArray == NULL)
- {
- AfxMessageBox("PutPlayList: SafeArrayCreate failed");
- delete pvarArrayEntry;
- return E_OUTOFMEMORY;
- }
- //
- // 正式把存储在 pvarArrayEntry 中的文件名加入到 SafeArray 中
- // 当我们需要加入多个元素到 SafeArray 中时,只需让 lArrayPos 加 1 即可
- //
- pvarArrayEntry->vt = VT_BSTR;
- pvarArrayEntry->bstrVal = ::SysAllocString(bstrFileName);
- SafeArrayPutElement( pPlayListArray, &lArrayPos, pvarArrayEntry);
- VariantClear(pvarArrayEntry);
- //
- // 准备varPlaylist, 为调用 put_PlayList 作准备
- //
- V_VT(&varPlaylist) = VT_ARRAY | VT_VARIANT;
- V_ARRAY(&varPlaylist) = pPlayListArray;
- //
- // 最后,现在可以调用 put_PlayList 把 varPlaylist 放入到播放列表里了
- //
- hr = pMediaPlayback->put_PlayList(varPlaylist);
- if(FAILED(hr))
- {
- AfxMessageBox("PutPlayList: put_PlayList failed");
- delete pvarArrayEntry;
- return E_OUTOFMEMORY;
- }
- //
- //释放内存
- //
- delete pvarArrayEntry;
- pMediaPlayback->Release();
- VariantClear(&varPlaylist);
- return hr;
- }
- ///////////////////////////////////////////////////////////////////
- // StartRecord
- //
- //
- // 功能:录音
- // 说明:只有 WinXP 下才支持
- ///////////////////////////////////////////////////////////////////
- HRESULT CTapi::StartRecord()
- {
- HRESULT hr;
- hr = CreateAndSelectFileRecordTerminal();
- if(FAILED(hr))
- {
- //
- // 致命错误 - 无法继续下去 - 切断此次通话
- //
- m_pCall->Disconnect(DC_NORMAL);
- AfxMessageBox("CreateAndSelectFileRecordTerminal failed");
- return hr;
- }
- //
- // 获得 ITMediaControl 接口 - we need to call Start
- //
- ITMediaControl* pITMC = NULL;
- hr = m_pRecordFileTerm->QueryInterface(IID_ITMediaControl, (void**)&pITMC);
- if(FAILED(hr))
- {
- //
- // 致命错误 - 无法继续下去 - 切断此次通话
- //
- AfxMessageBox("ITFileTerminalEvent, but failed to QI ITMediaControl");
- m_pCall->Disconnect(DC_NORMAL);
- return hr;
- }
- hr = pITMC->Start();
- pITMC->Release();
- if(FAILED(hr))
- {
- //
- // 致命错误 - 无法继续下去 - 切断此次通话
- //
- AfxMessageBox("ITFileTerminalEvent, but ITMediaControl::Start");
- m_pCall->Disconnect(DC_NORMAL);
- return hr;
- }
- //
- // 录音一分钟后自动停止
- //
- SetTimer(m_hWnd,Timer_ID, Max_Rec_Time, NULL);
- m_dwMessages++;
- KillTimer(m_hWnd,Timer_ID);
- return S_OK;
- }
- ///////////////////////////////////////////////////////////////////
- // CheckStreamMT
- //
- // 检测输入的 pITStream 是否支持给定的媒体类型
- //
- ///////////////////////////////////////////////////////////////////
- HRESULT CTapi::CheckStreamMT(
- IN ITStream* pITStream,
- IN long mt)
- {
- long mtStream = TAPIMEDIATYPE_AUDIO;
- HRESULT hr = E_FAIL;
- if(FAILED(hr=pITStream->get_MediaType(&mtStream)))
- {
- return hr;
- }
- if(!(mt&mtStream))
- {
- return S_FALSE;
- }
- else
- {
- return S_OK;
- }
- }
- ///////////////////////////////////////////////////////////////////
- // CheckStreamDir
- //
- // 检测输入的 pITStream 是否有给定的媒体流方向
- //
- ///////////////////////////////////////////////////////////////////
- HRESULT CTapi::CheckStreamDir(
- IN ITStream* pITStream,
- IN TERMINAL_DIRECTION td)
- {
- TERMINAL_DIRECTION tdStream = TD_CAPTURE ;
- HRESULT hr =E_FAIL;
- if(FAILED(hr=pITStream->get_Direction(&tdStream)))
- {
- return hr;
- }
- if(td!=tdStream)
- {
- return S_FALSE;
- }
- else
- {
- return S_OK;
- }
- }
- ///////////////////////////////////////////////////////////////////
- // CreateAndSelectFileRecordTerminal
- //
- // 功能:
- // 1. 创建录音文件终端,
- // 2. 并对其指定录音文件名,
- // 3. 对当前输入媒体流选择音轨终端
- ///////////////////////////////////////////////////////////////////
- HRESULT CTapi::CreateAndSelectFileRecordTerminal()
- {
- //
- // 检测当前 m_pCall 是否有效
- //
- if (NULL == m_pCall)
- {
- AfxMessageBox("CreateAndSelectFileRecordTerminal: m_pCall 无效,可能当前没有通话");
- return E_UNEXPECTED;
- }
- //
- // 获得 ITStreamControl 接口
- //
- ITStreamControl* pStreamControl = NULL;
- HRESULT hr = m_pCall->QueryInterface( IID_ITStreamControl, (void**)&pStreamControl );
- if (FAILED(hr))
- {
- AfxMessageBox("CreateAndSelectFileRecordTerminal: QI for ITStreamControl failed");
- return E_FAIL;
- }
- //
- // 确保录音终端在这之前已经释放
- //
- if(NULL != m_pRecordFileTerm)
- {
- m_pRecordFileTerm->Release();
- m_pRecordFileTerm = NULL;
- }
- //
- // QI for ITBasicCallControl2 - 准备请求终端
- //
- ITBasicCallControl2* pITBCC2 = NULL;
- hr = m_pCall->QueryInterface( IID_ITBasicCallControl2, (void**)&pITBCC2 );
- if(FAILED(hr))
- {
- pStreamControl->Release();
- AfxMessageBox("CreateAndSelectFileRecordTerminal: QI for ITBasicCallControl2 failed");
- return hr;
- }
- //
- // 为 ReqestTerminal 获得 CLSID
- //
- BSTR bstrCLSID = NULL;
- hr = StringFromCLSID(CLSID_FileRecordingTerminal, &bstrCLSID);
- if(FAILED(hr))
- {
- pITBCC2->Release();
- pStreamControl->Release();
- AfxMessageBox("CreateAndSelectFileRecordTerminal: StringFromCLSID failed");
- return hr;
- }
- //
- // 请求录音终端,需要输入 媒体类型 和 媒体流方向
- //
- hr = pITBCC2->RequestTerminal(bstrCLSID,
- TAPIMEDIATYPE_AUDIO,
- TD_RENDER,
- &m_pRecordFileTerm);
- //
- // 释放内存
- //
- ::CoTaskMemFree(bstrCLSID);
- pITBCC2->Release();
- if(FAILED(hr))
- {
- pStreamControl->Release();
- AfxMessageBox("CreateAndSelectFileRecordTerminal: RequestTerminal failed");
- return hr;
- }
- //
- // 获得 ITMediaRecord 接口,然后可以调用 put_FileName 来指定要录音的文件名
- //
- ITMediaRecord* pITMediaRec = NULL;
- hr = m_pRecordFileTerm->QueryInterface( IID_ITMediaRecord, (void**)&pITMediaRec );
- if(FAILED(hr))
- {
- pStreamControl->Release();
- AfxMessageBox("CreateAndSelectFileRecordTerminal: QI ITMediaRecord failed");
- return hr;
- }
- //
- // 为受到的每一个消息建立不同的文件,一文件名后面的数字来区分
- //
- CString RecFileName;
- CString tmp;
- tmp.Format("%d.", m_dwMessages);
- RecFileName = m_RecFileName + tmp + m_RecFileExt;
- BSTR bstrFileName = RecFileName.AllocSysString();
-
- if(NULL == bstrFileName)
- {
- pStreamControl->Release();
- AfxMessageBox("CreateAndSelectFileRecordTerminal: SysAllocString failed");
- return E_OUTOFMEMORY;
- }
- //
- // 设置文件名
- //
- hr = pITMediaRec->put_FileName(bstrFileName);
-
- //
- // 释放内存
- //
- ::SysFreeString(bstrFileName);
- pITMediaRec->Release();
-
- if(FAILED(hr))
- {
- pStreamControl->Release();
- AfxMessageBox("CreateAndSelectFileRecordTerminal: put_FileName failed");
- return hr;
- }
- //
- // 获得 ITMultiTrackTerminal 接口
- //
- ITMultiTrackTerminal* pMTRecTerminal = NULL;
- hr = m_pRecordFileTerm->QueryInterface( IID_ITMultiTrackTerminal, (void**)&pMTRecTerminal );
- if(FAILED(hr))
- {
- pStreamControl->Release();
- AfxMessageBox("CreateAndSelectFileRecordTerminal: QI IID_ITMultiTrackTerminal failed");
- return hr;
- }
- //
- // 列举所有的媒体流
- //
- IEnumStream* pEnumStreams = NULL;
- hr = pStreamControl->EnumerateStreams(&pEnumStreams);
- pStreamControl->Release();
- if (FAILED(hr))
- {
- pMTRecTerminal->Release();
- AfxMessageBox("CreateAndSelectFileRecordTerminal: EnumerateStreams failed");
- return hr;
- }
- //
- // 只处理音频媒体类型
- //
- long lMediaTypes = TAPIMEDIATYPE_AUDIO;
- //
- // 遍历媒体流,创建我们需要的
- //
- ITStream* pStream = NULL;
- while (S_OK == pEnumStreams->Next(1, &pStream, NULL))
- {
-
- if ( (S_OK==CheckStreamMT(pStream,lMediaTypes))
- && (S_OK==CheckStreamDir(pStream,TD_RENDER)))
- {
- //
- // 我们已经有了录音文件终端,下面要创建音轨终端
- //
- ITTerminal* pRecordingTrack = NULL;
- //
- // 检测媒体类型
- //
- long lStreamMediaType=0;
- hr = pStream->get_MediaType(&lStreamMediaType);
- if (FAILED(hr))
- {
- pStream->Release();
- pStream=NULL;
- continue;
- }
- //
- // 创建音轨终端
- //
- hr = pMTRecTerminal->CreateTrackTerminal(lStreamMediaType, TD_RENDER, &pRecordingTrack);
- if (FAILED(hr))
- {
- pStream->Release();
- pStream=NULL;
- break;
- }
- //
- // 让当前媒体流选择音轨终端
- //
- hr = pStream->SelectTerminal(pRecordingTrack);
- //
- // 释放内存
- //
- pStream->Release();
- pStream=NULL;
- pRecordingTrack->Release();
- pRecordingTrack=NULL;
- if (FAILED(hr))
- {
- break;
- }
- lMediaTypes^=lStreamMediaType;
- if(lMediaTypes==0)
- {
- //
- // 我们已经为所有媒体类型选择了终端,任务完成!
- //
- break;
- }
- }
- //
- // 释放内存
- //
- if(pStream!=NULL)
- {
- pStream->Release();
- pStream = NULL;
- }
- } // while (enumerating streams on the call)
- //
- //释放内存
- //
- pMTRecTerminal->Release();
- pEnumStreams->Release();
-
- //
- // 检测我们是否已经为所有媒体选择了终端
- //
- if (lMediaTypes==0)
- {
- return S_OK;
- }
- else
- {
- AfxMessageBox("CreateAndSelectFileRecordTerminal: no streams available");
- return E_FAIL;
- }
- }
- ///////////////////////////////////////////////////////////////////
- // SameCall
- //
- // 检测是否与 m_pCall 是同一个
- //
- ///////////////////////////////////////////////////////////////////
- bool CTapi::SameCall(ITCallStateEvent* pCallStateEvent)
- {
- if(NULL == pCallStateEvent)
- {
- return false;
- }
- //
- // 获得 Call object
- //
- ITCallInfo* pCallInfo = NULL;
- HRESULT hr = pCallStateEvent->get_Call(&pCallInfo);
- if(NULL != pCallInfo && ( SUCCEEDED(hr) && NULL != m_pCall) )
- {
- bool bIsEqual = true;
- //
- // 比较两个 Call object 的 IUnknown 接口
- //
- IUnknown* pIUnkCallInfo = NULL;
- IUnknown* pIUnkCallControl = NULL;
- pCallInfo->QueryInterface(IID_IUnknown, (void**)&pIUnkCallInfo);
- m_pCall->QueryInterface(IID_IUnknown, (void**)&pIUnkCallControl);
- //
- //比较
- //
- if(pIUnkCallInfo != pIUnkCallControl)
- {
- bIsEqual = false;
- }
- //
- //释放内存
- //
- pCallInfo->Release();
- if(NULL != pIUnkCallInfo)
- {
- pIUnkCallInfo->Release();
- }
- if(NULL != pIUnkCallControl)
- {
- pIUnkCallControl->Release();
- }
- return bIsEqual;
- }
- else
- {
- return false;
- }
- }
- //////////////////////////////////////////////////////////////////
- //
- // GetTerminalFromStreamEvent
- //
- // 功能:从 MediaEvent 事件中获得媒体流,然后,再由媒体流获得终端
- // 如果对应的终端存在,则返回 NULL
- //////////////////////////////////////////////////////////////////
- HRESULT CTapi::GetTerminalFromStreamEvent(
- IN ITCallMediaEvent * pCallMediaEvent,
- OUT ITTerminal ** ppTerminal )
- {
- HRESULT hr;
- *ppTerminal = NULL;
- //
- // 获得这一事件当中的媒体流
- //
- ITStream * pStream;
- hr = pCallMediaEvent->get_Stream( &pStream );
- if ( FAILED(hr) || (pStream == NULL) ) return E_FAIL;
- //
- // 列举支持这一种媒体流的终端
- //
- IEnumTerminal * pEnumTerminal;
- hr = pStream->EnumerateTerminals( &pEnumTerminal );
- pStream->Release();
- if ( FAILED(hr) ) return hr;
- //
- // 获得第一个终端,如果没有找到,返回 E_FAIL 跳出这一事件的处理
- //
- hr = pEnumTerminal->Next(1, ppTerminal, NULL);
- if ( hr != S_OK )
- {
- pEnumTerminal->Release();
- return E_FAIL;
- }
- //
- // 进一步检测,注意这里不可能有大于一个终端,因为我们对每一各媒体流只选择
- // 过一个终端
- //
- ITTerminal * pExtraTerminal;
- hr = pEnumTerminal->Next(1, &pExtraTerminal, NULL);
- pEnumTerminal->Release();
- if ( hr == S_OK ) // 大于一个终端,不可能出现的情况
- {
- pExtraTerminal->Release();
- (*ppTerminal)->Release();
- *ppTerminal = NULL;
- return E_UNEXPECTED;
- }
- return S_OK;
- }