951 lines
25 KiB
C++
951 lines
25 KiB
C++
/*
|
|
|
|
Copyright (c) 1998-1999 Microsoft Corporation
|
|
|
|
*/
|
|
|
|
#include <windows.h>
|
|
#include <tapi3.h>
|
|
#include <strmif.h>
|
|
#include <mmsystem.h>
|
|
#include <uuids.h>
|
|
|
|
#include "callnot.h"
|
|
#include "resource.h"
|
|
|
|
//////////////////////////////////////////////////////////
|
|
// ANSMACH.EXE
|
|
//
|
|
// Sample application that handles incoming TAPI calls
|
|
// and uses the media streaming terminal to preform the
|
|
// functions of a simple answering machine.
|
|
//
|
|
// This sample was adapted from the T3IN sample.
|
|
//
|
|
// In order to receive incoming calls, the application must
|
|
// implement and register the outgoing ITCallNotification
|
|
// interface.
|
|
//
|
|
// This application will register to receive calls on
|
|
// all addresses that support at least the audio media type.
|
|
//
|
|
// NOTES:
|
|
// 1. 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.
|
|
// 2. Some voice boards may have problems with small sample sizes
|
|
// (see SetAllocatorProperties below)
|
|
// 4. This works for half-duplex modems and voice boards with the
|
|
// wave MSP, as well as IP telephony with the H.323 MSP.
|
|
//////////////////////////////////////////////////////////
|
|
|
|
//////////////////////////////////////////////////////////
|
|
// constants
|
|
//////////////////////////////////////////////////////////
|
|
|
|
const DWORD MAXTERMINALS = 5;
|
|
|
|
//////////////////////////////////////////////////////////
|
|
// GLOBALS
|
|
//////////////////////////////////////////////////////////
|
|
|
|
bool g_fPlay;
|
|
bool g_fRecord;
|
|
|
|
|
|
HINSTANCE ghInst;
|
|
ITTAPI * gpTapi;
|
|
ITBasicCallControl * gpCall = NULL;
|
|
HWND ghDlg = NULL;
|
|
ITStream *g_pRecordStream = NULL;
|
|
ITTerminal *g_pPlayMedStrmTerm = NULL, *g_pRecordMedStrmTerm = NULL;
|
|
|
|
TCHAR gszTapi30[] = TEXT("TAPI 3.0 Answering Machine Sample");
|
|
|
|
|
|
CTAPIEventNotification * gpTAPIEventNotification;
|
|
ULONG gulAdvise;
|
|
|
|
//////////////////////////////////////////////////////////
|
|
// PROTOTYPES
|
|
//////////////////////////////////////////////////////////
|
|
INT_PTR
|
|
CALLBACK
|
|
MainDialogProc(
|
|
HWND hDlg,
|
|
UINT uMsg,
|
|
WPARAM wParam,
|
|
LPARAM lParam
|
|
);
|
|
|
|
HRESULT
|
|
GetTerminal(
|
|
ITAddress *,
|
|
BSTR bstrMedia,
|
|
ITTerminal ** ppTerminal
|
|
);
|
|
HRESULT
|
|
RegisterTapiEventInterface();
|
|
|
|
HRESULT
|
|
ListenOnAddresses();
|
|
|
|
HRESULT
|
|
ListenOnThisAddress(
|
|
ITAddress * pAddress
|
|
);
|
|
|
|
HRESULT
|
|
AnswerTheCall();
|
|
|
|
HRESULT
|
|
DisconnectTheCall();
|
|
|
|
void
|
|
ReleaseTheCall();
|
|
|
|
void
|
|
DoMessage(
|
|
LPTSTR pszMessage
|
|
);
|
|
|
|
void
|
|
SetStatusMessage(
|
|
LPTSTR pszMessage
|
|
);
|
|
|
|
HRESULT
|
|
InitializeTapi();
|
|
|
|
void
|
|
ShutdownTapi();
|
|
|
|
//
|
|
// Telephone quality audio.
|
|
//
|
|
WAVEFORMATEX gwfx = {WAVE_FORMAT_PCM, 1, 8000, 16000, 2, 16, 0};
|
|
|
|
//////////////////////////////////////////////////////////
|
|
//
|
|
// FUNCTIONS
|
|
//
|
|
//////////////////////////////////////////////////////////
|
|
|
|
//////////////////////////////////////////////////////////
|
|
// WinMain
|
|
//////////////////////////////////////////////////////////
|
|
int
|
|
WINAPI
|
|
WinMain(
|
|
HINSTANCE hInst,
|
|
HINSTANCE hPrevInst,
|
|
LPSTR lpCmdLine,
|
|
int nCmdShow
|
|
)
|
|
{
|
|
if (__argc != 2)
|
|
{
|
|
MessageBox(NULL, "Usage: ansmach play|record|both", "ansmach", MB_OK);
|
|
return 0;
|
|
}
|
|
|
|
switch(__argv[1][0])
|
|
{
|
|
case 'p':
|
|
case 'P':
|
|
g_fPlay = true;
|
|
g_fRecord = false;
|
|
break;
|
|
|
|
case 'r':
|
|
case 'R':
|
|
g_fPlay = false;
|
|
g_fRecord = true;
|
|
break;
|
|
|
|
case 'b':
|
|
case 'B':
|
|
g_fPlay = true;
|
|
g_fRecord = true;
|
|
break;
|
|
|
|
default:
|
|
MessageBox(NULL, "Usage: ansmach play|record|both", "ansmach", MB_OK);
|
|
return 0;
|
|
break;
|
|
}
|
|
|
|
ghInst = hInst;
|
|
|
|
// need to coinit
|
|
if (!SUCCEEDED(CoInitializeEx(NULL, COINIT_MULTITHREADED)))
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
// do all tapi initialization
|
|
if (S_OK != InitializeTapi())
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
// everything is initialized, so
|
|
// start the main dialog box
|
|
DialogBox(
|
|
ghInst,
|
|
MAKEINTRESOURCE(IDD_MAINDLG),
|
|
NULL,
|
|
MainDialogProc
|
|
);
|
|
|
|
//
|
|
// When EndDialog is called, we get here; clean up and exit.
|
|
//
|
|
|
|
ShutdownTapi();
|
|
|
|
CoUninitialize();
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
// InitializeTapi
|
|
//
|
|
// Various initializations
|
|
///////////////////////////////////////////////////////////////
|
|
HRESULT
|
|
InitializeTapi()
|
|
{
|
|
HRESULT hr;
|
|
|
|
// cocreate the TAPI object
|
|
hr = CoCreateInstance(
|
|
CLSID_TAPI,
|
|
NULL,
|
|
CLSCTX_INPROC_SERVER,
|
|
IID_ITTAPI,
|
|
(LPVOID *)&gpTapi
|
|
);
|
|
|
|
if (hr != S_OK)
|
|
{
|
|
DoMessage(TEXT("CoCreateInstance on TAPI failed"));
|
|
return hr;
|
|
}
|
|
|
|
// call initialize. this must be called before
|
|
// any other tapi functions are called.
|
|
hr = gpTapi->Initialize();
|
|
|
|
if (S_OK != hr)
|
|
{
|
|
DoMessage(TEXT("TAPI failed to initialize"));
|
|
|
|
gpTapi->Release();
|
|
gpTapi = NULL;
|
|
|
|
return hr;
|
|
}
|
|
|
|
gpTAPIEventNotification = new CTAPIEventNotification;
|
|
|
|
hr = RegisterTapiEventInterface();
|
|
|
|
// Set the Event filter to only give us only the events we process
|
|
gpTapi->put_EventFilter(TE_CALLNOTIFICATION | TE_CALLSTATE | TE_CALLMEDIA);
|
|
|
|
// find all address objects that
|
|
// we will use to listen for calls on
|
|
hr = ListenOnAddresses();
|
|
|
|
if (S_OK != hr)
|
|
{
|
|
DoMessage(TEXT("Could not find any addresses to listen on"));
|
|
|
|
gpTapi->Release();
|
|
gpTapi = NULL;
|
|
|
|
return hr;
|
|
}
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////
|
|
// ShutdownTapi
|
|
///////////////////////////////////////////////////////////////
|
|
void
|
|
ShutdownTapi()
|
|
{
|
|
//
|
|
// if there is still a call, disconnect and release it
|
|
//
|
|
|
|
DisconnectTheCall();
|
|
|
|
ReleaseTheCall();
|
|
|
|
//
|
|
// Sleep for a little while to give tapi a chance to finish
|
|
// disconnecting the call. Note that our dialog box has
|
|
// disappeared by now so the user has no idea that our process
|
|
// is still around. This step would not be necessary if the
|
|
// call were disconnected interactively as a separate operation
|
|
// from shutting down TAPI. The argument to Sleep is in
|
|
// milliseconds.
|
|
//
|
|
|
|
Sleep(5000);
|
|
|
|
//
|
|
// release main object.
|
|
//
|
|
|
|
if (NULL != gpTapi)
|
|
{
|
|
gpTapi->Shutdown();
|
|
gpTapi->Release();
|
|
}
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
// MainDlgProc
|
|
///////////////////////////////////////////////////////////////////////////
|
|
INT_PTR
|
|
CALLBACK
|
|
MainDialogProc(
|
|
HWND hDlg,
|
|
UINT uMsg,
|
|
WPARAM wParam,
|
|
LPARAM lParam
|
|
)
|
|
{
|
|
switch (uMsg)
|
|
{
|
|
case WM_PRIVATETAPIEVENT:
|
|
{
|
|
OnTapiEvent(
|
|
(TAPI_EVENT) wParam,
|
|
(IDispatch *) lParam
|
|
);
|
|
|
|
return 0;
|
|
}
|
|
|
|
case WM_INITDIALOG:
|
|
{
|
|
// set up dialog
|
|
ghDlg = hDlg;
|
|
|
|
SetStatusMessage( TEXT("Waiting for a call..."));
|
|
|
|
return 0;
|
|
}
|
|
|
|
case WM_COMMAND:
|
|
{
|
|
if ( LOWORD(wParam) == IDCANCEL )
|
|
{
|
|
//
|
|
// quit
|
|
//
|
|
|
|
EndDialog( hDlg, 0 );
|
|
|
|
return 1;
|
|
}
|
|
|
|
switch ( LOWORD(wParam) )
|
|
{
|
|
// dial request
|
|
case IDC_ANSWER:
|
|
{
|
|
SetStatusMessage(TEXT("Answering..."));
|
|
// answer the call
|
|
if ( S_OK == AnswerTheCall() )
|
|
{
|
|
SetStatusMessage(TEXT("Connected"));
|
|
}
|
|
else
|
|
{
|
|
DoMessage(TEXT("Answer failed"));
|
|
SetStatusMessage(TEXT("Waiting for a call..."));
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
// disconnect request
|
|
case IDC_DISCONNECT:
|
|
{
|
|
// If the remote party disconnected first, then the call was
|
|
// already disconnected and released, so do not disconnect
|
|
// it again.
|
|
|
|
if ( gpCall != NULL )
|
|
{
|
|
SetStatusMessage(TEXT("Disconnecting..."));
|
|
|
|
// disconnect the call
|
|
if (S_OK != DisconnectTheCall())
|
|
{
|
|
DoMessage(TEXT("Disconnect failed"));
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
// connect notification
|
|
case IDC_CONNECTED:
|
|
{
|
|
SetStatusMessage(TEXT("Connected; waiting for media streams to run..."));
|
|
|
|
return 1;
|
|
}
|
|
|
|
// disconnected notification
|
|
case IDC_DISCONNECTED:
|
|
{
|
|
// release
|
|
ReleaseTheCall();
|
|
|
|
SetStatusMessage(TEXT("Waiting for a call..."));
|
|
|
|
return 1;
|
|
}
|
|
default:
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
default:
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
HRESULT
|
|
RegisterTapiEventInterface()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
IConnectionPointContainer * pCPC;
|
|
IConnectionPoint * pCP;
|
|
|
|
|
|
hr = gpTapi->QueryInterface(
|
|
IID_IConnectionPointContainer,
|
|
(void **)&pCPC
|
|
);
|
|
|
|
if (!SUCCEEDED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
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 audioin and audioout
|
|
// and will call ListenOnThisAddress to start listening on it.
|
|
////////////////////////////////////////////////////////////////////////
|
|
|
|
HRESULT
|
|
ListenOnAddresses()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
IEnumAddress * pEnumAddress;
|
|
ITAddress * pAddress;
|
|
ITMediaSupport * pMediaSupport;
|
|
VARIANT_BOOL bSupport;
|
|
|
|
// 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(
|
|
TAPIMEDIATYPE_AUDIO,
|
|
&bSupport
|
|
);
|
|
|
|
if (bSupport)
|
|
{
|
|
// If it does then we'll listen.
|
|
hr = ListenOnThisAddress( pAddress );
|
|
if (S_OK != hr)
|
|
{
|
|
DoMessage(TEXT("Listen failed on an address"));
|
|
}
|
|
}
|
|
|
|
pMediaSupport->Release();
|
|
pAddress->Release();
|
|
}
|
|
|
|
pEnumAddress->Release();
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////
|
|
// ListenOnThisAddress
|
|
//
|
|
// We call RegisterCallNotifications to inform TAPI that we want
|
|
// notifications of calls on this address. We already resistered
|
|
// our notification interface with TAPI, so now we are just telling
|
|
// TAPI that we want calls from this address to trigger events on
|
|
// our existing notification interface.
|
|
//
|
|
///////////////////////////////////////////////////////////////////
|
|
|
|
HRESULT
|
|
ListenOnThisAddress(
|
|
ITAddress * pAddress
|
|
)
|
|
{
|
|
|
|
//
|
|
// RegisterCallNotifications takes a media type bitmap indicating
|
|
// the set of media types we are interested in. We know the
|
|
// address supports audio.
|
|
//
|
|
|
|
long lMediaTypes = TAPIMEDIATYPE_AUDIO;
|
|
|
|
HRESULT hr;
|
|
long lRegister;
|
|
|
|
hr = gpTapi->RegisterCallNotifications(
|
|
pAddress,
|
|
VARIANT_TRUE,
|
|
VARIANT_TRUE,
|
|
lMediaTypes,
|
|
0,
|
|
&lRegister
|
|
);
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////
|
|
// GetMediaStreamTerminal
|
|
//
|
|
// Create a media streaming terminal for the given
|
|
// direction.
|
|
//
|
|
/////////////////////////////////////////////////////////
|
|
|
|
HRESULT
|
|
GetMediaStreamTerminal(
|
|
ITAddress * pAddress,
|
|
TERMINAL_DIRECTION dir,
|
|
ITTerminal ** ppTerminal
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
ITTerminalSupport * pTerminalSupport;
|
|
BSTR bstrTerminalClass;
|
|
LPOLESTR lpTerminalClass;
|
|
AM_MEDIA_TYPE mt;
|
|
|
|
// get the terminal support interface
|
|
pAddress->QueryInterface( IID_ITTerminalSupport, (void **)&pTerminalSupport );
|
|
|
|
StringFromIID(
|
|
CLSID_MediaStreamTerminal,
|
|
&lpTerminalClass
|
|
);
|
|
bstrTerminalClass = SysAllocString ( lpTerminalClass );
|
|
CoTaskMemFree( lpTerminalClass );
|
|
|
|
hr = pTerminalSupport->CreateTerminal(
|
|
bstrTerminalClass,
|
|
TAPIMEDIATYPE_AUDIO,
|
|
dir,
|
|
ppTerminal
|
|
);
|
|
|
|
SysFreeString( bstrTerminalClass );
|
|
pTerminalSupport->Release();
|
|
|
|
if (FAILED(hr)) { return hr; }
|
|
|
|
if (TD_CAPTURE == dir)
|
|
{
|
|
//
|
|
// Now set the media format for the terminal.
|
|
//
|
|
// On addresses where a variety of formats are supported (such as
|
|
// Wave MSP addresses, which are used on most modems and voice boards),
|
|
// this call is mandatory or the terminal will not be able to connect.
|
|
//
|
|
// For other addresses, such as those implemented over IP, the format
|
|
// may be fixed / predetermined. In that case, this call will fail
|
|
// if the format is not the same as the predetermined format. To
|
|
// retrieve such a predetermined format, an application can use
|
|
// ITAMMediaFormat::get_MediaFormat.
|
|
//
|
|
// For the purposes of this example, we just set our preferred format
|
|
// and ignore any failure code. This is fine for both Wave MSP
|
|
// addresses and for addresses implemented over IP (which happens to
|
|
// use the same format the one we are setting here).
|
|
//
|
|
|
|
ITAMMediaFormat *pITFormat;
|
|
hr = (*ppTerminal)->QueryInterface(IID_ITAMMediaFormat, (void **)&pITFormat);
|
|
if (FAILED(hr)) { (*ppTerminal)->Release(); *ppTerminal = NULL; return hr; }
|
|
|
|
mt.majortype = MEDIATYPE_Audio;
|
|
mt.subtype = MEDIASUBTYPE_PCM;
|
|
mt.bFixedSizeSamples = TRUE;
|
|
mt.bTemporalCompression = FALSE;
|
|
mt.lSampleSize = 0;
|
|
mt.formattype = FORMAT_WaveFormatEx;
|
|
mt.pUnk = NULL;
|
|
mt.cbFormat = sizeof(WAVEFORMATEX);
|
|
mt.pbFormat = (BYTE*)&gwfx;
|
|
|
|
pITFormat->put_MediaFormat(&mt);
|
|
pITFormat->Release();
|
|
|
|
|
|
//
|
|
// optional - set the buffer size of fragments negotiated with rest of the graph
|
|
// this is only needed for applications that are not happy with the default fragment size
|
|
// (which depends on the MSP -- for the Wave MSP (used on most modems and voice boards)
|
|
// the default is 20 ms.
|
|
//
|
|
|
|
ITAllocatorProperties *pITAllocatorProperties;
|
|
hr = (*ppTerminal)->QueryInterface(IID_ITAllocatorProperties, (void **)&pITAllocatorProperties);
|
|
if (FAILED(hr)) { (*ppTerminal)->Release(); *ppTerminal = NULL; return hr; }
|
|
|
|
// Call to SetAllocatorProperties below illustrates how
|
|
// an app can control the number and size of buffers.
|
|
// A 30 ms sample size is most appropriate for IP (especailly G.723.1)
|
|
// (that's 480 bytes at 16-bit 8 KHz PCM); in fact you may get very
|
|
// poor audio quality with other settings.
|
|
// However, 30ms can cause poor audio quality on some voice boards --
|
|
// voice boards generally work best with large buffers.
|
|
// If this method is not called, the allocator properties
|
|
// suggested by the connecting filter may be used.
|
|
|
|
ALLOCATOR_PROPERTIES AllocProps;
|
|
AllocProps.cbBuffer = 480;
|
|
AllocProps.cBuffers = 5;
|
|
AllocProps.cbAlign = 1;
|
|
AllocProps.cbPrefix = 0;
|
|
|
|
hr = pITAllocatorProperties->SetAllocatorProperties(&AllocProps);
|
|
hr = pITAllocatorProperties->SetAllocateBuffers(TRUE);
|
|
|
|
BOOL bAllocateBuffer;
|
|
hr = pITAllocatorProperties->GetAllocateBuffers(&bAllocateBuffer);
|
|
|
|
pITAllocatorProperties->Release();
|
|
}
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////
|
|
// ReleaseTerminals
|
|
//
|
|
/////////////////////////////////////////////////////////////////
|
|
|
|
void
|
|
ReleaseTerminals(
|
|
ITTerminal ** ppTerminals,
|
|
LONG nNumTerminals
|
|
)
|
|
{
|
|
for (long i = 0; i < nNumTerminals; i ++)
|
|
{
|
|
if (ppTerminals[i])
|
|
{
|
|
ppTerminals[i]->Release();
|
|
}
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
// Answer the call
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
HRESULT
|
|
AnswerTheCall()
|
|
{
|
|
HRESULT hr;
|
|
ITCallInfo * pCallInfo;
|
|
ITAddress * pAddress;
|
|
|
|
|
|
g_pRecordStream = NULL;
|
|
g_pPlayMedStrmTerm = NULL;
|
|
g_pRecordMedStrmTerm = NULL;
|
|
|
|
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();
|
|
|
|
ITStreamControl * pStreamControl;
|
|
|
|
// get the ITStreamControl interface for this call
|
|
hr = gpCall->QueryInterface(IID_ITStreamControl,
|
|
(void **) &pStreamControl);
|
|
|
|
// enumerate the streams
|
|
|
|
IEnumStream * pEnumStreams;
|
|
hr = pStreamControl->EnumerateStreams(&pEnumStreams);
|
|
pStreamControl->Release();
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
pAddress->Release();
|
|
gpCall->Release();
|
|
gpCall = NULL;
|
|
return hr;
|
|
}
|
|
|
|
// for each stream
|
|
ITStream * pStream;
|
|
|
|
|
|
// Since we are interested in the audio capture and render streams only,
|
|
// the while loop terminates when either all the streams are done or
|
|
// when terminals for both the streams i.e. render and capture are created
|
|
// We will also make sure that even though terminals are created on the
|
|
// streams only one terminal will be selected to support half duplex lines
|
|
while ( ( S_OK == pEnumStreams->Next(1, &pStream, NULL) ) &&
|
|
( (g_pPlayMedStrmTerm == NULL) || (g_pRecordMedStrmTerm==NULL) ) )
|
|
{
|
|
TERMINAL_DIRECTION td;
|
|
hr = pStream->get_Direction(&td);
|
|
|
|
if ( SUCCEEDED(hr) )
|
|
{
|
|
//
|
|
// create the media terminals for this call
|
|
//
|
|
|
|
// Only one capture terminal will be created even if they are multiple capture streams
|
|
//
|
|
if ( (td == TD_CAPTURE) && g_fPlay && (g_pPlayMedStrmTerm == NULL))
|
|
{
|
|
GetMediaStreamTerminal(
|
|
pAddress,
|
|
TD_CAPTURE,
|
|
&g_pPlayMedStrmTerm
|
|
);
|
|
|
|
// the created terminal is selected on the stream if the user
|
|
// wishes to play or play and record. The play terminal has to be
|
|
// selected in any case before the record since the message will always
|
|
// be played before the record
|
|
|
|
hr = pStream->SelectTerminal(g_pPlayMedStrmTerm);
|
|
|
|
if ( FAILED (hr) )
|
|
{
|
|
::MessageBox(NULL,"Select Terminal for Play Failed", "Answer the call", MB_OK);
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
// Create terminal for Render stream
|
|
// Note that we only create the terminal.. we do not select it on the stream
|
|
if ( (td == TD_RENDER) && g_fRecord && (g_pRecordMedStrmTerm == NULL))
|
|
{
|
|
GetMediaStreamTerminal(
|
|
pAddress,
|
|
TD_RENDER,
|
|
&g_pRecordMedStrmTerm
|
|
);
|
|
g_pRecordStream = pStream;
|
|
g_pRecordStream->AddRef();
|
|
}
|
|
}
|
|
pStream->Release();
|
|
}
|
|
|
|
|
|
// the record terminal is selected on the stream only if the play terminal is not already
|
|
// selected. This will happen only in the case when the user selects only record and no play
|
|
// Only one terminal is selected at a time inorder to support half-duplex lines
|
|
if (!g_fPlay)
|
|
{
|
|
hr = g_pRecordStream->SelectTerminal(g_pRecordMedStrmTerm);
|
|
|
|
if ( FAILED (hr) )
|
|
{
|
|
::MessageBox(NULL,"Select Terminal for Record Failed", "Answer the call", MB_OK);
|
|
return hr;
|
|
}
|
|
|
|
g_pRecordMedStrmTerm->Release();
|
|
g_pRecordStream->Release();
|
|
}
|
|
|
|
|
|
pEnumStreams->Release();
|
|
|
|
// release the address
|
|
pAddress->Release();
|
|
|
|
// answer the call
|
|
hr = gpCall->Answer();
|
|
|
|
if (S_OK != hr)
|
|
{
|
|
gpCall->Release();
|
|
gpCall = NULL;
|
|
return hr;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// DisconnectTheCall
|
|
//
|
|
// Disconnects the call
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
HRESULT
|
|
DisconnectTheCall()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
if (NULL != gpCall)
|
|
{
|
|
hr = gpCall->Disconnect( DC_NORMAL );
|
|
|
|
// Do not release the call yet, as that would prevent
|
|
// us from receiving the disconnected notification.
|
|
|
|
return hr;
|
|
}
|
|
|
|
return S_FALSE;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// ReleaseTheCall
|
|
//
|
|
// Releases the call
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
void
|
|
ReleaseTheCall()
|
|
{
|
|
if (NULL != gpCall)
|
|
{
|
|
gpCall->Release();
|
|
gpCall = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////
|
|
//
|
|
// HELPER FUNCTIONS
|
|
//
|
|
///////////////////////////////////////////////////////////////////
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////
|
|
// DoMessage
|
|
///////////////////////////////////////////////////////////////////
|
|
|
|
void
|
|
DoMessage(
|
|
LPTSTR pszMessage
|
|
)
|
|
{
|
|
MessageBox(
|
|
ghDlg,
|
|
pszMessage,
|
|
gszTapi30,
|
|
MB_OK
|
|
);
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////
|
|
// SetStatusMessage
|
|
//////////////////////////////////////////////////////////////////
|
|
|
|
void
|
|
SetStatusMessage(
|
|
LPTSTR pszMessage
|
|
)
|
|
{
|
|
SetDlgItemText(
|
|
ghDlg,
|
|
IDC_STATUS,
|
|
pszMessage
|
|
);
|
|
}
|
|
|
|
|
|
|