483 lines
13 KiB
C++
483 lines
13 KiB
C++
/*++
|
|
|
|
Copyright (c) 1999 Microsoft Corporation. All Rights Reserved.
|
|
|
|
Module Name:
|
|
callnot.cpp
|
|
|
|
Abstract:
|
|
|
|
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"
|
|
|
|
extern ITBasicCallControl * gpCall;
|
|
extern HWND ghDlg;
|
|
extern BSTR gbstrVideo;
|
|
extern BOOL gfAutoAnswer;
|
|
|
|
void
|
|
DoMessage(
|
|
LPWSTR pszMessage
|
|
);
|
|
|
|
void
|
|
SetStatusMessage(
|
|
LPWSTR pszMessage
|
|
);
|
|
|
|
void
|
|
EnableButton(
|
|
int ID
|
|
);
|
|
void
|
|
DisableButton(
|
|
int ID
|
|
);
|
|
|
|
enum {
|
|
VWP_LEFT = 20,
|
|
VWP_TOP = 100
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////////////
|
|
// HostWindow
|
|
//
|
|
// This method takes a pVideoWindow interface pointer and puts
|
|
// the window in our dialog.
|
|
//
|
|
// The location depends on the iWhere parameter.
|
|
//
|
|
///////////////////////////////////////////////////////////////////
|
|
|
|
void
|
|
HostWindow(
|
|
IVideoWindow *pVideoWindow,
|
|
int iWhere
|
|
)
|
|
{
|
|
|
|
pVideoWindow->put_Owner( (LONG_PTR)ghDlg );
|
|
pVideoWindow->put_WindowStyle( WS_CHILDWINDOW | WS_BORDER );
|
|
|
|
long lWidth, lHeight;
|
|
pVideoWindow->get_Width( &lWidth );
|
|
pVideoWindow->get_Height( &lHeight );
|
|
|
|
pVideoWindow->SetWindowPosition( VWP_LEFT + (iWhere * (VWP_LEFT + lWidth)),
|
|
VWP_TOP,
|
|
lWidth,
|
|
lHeight );
|
|
|
|
pVideoWindow->put_Visible( VARIANT_TRUE );
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////
|
|
// GetVideoRenderTerminalFromStreamEvent
|
|
//
|
|
// Find out if this stream has a video render terminal. If not,
|
|
// we don't need to do anything with this stream. Also note
|
|
// if this is the video capture stream or the video render
|
|
// stream.
|
|
//
|
|
///////////////////////////////////////////////////////////////////
|
|
|
|
HRESULT
|
|
GetVideoRenderTerminalFromStreamEvent(
|
|
ITCallMediaEvent * pCallMediaEvent,
|
|
ITTerminal ** ppTerminal,
|
|
BOOL * pfRenderStream
|
|
)
|
|
{
|
|
HRESULT hr;
|
|
|
|
//
|
|
// Get the stream for this event.
|
|
//
|
|
|
|
ITStream * pStream;
|
|
|
|
hr = pCallMediaEvent->get_Stream( &pStream );
|
|
|
|
if ( FAILED(hr) || (pStream == NULL) )
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
|
|
//
|
|
// Find out if this is a video stream. If not, fail --
|
|
// we don't care about this stream.
|
|
//
|
|
|
|
long lMediaType;
|
|
|
|
hr = pStream->get_MediaType(&lMediaType);
|
|
|
|
if ( FAILED(hr) )
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
if ( lMediaType != TAPIMEDIATYPE_VIDEO )
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
|
|
//
|
|
// Find out the direction of this stream. We will return this info to the
|
|
// caller.
|
|
//
|
|
|
|
TERMINAL_DIRECTION td;
|
|
|
|
hr = pStream->get_Direction(&td);
|
|
|
|
if ( FAILED(hr) )
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
*pfRenderStream = (td == TD_RENDER);
|
|
|
|
//
|
|
// Enumerate the terminals on this stream.
|
|
//
|
|
|
|
IEnumTerminal * pEnumTerminal;
|
|
|
|
hr = pStream->EnumerateTerminals( &pEnumTerminal );
|
|
|
|
pStream->Release();
|
|
|
|
if ( FAILED(hr) )
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// Search for the first video render terminal and return it. If there are
|
|
// none, fail -- we don't care about this stream.
|
|
//
|
|
|
|
while ( S_OK == pEnumTerminal->Next(1, ppTerminal, NULL) )
|
|
{
|
|
if ( SUCCEEDED( (*ppTerminal)->get_Direction(&td) ) )
|
|
{
|
|
if ( td == TD_RENDER )
|
|
{
|
|
// found it; done with enum, but keep ref on terminal
|
|
pEnumTerminal->Release();
|
|
|
|
return S_OK;
|
|
}
|
|
}
|
|
|
|
// this is not the right one
|
|
(*ppTerminal)->Release();
|
|
}
|
|
|
|
pEnumTerminal->Release();
|
|
|
|
return E_FAIL; // no terminal found
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////
|
|
// 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:
|
|
{
|
|
//
|
|
// TE_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( L"Incoming call, but failed to get the interface");
|
|
}
|
|
else
|
|
{
|
|
CALL_PRIVILEGE cp;
|
|
ITCallInfo * pCall;
|
|
|
|
//
|
|
// get the call
|
|
//
|
|
|
|
hr = pNotify->get_Call( &pCall );
|
|
|
|
pNotify->Release();
|
|
|
|
if ( SUCCEEDED(hr) )
|
|
{
|
|
//
|
|
// check to see if we own the call
|
|
//
|
|
|
|
hr = pCall->get_Privilege( &cp );
|
|
|
|
if ( FAILED(hr) || (CP_OWNER != cp) )
|
|
{
|
|
//
|
|
// just ignore it if we don't own it
|
|
//
|
|
|
|
pCall->Release();
|
|
|
|
pEvent->Release(); // we addrefed it CTAPIEventNotification::Event()
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
//
|
|
// Get the ITBasicCallControl interface and save it in our
|
|
// global variable.
|
|
//
|
|
|
|
hr = pCall->QueryInterface( IID_ITBasicCallControl,
|
|
(void**)&gpCall );
|
|
|
|
pCall->Release();
|
|
|
|
if ( SUCCEEDED(hr) )
|
|
{
|
|
//
|
|
// update UI
|
|
//
|
|
|
|
EnableButton( IDC_ANSWER );
|
|
DisableButton( IDC_DISCONNECT );
|
|
SetStatusMessage(L"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
|
|
hr = pEvent->QueryInterface( IID_ITCallStateEvent, (void **)&pCallStateEvent );
|
|
|
|
if ( FAILED(hr) )
|
|
{
|
|
break;
|
|
}
|
|
|
|
// get the CallState that we are being notified of.
|
|
hr = pCallStateEvent->get_State( &cs );
|
|
|
|
// Release the interface
|
|
pCallStateEvent->Release();
|
|
|
|
if ( FAILED(hr) )
|
|
{
|
|
break;
|
|
}
|
|
|
|
// if it's offering, update our UI
|
|
if (CS_OFFERING == cs)
|
|
{
|
|
if (gfAutoAnswer)
|
|
{
|
|
PostMessage(ghDlg, WM_COMMAND, IDC_ANSWER, 0);
|
|
}
|
|
else
|
|
{
|
|
SetStatusMessage(L"Click the Answer button");
|
|
}
|
|
}
|
|
else if (CS_DISCONNECTED == cs)
|
|
{
|
|
PostMessage(ghDlg, WM_COMMAND, IDC_DISCONNECTED, 0);
|
|
}
|
|
else if (CS_CONNECTED == cs)
|
|
{
|
|
// nothing to do -- we handle connection synchronously
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case TE_CALLMEDIA:
|
|
{
|
|
// TE_CALLMEDIA is a media event. pEvent is
|
|
// an ITCallMediaEvent
|
|
|
|
CALL_MEDIA_EVENT cme;
|
|
ITCallMediaEvent * pCallMediaEvent;
|
|
|
|
// Get the interface
|
|
hr = pEvent->QueryInterface( IID_ITCallMediaEvent, (void **)&pCallMediaEvent );
|
|
|
|
if ( FAILED(hr) )
|
|
{
|
|
break;
|
|
}
|
|
|
|
// get the CALL_MEDIA_EVENT that we are being notified of.
|
|
hr = pCallMediaEvent->get_Event( &cme );
|
|
|
|
if ( SUCCEEDED(hr) )
|
|
{
|
|
switch ( cme )
|
|
{
|
|
case CME_STREAM_NOT_USED:
|
|
case CME_STREAM_INACTIVE:
|
|
case CME_NEW_STREAM:
|
|
break;
|
|
|
|
case CME_STREAM_FAIL:
|
|
DoMessage( L"Call media event: stream failed");
|
|
break;
|
|
|
|
case CME_TERMINAL_FAIL:
|
|
DoMessage( L"Call media event: terminal failed");
|
|
break;
|
|
|
|
case CME_STREAM_ACTIVE:
|
|
{
|
|
//
|
|
// Find out if this stream has a video render terminal. If not,
|
|
// we don't need to do anything with this stream. Also note
|
|
// if this is the video capture stream or the video render
|
|
// stream.
|
|
//
|
|
|
|
ITTerminal * pTerminal;
|
|
BOOL fRenderStream;
|
|
|
|
hr = GetVideoRenderTerminalFromStreamEvent(
|
|
pCallMediaEvent,
|
|
&pTerminal,
|
|
&fRenderStream
|
|
);
|
|
|
|
if ( SUCCEEDED(hr) )
|
|
{
|
|
// Get the video window interface for the terminal
|
|
IVideoWindow *pVideoWindow = NULL;
|
|
|
|
hr = pTerminal->QueryInterface( IID_IVideoWindow, (void**)&pVideoWindow );
|
|
|
|
pTerminal->Release();
|
|
|
|
if ( SUCCEEDED(hr) )
|
|
{
|
|
// Put this window in our dialog
|
|
HostWindow(pVideoWindow, fRenderStream);
|
|
|
|
pVideoWindow->Release();
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
// We no longer need this interface.
|
|
pCallMediaEvent->Release();
|
|
|
|
break;
|
|
}
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
pEvent->Release(); // we addrefed it CTAPIEventNotification::Event()
|
|
|
|
return S_OK;
|
|
}
|
|
|