Media modes | TAPIMEDIAMODE_AUDIO |
. | TAPIMEDIAMODE_VIDEO |
. | TAPIMEDIAMODE_DATAMODEM |
. | TAPIMEDIAMODE_G3FAX |
Terminal classes | CLSID_SpeakerphoneTerminal |
. | CLSID_MicrophoneTerminal |
. | CLSID_SpeakersTerminal |
. | CLSID_MediaStreamTerminal |
. | CLSID_FileTerminal |
. | CLSID_BridgeTerminal |
. | CLSID_DigitTerminal |
. | CLSID_DataTerminal |
Address types | LINEADDRESSTYPE_PHONENUMBER |
. | LINEADDRESSTYPE_SDP |
. | LINEADDRESSTYPE_EMAILNAME |
. | LINEADDRESSTYPE_DOMAINNAME |
. | LINEADDRESSTYPE_IPADDRESS. |
Protocols | TAPIPROTOCOL_PSTN |
. | TAPIPROTOCOL_H323 |
. | TAPIPROTOCOL_Multicast |
Figure 4 INCOMING Sample Code
incoming.cpp
#define UNICODE
#include <windows.h>
#include <tapi3.h>
#include "incoming.h"
#include "callnot.h"
#include "resource.h"
//////////////////////////////////////////////////////////
// INCOMING.EXE
//
// Sample application that handles incoming TAPI calls. This
// application will register to recieve calls on
// all addresses that support at least audioin and audioout.
//
// NOTE: This application is limited to working with one call at
// a time, and will not work correctly if multiple calls
// are present at the same time.
//////////////////////////////////////////////////////////
HINSTANCE ghInst;
HWND ghDlg = NULL;
ITTAPI * gpTapi;
ITBasicCallControl * gpCall;
CTAPIEventNotification * gpTAPIEventNotification = NULL;
ULONG gulAdvise;
long * gplRegistrationInstances = NULL;
DWORD gdwNumRegistrations = 10;
WCHAR gszTapi30[] = L"TAPI 3.0 Incoming Call Sample";
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmdLine,
int nCmdShow)
{
ghInst = hInst;
// need to coinit
if (!SUCCEEDED(CoInitializeEx(NULL, COINIT_MULTITHREADED)))
{
return 0;
}
// do tapi initialization
if (S_OK != InitializeTapi())
{
return 0;
}
// everything is initialized, so
// start the main dialog box
DialogBox(ghInst, MAKEINTRESOURCE(IDD_MAINDLG), NULL, MainDialogProc);
// clean up
ShutdownTapi();
CoUninitialize();
return 1;
}
HRESULT
InitializeTapi()
{
HRESULT hr;
// cocreate the TAPI object
hr = CoCreateInstance(CLSID_TAPI, NULL, CLSCTX_INPROC_SERVER, IID_ITTAPI,
(LPVOID *)&gpTapi);
if ( !SUCCEEDED(hr) )
{
DoMessage(L"CoCreateInstance on TAPI failed");
return hr;
}
// call initialize. this must be called before
// any other tapi functions are called.
hr = gpTapi->Initialize();
if ( !SUCCEEDED(hr) )
{
DoMessage(L"TAPI failed to initialize");
gpTapi->Release();
gpTapi = NULL;
return hr;
}
// Register the event interface
hr = RegisterTapiEventInterface();
if ( !SUCCEEDED(hr) )
{
ShutdownTapi();
return hr;
}
// find all address objects that
// we will use to listen for calls on
hr = ListenOnAddresses();
if ( !SUCCEEDED(hr) )
{
DoMessage(L"Could not find any addresses to listen on");
ShutdownTapi();
return hr;
}
return S_OK;
}
void
ShutdownTapi()
{
// if there is still a call,
// release it
ReleaseTheCall();
// release main object.
if ( NULL != gpTapi )
{
IConnectionPointContainer * pCPC;
IConnectionPoint * pCP;
DWORD dwCount;
// unregister all of our call notifications
if ( NULL != gplRegistrationInstances )
{
for ( dwCount = 0; dwCount < gdwNumRegistrations; dwCount++ )
{
gpTapi->UnregisterNotifications(
gplRegistrationInstances[dwCount]);
}
LocalFree( gplRegistrationInstances );
gplRegistrationInstances = NULL;
}
// unadvise our connection point
gpTapi->QueryInterface( IID_IConnectionPointContainer, (void**) &pCPC);
pCPC->FindConnectionPoint(IID_ITTAPIEventNotification, &pCP);
pCP->Unadvise( gulAdvise );
pCPC->Release();
pCP->Release();
// shutdown tapi
gpTapi->Shutdown();
gpTapi->Release();
gpTapi = NULL;
}
}
BOOL
CALLBACK
MainDialogProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_INITDIALOG:
{
// set up dialog
ghDlg = hDlg;
DisableButton( IDC_ANSWER );
DisableButton( IDC_DISCONNECT );
SetStatusMessage( L"Waiting for a call..." );
return 0;
}
case WM_COMMAND:
{
switch ( LOWORD(wParam) )
{
// answer request
case IDCANCEL:
{
// quit
EndDialog( hDlg, 0 );
return 1;
}
case IDC_ANSWER:
{
SetStatusMessage(L"Answering...");
// answer the call
if ( S_OK == AnswerTheCall() )
{
EnableButton( IDC_DISCONNECT );
DisableButton( IDC_ANSWER );
}
else
{
DisableButton( IDC_ANSWER );
DoMessage(L"Answer failed");
SetStatusMessage(L"Waiting for a call...");
}
return 1;
}
// disconnect request
case IDC_DISCONNECT:
{
SetStatusMessage(L"Disconnecting...");
// disconnect
if (S_OK != DisconnectTheCall())
{
DoMessage(L"Disconnect failed");
}
return 1;
}
default:
return 0;
}
// this is where we handle any TAPI
// events
case WM_PRIVATETAPIEVENT:
{
OnTapiEvent((TAPI_EVENT) wParam, (IDispatch *) lParam);
}
}
default:
return 0;
}
}
HRESULT
RegisterTapiEventInterface()
{
HRESULT hr = S_OK;
IConnectionPointContainer * pCPC;
IConnectionPoint * pCP;
// create a notification object
gpTAPIEventNotification = new CTAPIEventNotification;
if ( NULL == gpTAPIEventNotification )
{
DoMessage(L"Failed to create event interface");
return E_FAIL;
}
// get the connectionpointcontainer interface
// off the tapi object
hr = gpTapi->QueryInterface(IID_IConnectionPointContainer, (void **)&pCPC);
if ( !SUCCEEDED(hr) )
{
return hr;
}
// get the correct connection point
hr = pCPC->FindConnectionPoint(IID_ITTAPIEventNotification, &pCP);
pCPC->Release();
if ( !SUCCEEDED(hr) )
{
return hr;
}
hr = pCP->Advise(gpTAPIEventNotification, &gulAdvise);
pCP->Release();
return hr;
}
//////////////////////////////////////////////////////////////////////
// ListenOnAddresses
//
// This procedure will find all addresses that support audio
// and will call ListenOnThisAddress to start listening on it
//////////////////////////////////////////////////////////////////////
HRESULT
ListenOnAddresses()
{
HRESULT hr = S_OK;
IEnumAddress * pEnumAddress;
ITAddress * pAddress;
ITMediaSupport * pMediaSupport;
VARIANT_BOOL bSupport;
DWORD dwCount = 0;
gplRegistrationInstances = (long *) LocalAlloc(
LPTR, sizeof(long) * gdwNumRegistrations);
if ( NULL == gplRegistrationInstances )
{
return E_FAIL;
}
// enumerate the addresses
hr = gpTapi->EnumerateAddresses( &pEnumAddress );
if (S_OK != hr)
{
return hr;
}
while ( TRUE )
{
// get the next address
hr = pEnumAddress->Next( 1, &pAddress, NULL );
if (S_OK != hr)
{
break;
}
pAddress->QueryInterface(IID_ITMediaSupport, (void **)&pMediaSupport);
// does it support Audio?
pMediaSupport->QueryMediaType(TAPIMEDIAMODE_AUDIO, &bSupport);
if (bSupport)
{
// If it does then we'll listen.
hr = ListenOnThisAddress(pAddress, gplRegistrationInstances, dwCount);
if ( !SUCCEEDED(hr) )
{
DoMessage(L"Listen failed on an address");
}
else
{
dwCount++;
}
}
pMediaSupport->Release();
pAddress->Release();
if (dwCount == gdwNumRegistrations)
{
ResizeRegistration();
}
}
pEnumAddress->Release();
return S_OK;
}
///////////////////////////////////////////////////////////////////
// ListenOnThisAddress
//
// the app must call RegisterCallNotifications
// for the address that it wants calls on
///////////////////////////////////////////////////////////////////
HRESULT
ListenOnThisAddress(ITAddress * pAddress, long * plRegistrations,
DWORD dwRegistrationCount)
{
HRESULT hr = S_OK;
long lMediaTypes;
ITMediaSupport * pMediaSupport;
// listen for all media types that the address
// supports.
hr = pAddress->QueryInterface(IID_ITMediaSupport, (void**)&pMediaSupport);
hr = pMediaSupport->get_MediaTypes( &lMediaTypes );
pMediaSupport->Release();
hr = gpTapi->RegisterCallNotifications(pAddress,
VARIANT_TRUE, // monitor
VARIANT_TRUE, // owner
lMediaTypes, // media to listen for
0, // callback instance
// registration instance
&(plRegistrations[dwRegistrationCount])
);
return hr;
}
/////////////////////////////////////////////////////////
// GetVideoRenderTerminal
//
// Creates a Terminal for the VideoIn mediatype
// This is a dynamic terminal type.
/////////////////////////////////////////////////////////
HRESULT
GetVideoRenderTerminal(ITTerminalSupport * pTerminalSupport,
ITTerminal ** ppTerminal)
{
HRESULT hr = S_OK;
BSTR bstrTerminalClass;
LPOLESTR lpTerminalClass;
// need to pass in the terminal class
// as a BSTR
StringFromIID(CLSID_VideoWindowTerm, &lpTerminalClass);
bstrTerminalClass = SysAllocString ( lpTerminalClass );
CoTaskMemFree( lpTerminalClass );
// try to create the terminal
hr = pTerminalSupport->CreateTerminal(bstrTerminalClass, TAPIMEDIAMODE_VIDEO,
TD_RENDER, ppTerminal);
SysFreeString( bstrTerminalClass );
return hr;
}
/////////////////////////////////////////////////////////////////
// CreateTerminals
//
// Create audioin and audioout terminals. Videoin and videoout
// terminals are created only when the address supports them.
//
// This function assumes that ppTerminals has a size no less than
// four.
/////////////////////////////////////////////////////////////////
HRESULT
CreateTerminals(ITAddress * pAddress, ITTerminal ** ppTerminals,
DWORD * pdwNumTerminals)
{
DWORD dwCount = 0;
HRESULT hr;
ITTerminalSupport * pTerminalSupport;
ITMediaSupport * pMediaSupport;
VARIANT_BOOL bSupport;
TERMINAL_DIRECTION td;
// get the terminal support interface of the
// address object
hr = pAddress->QueryInterface(IID_ITTerminalSupport,
(void **) &pTerminalSupport);
if ( !SUCCEEDED(hr) )
{
return hr;
}
// get the default audio render terminal
hr = pTerminalSupport->GetDefaultTerminal(TAPIMEDIAMODE_AUDIO, TD_RENDER,
&(ppTerminals[dwCount]));
if ( SUCCEEDED(hr) )
{
// does the terminal actually support TD_BOTH?
(ppTerminals[dwCount])->get_Direction( &td );
dwCount++;
}
// if not, get the audio capture terminal also
if ( (SUCCEEDED(hr) && (TD_BOTH != td)) || !SUCCEEDED(hr) )
{
hr = pTerminalSupport->GetDefaultTerminal(TAPIMEDIAMODE_AUDIO,
TD_CAPTURE, &(ppTerminals[dwCount]));
if ( SUCCEEDED(hr) )
{
dwCount++;
}
}
// Find out if the address supports video.
pAddress->QueryInterface(IID_ITMediaSupport, (void **)&pMediaSupport);
// does it support VideoIn?
pMediaSupport->QueryMediaType(TAPIMEDIAMODE_VIDEO, &bSupport);
pMediaSupport->Release();
if (bSupport)
{
//
// get the video render terminal
//
hr = GetVideoRenderTerminal(pTerminalSupport, &(ppTerminals[dwCount]));
if ( SUCCEEDED(hr) )
{
dwCount++;
}
// get the Terminal for Video capture
hr = pTerminalSupport->GetDefaultTerminal(TAPIMEDIAMODE_VIDEO, TD_CAPTURE,
&(ppTerminals[dwCount]));
if ( SUCCEEDED(hr) )
{
// If we're using the Video capture terminal, also enable
// the preview window
ITPreviewConfig * pPreviewConfig;
hr = (ppTerminals[dwCount])->QueryInterface(IID_ITPreviewConfig,
(void**)&pPreviewConfig);
if ( SUCCEEDED(hr) )
{
pPreviewConfig->put_EnablePreview(VARIANT_TRUE);
pPreviewConfig->Release();
}
dwCount++;
}
}
// clean up
pTerminalSupport->Release();
// return # of terminals
*pdwNumTerminals = dwCount;
return S_OK;
}
void
ReleaseTerminals(ITTerminal ** ppTerminals, DWORD dwNumTerminals)
{
DWORD dwCount;
for (dwCount = 0; dwCount < dwNumTerminals; dwCount++)
{
if (ppTerminals[dwCount])
{
ppTerminals[dwCount]->Release();
}
}
}
HRESULT
AnswerTheCall()
{
HRESULT hr;
ITCallInfo * pCallInfo;
ITAddress * pAddress;
ITTerminal * ppTerminals[MAXTERMINALS];
DWORD dwNumTerminals;
DWORD dwCount;
if (NULL == gpCall)
{
return E_UNEXPECTED;
}
// get the address object of this call
gpCall->QueryInterface( IID_ITCallInfo, (void**)&pCallInfo );
pCallInfo->get_Address( &pAddress );
pCallInfo->Release();
// create the media terminals for this call
hr = CreateTerminals(pAddress, ppTerminals, &dwNumTerminals);
// release the address
pAddress->Release();
if (S_OK != hr)
{
ReleaseTheCall();
return hr;
}
for (dwCount = 0; dwCount < dwNumTerminals; dwCount++)
{
// For each terminal, call SelectTerminal
hr = gpCall->SelectTerminal(ppTerminals[dwCount]);
}
ReleaseTerminals(ppTerminals, dwNumTerminals);
if (S_OK != hr)
{
ReleaseTheCall();
return hr;
}
// answer the call
hr = gpCall->Answer();
return hr;
}
HRESULT
DisconnectTheCall()
{
HRESULT hr = S_OK;
if (NULL != gpCall)
{
hr = gpCall->Disconnect( DC_NORMAL );
ReleaseTheCall();
return hr;
}
return S_FALSE;
}
void
ReleaseTheCall()
{
if (NULL != gpCall)
{
gpCall->Release();
gpCall = NULL;
}
}
void
DoMessage(LPWSTR pszMessage)
{
MessageBox(ghDlg, pszMessage, gszTapi30, MB_OK);
}
void
SetStatusMessage(LPWSTR pszMessage)
{
SetDlgItemText(ghDlg, IDC_STATUS, pszMessage);
}
void
EnableButton(int ID)
{
SendDlgItemMessage(ghDlg, ID, BM_SETSTYLE, BS_DEFPUSHBUTTON, 0);
EnableWindow(GetDlgItem( ghDlg, ID ), TRUE);
SetFocus(GetDlgItem( ghDlg, ID ));
}
void
DisableButton(int ID)
{
SendDlgItemMessage(ghDlg, ID, BM_SETSTYLE, BS_PUSHBUTTON, 0);
EnableWindow(GetDlgItem( ghDlg, ID ), FALSE);
}
HRESULT
ResizeRegistration()
{
long * plTemp;
plTemp = (long *)LocalAlloc(LPTR, sizeof(long) * gdwNumRegistrations * 2);
if ( NULL == plTemp )
{
return E_FAIL;
}
CopyMemory(plTemp, gplRegistrationInstances,
sizeof(long) * gdwNumRegistrations);
gdwNumRegistrations *= 2;
LocalFree( gplRegistrationInstances );
gplRegistrationInstances = plTemp;
return S_OK;
}
callnot.cpp
//////////////////////////////////////////////////////////////////////
// callnot.cpp
//
// Implementation of the ITTAPIEventNotification interface.
//
// This is an outgoing interface that is defined by TAPI 3.0. This
// is basically a callback function that TAPI 3.0 calls to inform
// the application of events related to calls (on a specific address)
//
// Please refer to COM documentation for information on outgoing
// interfaces.
//
// An application must implement and register this interface in order
// to receive calls and events related to calls
//
//////////////////////////////////////////////////////////////////////
#include "windows.h"
#include "tapi3.h"
#include "incoming.h"
#include "callnot.h"
#include "resource.h"
#include "control.h"
#include "strmif.h"
extern ITBasicCallControl * gpCall;
extern HWND ghDlg;
extern BSTR gbstrVideo;
///////////////////////////////////////////////////////////////////
//
// CTAPIEventNotification::Event
//
// The only method in the ITCallEventNotification interface. This gets
// called by TAPI 3.0 when there is a call event to report
//
// We don't do any processing here, just post a message to our
// main UI thread to handle this. In general, an MTA application should
// do as little as possible on the thread that the callback
// is fired in
//
///////////////////////////////////////////////////////////////////
HRESULT
STDMETHODCALLTYPE
CTAPIEventNotification::Event(TAPI_EVENT TapiEvent, IDispatch * pEvent)
{
// addref the event so it doesn't go away
pEvent->AddRef();
// post a message to ourself
PostMessage(hDlg, WM_PRIVATETAPIEVENT, (WPARAM) TapiEvent, (LPARAM) pEvent);
return S_OK;
}
///////////////////////////////////////////////////////////////////
// OnTapiEvent
//
// This is the real event handler. This is called from our
// msg proc when the WM_PRIVATETAPIEVENT message is received
///////////////////////////////////////////////////////////////////
HRESULT
OnTapiEvent(TAPI_EVENT TapiEvent, IDispatch * pEvent)
{
HRESULT hr;
switch ( TapiEvent )
{
case TE_CALLNOTIFICATION:
{
HandleCallNotificationEvent( pEvent );
break;
}
case TE_CALLSTATE:
{
HandleCallStateEvent( pEvent );
break;
}
default:
// not handling other events
break;
}
// make sure to release the event
// we addref'd in in CTAPIEventNotification::Event()
pEvent->Release();
return S_OK;
}
void HandleCallNotificationEvent( IDispatch * pEvent )
{
ITCallNotificationEvent * pNotify;
CALL_NOTIFICATION_EVENT cne;
ITCallInfo * pCall;
HRESULT hr;
// CET_CALLNOTIFICATION means that the application is being notified
// of a new call.
//
// Note that we don't answer the call at this point. The application
// should wait for a CS_OFFERING CallState message before answering
// the call.
// get the correct interface on the event
hr = pEvent->QueryInterface(IID_ITCallNotificationEvent, (void **)&pNotify);
if (S_OK != hr)
{
DoMessage( L"Incoming call, but failed to get the event interface");
return;
}
// check to see if we own the call
pNotify->get_Event( &cne );
if ( CNE_OWNER != cne )
{
return;
}
// get the call
pNotify->get_Call( &pCall );
// save the BasicCallControl interface of this
// call in the global
pCall->QueryInterface( IID_ITBasicCallControl, (void**)&gpCall );
// release our local reference
pCall->Release();
// release the event object
pNotify->Release();
// update UI
SetStatusMessage(L"New Owner Call");
return;
}
void MakeWindowsVisible(ITCallStateEvent * pCallStateEvent)
{
HRESULT hr;
ITCallInfo * pCallInfo = NULL;
IEnumTerminal * pEnumTerm = NULL;
ITTerminal * pTerminal = NULL;
IVideoWindow * pVideoWindow = NULL;
// Get the call this event is for