2025-04-27 07:49:33 -04:00

461 lines
13 KiB
C++

//////////////////////////////////////////////////////////////////////
//
//
// Copyright (c) 1998-1999 Microsoft Corporation
//
//
// 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 <control.h>
#include <strmif.h>
#include "callnot.h"
#include "resource.h"
#include "term.h"
extern ITBasicCallControl * gpCall;
extern HWND ghDlg;
extern BSTR gbstrAudio;
extern bool g_fPlay;
extern bool g_fRecord;
extern ITTerminal *g_pPlayMedStrmTerm, *g_pRecordMedStrmTerm;
extern ITStream *g_pRecordStream;
CWorkerThread gWorkerThread;
void
DoMessage(
LPTSTR pszMessage
);
void
SetStatusMessage(
LPTSTR pszMessage
);
HRESULT GetTerminalFromStreamEvent( ITCallMediaEvent * pCallMediaEvent,
ITTerminal ** ppTerminal )
{
HRESULT hr;
//
// Get the stream for this event.
//
ITStream * pStream;
hr = pCallMediaEvent->get_Stream( &pStream );
if ( FAILED(hr) || (pStream == NULL) ) return E_FAIL;
//
// Enumerate the terminals on this stream.
//
IEnumTerminal * pEnumTerminal;
hr = pStream->EnumerateTerminals( &pEnumTerminal );
pStream->Release();
if ( FAILED(hr) ) return hr;
//
// Get the first terminal -- if there aren't any, return E_FAIL so that
// we skip this event (this happens when the terminal is on the other
// stream).
//
hr = pEnumTerminal->Next(1, ppTerminal, NULL);
if ( hr != S_OK )
{
pEnumTerminal->Release();
return E_FAIL;
}
//
// Sanity check: see if there's more than one terminal. That shouldn't
// happen since we only ever select one terminal on each stream.
//
ITTerminal * pExtraTerminal;
hr = pEnumTerminal->Next(1, &pExtraTerminal, NULL);
pEnumTerminal->Release();
if ( hr == S_OK ) // more than one terminal -- impossible!
{
pExtraTerminal->Release();
(*ppTerminal)->Release();
*ppTerminal = NULL;
return E_UNEXPECTED;
}
return S_OK;
}
///////////////////////////////////////////////////////////////////
// PlayAndRecordMessage
//
// Plays a recorded message and records the incoming message.
//
///////////////////////////////////////////////////////////////////
HRESULT PlayAndRecordMessage(ITTerminal *pPlayStreamTerm, ITTerminal *pRecordStreamTerm)
{
CStreamMessageWI *pwi = new CStreamMessageWI();
if (NULL == pwi)
{
return E_OUTOFMEMORY;
}
BOOL InitStatus = pwi->Init(pPlayStreamTerm, pRecordStreamTerm);
if (!InitStatus)
{
delete pwi;
return E_FAIL;
}
HRESULT hr = gWorkerThread.AsyncWorkItem(pwi);
if (FAILED(hr))
{
//
// the message was not queued and will not be processed and freed.
// delete it here
//
delete pwi;
return hr;
}
return S_OK;
}
///////////////////////////////////////////////////////////////////
// CallEventNotification
//
// The only method in the ITCallEventNotification interface. This gets
// called by TAPI 3.0 when there is a call event to report. This just
// posts the message to our UI thread, so that we do as little as
// possible on TAPI's callback thread.
//
///////////////////////////////////////////////////////////////////
HRESULT
STDMETHODCALLTYPE
CTAPIEventNotification::Event(
TAPI_EVENT TapiEvent,
IDispatch * pEvent
)
{
//
// Addref the event so it doesn't go away.
//
pEvent->AddRef();
//
// Post a message to our own UI thread.
//
PostMessage(
ghDlg,
WM_PRIVATETAPIEVENT,
(WPARAM) TapiEvent,
(LPARAM) pEvent
);
return S_OK;
}
///////////////////////////////////////////////////////////////////
// OnTapiEvent
//
// This is the real event handler, called on our UI thread when
// the WM_PRIVATETAPIEVENT message is received
//
///////////////////////////////////////////////////////////////////
HRESULT
OnTapiEvent(
TAPI_EVENT TapiEvent,
IDispatch * pEvent
)
{
HRESULT hr;
switch ( TapiEvent )
{
case TE_CALLNOTIFICATION:
{
// CET_CALLNOTIFICATION means that the application is being notified
// of a new call.
//
// Note that we don't answer to call at this point. The application
// should wait for a CS_OFFERING CallState message before answering
// the call.
ITCallNotificationEvent * pNotify;
hr = pEvent->QueryInterface( IID_ITCallNotificationEvent, (void **)&pNotify );
if (S_OK != hr)
{
DoMessage( TEXT("Incoming call, but failed to get the interface"));
}
else
{
CALL_PRIVILEGE cp;
ITCallInfo * pCall;
//
// get the call
//
hr = pNotify->get_Call( &pCall );
//
// release the event object
//
pNotify->Release();
//
// check to see if we own the call
//
hr = pCall->get_Privilege( &cp );
if ( CP_OWNER != cp )
{
// just ignore it if we don't own it
pCall->Release();
pEvent->Release(); // we addrefed it CTAPIEventNotification::Event()
return S_OK;
}
hr = pCall->QueryInterface( IID_ITBasicCallControl, (void**)&gpCall );
pCall->Release();
//
// update UI
//
SetStatusMessage(TEXT("Incoming Owner Call"));
}
break;
}
case TE_CALLSTATE:
{
// TE_CALLSTATE is a call state event. pEvent is
// an ITCallStateEvent
CALL_STATE cs;
ITCallStateEvent * pCallStateEvent;
// Get the interface
pEvent->QueryInterface( IID_ITCallStateEvent, (void **)&pCallStateEvent );
// get the CallState that we are being notified of.
pCallStateEvent->get_State( &cs );
pCallStateEvent->Release();
// if it's offering, update our UI
if (CS_OFFERING == cs)
{
PostMessage(ghDlg, WM_COMMAND, IDC_ANSWER, 0);
}
else if (CS_DISCONNECTED == cs)
{
PostMessage(ghDlg, WM_COMMAND, IDC_DISCONNECTED, 0);
}
else if (CS_CONNECTED == cs)
{
PostMessage(ghDlg, WM_COMMAND, IDC_CONNECTED, 0);
}
break;
}
case TE_CALLMEDIA:
{
// TE_CALLMEDIA is a media event. pEvent is
// an ITCallMediaEvent
CALL_MEDIA_EVENT cme;
ITCallMediaEvent * pCallMediaEvent;
// Get the interface
pEvent->QueryInterface( IID_ITCallMediaEvent, (void **)&pCallMediaEvent );
// get the CALL_MEDIA_EVENT that we are being notified of.
pCallMediaEvent->get_Event( &cme );
switch ( cme )
{
case CME_STREAM_INACTIVE:
{
//
// CME_STREAM_INACTIVE tells us that the playback MST finished
// playing. Since only one terminal, either play or record, has
// to be selected at a time, the play terminal is first unselected
// and then the record terminal is selected.
// This event is only invoked in the play or the both case, since the call
// is disconnected after the reocrding is done.
//
if ( ( g_pRecordMedStrmTerm != NULL ) && ( g_pPlayMedStrmTerm != NULL ) )
{
ITStream *pStream;
if ( SUCCEEDED( hr = pCallMediaEvent->get_Stream(&pStream) ) )
{
pStream->UnselectTerminal(g_pPlayMedStrmTerm);
pStream->Release();
g_pPlayMedStrmTerm->Release();
g_pPlayMedStrmTerm = NULL;
}
else
{
::MessageBox(NULL, "UnSelect for Play Terminal Failed", "Call Media Event", MB_OK);
}
if ( SUCCEEDED( hr = g_pRecordStream->SelectTerminal(g_pRecordMedStrmTerm) ) )
{
g_pRecordMedStrmTerm->Release();
g_pRecordStream->Release();
g_pRecordMedStrmTerm= NULL;
}
else
{
::MessageBox(NULL, "Select Terminal for Record Failed", "Call Media Event", MB_OK);
}
}
// Once the recording is done, we disconnect but when only play
// is selected we need to post a disconnect message
else if (!g_fRecord)
{
PostMessage(ghDlg, WM_COMMAND, IDC_DISCONNECT, 0);
}
break;
}
case CME_STREAM_NOT_USED:
case CME_NEW_STREAM:
break;
case CME_STREAM_FAIL:
::MessageBox(NULL, "Stream failed", "Call media event", MB_OK);
break;
case CME_TERMINAL_FAIL:
::MessageBox(NULL, "Terminal failed", "Call media event", MB_OK);
break;
case CME_STREAM_ACTIVE:
{
ITTerminal *pPlayStreamTerm = NULL;
ITTerminal *pRecordStreamTerm = NULL;
//
// Get the terminal that's now active.
//
ITTerminal * pTerminal;
hr = GetTerminalFromStreamEvent(pCallMediaEvent, &pTerminal);
if ( FAILED(hr) ) break;
//
// Remember this terminal based on the direction.
//
TERMINAL_DIRECTION td;
hr = pTerminal->get_Direction( &td);
if ( FAILED(hr) )
{
pTerminal->Release();
break;
}
if ( TD_CAPTURE == td )
{
pPlayStreamTerm = pTerminal;
}
else if ( TD_RENDER == td )
{
pRecordStreamTerm = pTerminal;
}
else
{
// this is not supposed to happen
pTerminal->Release();
break;
}
//
// Now do the actual streaming...
//
PlayAndRecordMessage(pPlayStreamTerm, pRecordStreamTerm);
if (NULL != pPlayStreamTerm) pPlayStreamTerm->Release();
if (NULL != pRecordStreamTerm) pRecordStreamTerm->Release();
//
// Start over for the next call.
//
pPlayStreamTerm = NULL;
pRecordStreamTerm = NULL;
break;
}
default:
break;
}
// We no longer need this interface.
pCallMediaEvent->Release();
break;
}
}
pEvent->Release(); // we addrefed it CTAPIEventNotification::Event()
return S_OK;
}