You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1627 lines
41 KiB
1627 lines
41 KiB
|
|
//////////////////////////////////////////////////////////
|
|
// T3OUT.EXE
|
|
//
|
|
// Example of making an outgoing call with TAPI 3.0
|
|
//
|
|
// This application will allow a user to make a call
|
|
// by using TAPI 3.0. The application will simply look
|
|
// for the first TAPI line that support Audio, and can
|
|
// dial a phone number. It will then use that line to
|
|
// make calls.
|
|
//
|
|
// This application does not handle incoming calls, and
|
|
// does not process incoming messages.
|
|
//
|
|
//////////////////////////////////////////////////////////
|
|
|
|
#include "Precomp.h"
|
|
|
|
//////////////////////////////////////////////////////////
|
|
// Constants
|
|
//////////////////////////////////////////////////////////
|
|
|
|
const DWORD ADDRESSLENGTH = 128;
|
|
const DWORD MAXTERMINALS = 5;
|
|
|
|
const WCHAR * const gszTapi30 = L"TAPI 3.0 Outgoing Call Sample";
|
|
|
|
|
|
const WCHAR * const SDP = L"\
|
|
v=0\n\
|
|
o=muhan 0 0 IN IP4 172.31.76.157\n\
|
|
s=TestConf_01\n\
|
|
c=IN IP4 239.9.20.11/15\n\
|
|
t=0 0\n\
|
|
m=video 20050 RTP/AVP 34\n\
|
|
a=fmtp:34 CIF=4\n\
|
|
";
|
|
/*
|
|
k=clear:testkey\n\
|
|
m=video 20000 RTP/AVP 34\n\
|
|
m=audio 20040 RTP/AVP 0 4\n\
|
|
*/
|
|
const WCHAR * const gszConferenceName = L"Conference Name";
|
|
const WCHAR * const gszEmailName = L"Email Name";
|
|
const WCHAR * const gszMachineName = L"Machine Name";
|
|
const WCHAR * const gszPhoneNumber = L"Phone Number";
|
|
const WCHAR * const gszIPAddress = L"IP Address";
|
|
|
|
//////////////////////////////////////////////////////////
|
|
// GLOBALS
|
|
//////////////////////////////////////////////////////////
|
|
HINSTANCE ghInst;
|
|
HWND ghDlg = NULL;
|
|
ITTAPI * gpTapi;
|
|
ITAddress * gpAddress = NULL;
|
|
ITBasicCallControl * gpCall;
|
|
ITStream * gpVideoRenderStream;
|
|
ITTerminal * gpLastVideoWindow;
|
|
|
|
//////////////////////////////////////////////////////////
|
|
// PROTOTYPES
|
|
//////////////////////////////////////////////////////////
|
|
BOOL
|
|
CALLBACK
|
|
MainDialogProc(
|
|
HWND hDlg,
|
|
UINT uMsg,
|
|
WPARAM wParam,
|
|
LPARAM lParam
|
|
);
|
|
|
|
HRESULT
|
|
FindAnAddress(
|
|
DWORD dwAddressType,
|
|
BSTR * ppName
|
|
);
|
|
|
|
HRESULT
|
|
GetTerminal(
|
|
ITStream * pStream,
|
|
ITTerminal ** ppTerminal
|
|
);
|
|
|
|
HRESULT
|
|
FindCaptureTerminal(
|
|
ITStream * pStream,
|
|
ITTerminal ** ppTerminal
|
|
);
|
|
|
|
HRESULT
|
|
GetVideoRenderTerminal(
|
|
ITTerminal ** ppTerminal
|
|
);
|
|
|
|
HRESULT
|
|
MakeTheCall(
|
|
DWORD dwAddressType,
|
|
PWCHAR szAddressToCall
|
|
);
|
|
|
|
HRESULT
|
|
DisconnectTheCall();
|
|
|
|
void
|
|
DoMessage(
|
|
LPWSTR pszMessage
|
|
);
|
|
|
|
HRESULT
|
|
InitializeTapi();
|
|
|
|
void
|
|
ShutdownTapi();
|
|
|
|
void
|
|
EnableButton(
|
|
HWND hDlg,
|
|
int ID
|
|
);
|
|
void
|
|
DisableButton(
|
|
HWND hDlg,
|
|
int ID
|
|
);
|
|
|
|
BOOL
|
|
AddressSupportsMediaType(
|
|
ITAddress * pAddress,
|
|
long lMediaType
|
|
);
|
|
|
|
void ShowDialogs(ITBasicCallControl *pCall);
|
|
|
|
//////////////////////////////////////////////////////////
|
|
// WinMain
|
|
//////////////////////////////////////////////////////////
|
|
int
|
|
WINAPI
|
|
WinMain(
|
|
HINSTANCE hInst,
|
|
HINSTANCE hPrevInst,
|
|
LPSTR lpCmdLine,
|
|
int nCmdShow
|
|
)
|
|
{
|
|
ghInst = hInst;
|
|
|
|
|
|
// need to coinit
|
|
if ( FAILED( CoInitializeEx(NULL, COINIT_MULTITHREADED) ) )
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
if ( FAILED( InitializeTapi() ) )
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
// everything is initialized, so
|
|
// start the main dialog box
|
|
DialogBox(
|
|
ghInst,
|
|
MAKEINTRESOURCE(IDD_MAINDLG),
|
|
NULL,
|
|
MainDialogProc
|
|
);
|
|
|
|
|
|
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 ( FAILED(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 (S_OK != hr)
|
|
{
|
|
DoMessage(L"TAPI failed to initialize");
|
|
|
|
gpTapi->Release();
|
|
gpTapi = NULL;
|
|
|
|
return hr;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////
|
|
// ShutdownTapi
|
|
///////////////////////////////////////////////////////////////
|
|
void
|
|
ShutdownTapi()
|
|
{
|
|
// if there is still a call,
|
|
// release it
|
|
if (NULL != gpCall)
|
|
{
|
|
gpCall->Release();
|
|
gpCall = NULL;
|
|
}
|
|
|
|
// if we have an address object
|
|
// release it
|
|
if (NULL != gpAddress)
|
|
{
|
|
gpAddress->Release();
|
|
gpAddress = NULL;
|
|
}
|
|
|
|
// release main object.
|
|
if (NULL != gpTapi)
|
|
{
|
|
gpTapi->Shutdown();
|
|
gpTapi->Release();
|
|
gpTapi = NULL;
|
|
}
|
|
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
// InitAddressTypeComboBox
|
|
//
|
|
// Put address type string in the combo box
|
|
// and save the addresstype with the string
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////
|
|
void
|
|
InitAddressTypeComboBox(
|
|
HWND hComboBox
|
|
)
|
|
{
|
|
int i;
|
|
|
|
i = SendMessage( hComboBox, CB_ADDSTRING, 0, (long)gszConferenceName );
|
|
|
|
SendMessage(
|
|
hComboBox,
|
|
CB_SETITEMDATA ,
|
|
i,
|
|
(long)LINEADDRESSTYPE_SDP
|
|
);
|
|
|
|
SendMessage( hComboBox, CB_SETCURSEL, i, 0 );
|
|
|
|
|
|
i = SendMessage( hComboBox, CB_ADDSTRING, 0, (long)gszEmailName );
|
|
|
|
SendMessage(
|
|
hComboBox,
|
|
CB_SETITEMDATA ,
|
|
i,
|
|
(long)LINEADDRESSTYPE_EMAILNAME
|
|
);
|
|
|
|
|
|
i = SendMessage( hComboBox, CB_ADDSTRING, 0, (long)gszMachineName );
|
|
|
|
SendMessage(
|
|
hComboBox,
|
|
CB_SETITEMDATA ,
|
|
i,
|
|
(long)LINEADDRESSTYPE_DOMAINNAME
|
|
);
|
|
|
|
i = SendMessage( hComboBox, CB_ADDSTRING, 0, (long)gszPhoneNumber );
|
|
|
|
SendMessage(
|
|
hComboBox,
|
|
CB_SETITEMDATA ,
|
|
i,
|
|
(long)LINEADDRESSTYPE_PHONENUMBER
|
|
);
|
|
|
|
|
|
i = SendMessage( hComboBox, CB_ADDSTRING, 0, (long)gszIPAddress );
|
|
|
|
SendMessage(
|
|
hComboBox,
|
|
CB_SETITEMDATA ,
|
|
i,
|
|
(long)LINEADDRESSTYPE_IPADDRESS
|
|
);
|
|
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
// MainDlgProc
|
|
///////////////////////////////////////////////////////////////////////////
|
|
BOOL
|
|
CALLBACK
|
|
MainDialogProc(
|
|
HWND hDlg,
|
|
UINT uMsg,
|
|
WPARAM wParam,
|
|
LPARAM lParam
|
|
)
|
|
{
|
|
switch (uMsg)
|
|
{
|
|
case WM_INITDIALOG:
|
|
{
|
|
HWND hComboBox;
|
|
|
|
|
|
// set up dialog
|
|
ghDlg = hDlg;
|
|
|
|
EnableButton( hDlg, IDOK );
|
|
DisableButton( hDlg, IDC_DISCONNECT );
|
|
DisableButton( hDlg, IDC_SETTINGS );
|
|
|
|
hComboBox = GetDlgItem( hDlg, IDC_ADDRESSTYPE );
|
|
|
|
InitAddressTypeComboBox(hComboBox);
|
|
|
|
SetFocus( hComboBox );
|
|
|
|
return 0;
|
|
}
|
|
|
|
case WM_COMMAND:
|
|
{
|
|
switch(LOWORD(wParam))
|
|
{
|
|
case IDCANCEL:
|
|
{
|
|
// quit
|
|
EndDialog( hDlg, 0 );
|
|
|
|
break;
|
|
}
|
|
|
|
case IDOK:
|
|
{
|
|
// dial request
|
|
HWND hComboBox;
|
|
DWORD dwIndex;
|
|
DWORD dwAddressType;
|
|
WCHAR szAddressToCall[ADDRESSLENGTH];
|
|
|
|
|
|
// get the address type the user selected.
|
|
hComboBox = GetDlgItem( hDlg, IDC_ADDRESSTYPE );
|
|
dwIndex = SendMessage( hComboBox, CB_GETCURSEL, 0, 0 );
|
|
|
|
dwAddressType = SendMessage(
|
|
hComboBox,
|
|
CB_GETITEMDATA,
|
|
dwIndex,
|
|
0
|
|
);
|
|
|
|
// get the address the user wants to call
|
|
GetDlgItemText(
|
|
hDlg,
|
|
IDC_ADDRESS,
|
|
szAddressToCall,
|
|
ADDRESSLENGTH
|
|
);
|
|
|
|
// make the call
|
|
if ( S_OK == MakeTheCall(dwAddressType, szAddressToCall) )
|
|
{
|
|
EnableButton( hDlg, IDC_DISCONNECT );
|
|
EnableButton( hDlg, IDC_SETTINGS );
|
|
DisableButton( hDlg, IDOK );
|
|
}
|
|
else
|
|
{
|
|
DoMessage(L"The call failed to connect");
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case IDC_DISCONNECT:
|
|
{
|
|
// disconnect request
|
|
if (S_OK == DisconnectTheCall())
|
|
{
|
|
EnableButton( hDlg, IDOK );
|
|
DisableButton( hDlg, IDC_DISCONNECT );
|
|
DisableButton( hDlg, IDC_SETTINGS );
|
|
}
|
|
else
|
|
{
|
|
DoMessage(L"The call failed to disconnect");
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case IDC_SETTINGS:
|
|
{
|
|
// Show TAPI 3.1 configuration dialogs
|
|
ShowDialogs(gpCall);
|
|
|
|
break;
|
|
}
|
|
|
|
case IDC_ADDWINDOW:
|
|
{
|
|
if (gpVideoRenderStream)
|
|
{
|
|
ITTerminal * pTerminal;
|
|
HRESULT hr = GetVideoRenderTerminal(&pTerminal);
|
|
|
|
if ( SUCCEEDED(hr) )
|
|
{
|
|
hr = gpVideoRenderStream->SelectTerminal(pTerminal);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (gpLastVideoWindow) gpLastVideoWindow->Release();
|
|
gpLastVideoWindow = pTerminal;
|
|
}
|
|
else
|
|
{
|
|
pTerminal->Release();
|
|
}
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case IDC_DELWINDOW:
|
|
{
|
|
if (gpVideoRenderStream && gpLastVideoWindow)
|
|
{
|
|
HRESULT hr = gpVideoRenderStream->UnselectTerminal(gpLastVideoWindow);
|
|
|
|
gpLastVideoWindow->Release();
|
|
gpLastVideoWindow = NULL;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
default:
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
default:
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
// FindAnAddress
|
|
//
|
|
// Finds an address object that this application will use to make calls on.
|
|
//
|
|
// This function finds an address that supports the addresstype passed
|
|
// in, as well as the audioin and audioout media types.
|
|
//
|
|
// Return Value
|
|
// S_OK if it finds an address
|
|
// E_FAIL if it does not find an address
|
|
////////////////////////////////////////////////////////////////////////
|
|
HRESULT
|
|
FindAnAddress(
|
|
DWORD dwAddressType,
|
|
BSTR * ppName
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
BOOL bFoundAddress = FALSE;
|
|
IEnumAddress * pEnumAddress;
|
|
ITAddress * pAddress;
|
|
ITAddressCapabilities * pAddressCaps;
|
|
long lType = 0;
|
|
|
|
// if we have an address object
|
|
// release it
|
|
if (NULL != gpAddress)
|
|
{
|
|
gpAddress->Release();
|
|
gpAddress = NULL;
|
|
}
|
|
|
|
// enumerate the addresses
|
|
hr = gpTapi->EnumerateAddresses( &pEnumAddress );
|
|
|
|
if ( FAILED(hr) )
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
while ( !bFoundAddress )
|
|
{
|
|
// get the next address
|
|
hr = pEnumAddress->Next( 1, &pAddress, NULL );
|
|
|
|
if (S_OK != hr)
|
|
{
|
|
break;
|
|
}
|
|
|
|
|
|
hr = pAddress->QueryInterface(IID_ITAddressCapabilities, (void**)&pAddressCaps);
|
|
|
|
if ( SUCCEEDED(hr) )
|
|
{
|
|
|
|
hr = pAddressCaps->get_AddressCapability( AC_ADDRESSTYPES, &lType );
|
|
|
|
pAddressCaps->Release();
|
|
|
|
if ( SUCCEEDED(hr) )
|
|
{
|
|
// is the type we are looking for?
|
|
if ( dwAddressType & lType )
|
|
{
|
|
// does it support audio?
|
|
if ( AddressSupportsMediaType(pAddress, TAPIMEDIATYPE_AUDIO) )
|
|
{
|
|
// does it have a name?
|
|
if ( SUCCEEDED( pAddress->get_AddressName(ppName) ) )
|
|
{
|
|
// save it in the global variable
|
|
// since we break out of the loop, this one won't
|
|
// get released
|
|
|
|
gpAddress = pAddress;
|
|
|
|
bFoundAddress = TRUE;
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pAddress->Release();
|
|
|
|
} // end while loop
|
|
|
|
pEnumAddress->Release();
|
|
|
|
if (!bFoundAddress)
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////
|
|
// IsVideoCaptureStream
|
|
//
|
|
// Returns true if the stream is for video capture
|
|
/////////////////////////////////////////////////////////////////
|
|
|
|
BOOL
|
|
IsVideoCaptureStream(
|
|
ITStream * pStream
|
|
)
|
|
{
|
|
TERMINAL_DIRECTION tdStreamDirection;
|
|
long lStreamMediaType;
|
|
|
|
if ( FAILED( pStream ->get_Direction(&tdStreamDirection) ) ) { return FALSE; }
|
|
if ( FAILED( pStream ->get_MediaType(&lStreamMediaType) ) ) { return FALSE; }
|
|
|
|
return (tdStreamDirection == TD_CAPTURE) &&
|
|
(lStreamMediaType == TAPIMEDIATYPE_VIDEO);
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////
|
|
// IsVideoRenderStream
|
|
//
|
|
// Returns true if the stream is for video render
|
|
/////////////////////////////////////////////////////////////////
|
|
|
|
BOOL
|
|
IsVideoRenderStream(
|
|
ITStream * pStream
|
|
)
|
|
{
|
|
TERMINAL_DIRECTION tdStreamDirection;
|
|
long lStreamMediaType;
|
|
|
|
if ( FAILED( pStream ->get_Direction(&tdStreamDirection) ) ) { return FALSE; }
|
|
if ( FAILED( pStream ->get_MediaType(&lStreamMediaType) ) ) { return FALSE; }
|
|
|
|
return (tdStreamDirection == TD_RENDER) &&
|
|
(lStreamMediaType == TAPIMEDIATYPE_VIDEO);
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////
|
|
// IsAudioCaptureStream
|
|
//
|
|
// Returns true if the stream is for audio capture
|
|
/////////////////////////////////////////////////////////////////
|
|
|
|
BOOL
|
|
IsAudioCaptureStream(
|
|
ITStream * pStream
|
|
)
|
|
{
|
|
TERMINAL_DIRECTION tdStreamDirection;
|
|
long lStreamMediaType;
|
|
|
|
if ( FAILED( pStream ->get_Direction(&tdStreamDirection) ) ) { return FALSE; }
|
|
if ( FAILED( pStream ->get_MediaType(&lStreamMediaType) ) ) { return FALSE; }
|
|
|
|
return (tdStreamDirection == TD_CAPTURE) &&
|
|
(lStreamMediaType == TAPIMEDIATYPE_AUDIO);
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////
|
|
// EnablePreview
|
|
//
|
|
// Selects a video render terminal on a video capture stream,
|
|
// thereby enabling video preview.
|
|
/////////////////////////////////////////////////////////////////
|
|
|
|
HRESULT
|
|
EnablePreview(
|
|
ITStream * pStream
|
|
)
|
|
{
|
|
ITTerminal * pTerminal;
|
|
|
|
HRESULT hr = GetVideoRenderTerminal(&pTerminal);
|
|
|
|
if ( SUCCEEDED(hr) )
|
|
{
|
|
hr = pStream->SelectTerminal(pTerminal);
|
|
|
|
pTerminal->Release();
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT
|
|
EnableAEC(
|
|
ITStream * pStream
|
|
)
|
|
{
|
|
ITAudioDeviceControl *pITAudioDeviceControl;
|
|
|
|
HRESULT hr = pStream->QueryInterface(&pITAudioDeviceControl);
|
|
|
|
if ( SUCCEEDED(hr) )
|
|
{
|
|
hr = pITAudioDeviceControl->Set(AudioDevice_AcousticEchoCancellation, 1, TAPIControl_Flags_None);
|
|
|
|
pITAudioDeviceControl->Release();
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
void WINAPI DeleteMediaType(AM_MEDIA_TYPE* pmt)
|
|
{
|
|
if (pmt->cbFormat != 0) {
|
|
CoTaskMemFree((PVOID)pmt->pbFormat);
|
|
}
|
|
if (pmt->pUnk != NULL) {
|
|
pmt->pUnk->Release();
|
|
pmt->pUnk = NULL;
|
|
}
|
|
CoTaskMemFree((PVOID)pmt);;
|
|
}
|
|
|
|
HRESULT
|
|
CheckFormats(
|
|
ITStream *pStream
|
|
)
|
|
{
|
|
HRESULT hr;
|
|
DWORD dw;
|
|
BYTE * buf;
|
|
|
|
ITFormatControl *pITFormatControl;
|
|
hr = pStream->QueryInterface(&pITFormatControl);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
// get the number of capabilities of the stream.
|
|
DWORD dwCount;
|
|
hr = pITFormatControl->GetNumberOfCapabilities(&dwCount);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
goto cleanup;
|
|
}
|
|
|
|
TAPI_STREAM_CONFIG_CAPS caps;
|
|
AM_MEDIA_TYPE *pMediaType;
|
|
|
|
// Walk through each capability.
|
|
for (dw = 0; dw < dwCount; dw ++)
|
|
{
|
|
BOOL fEnabled;
|
|
|
|
hr = pITFormatControl->GetStreamCaps(dw, &pMediaType, &caps, &fEnabled);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
DeleteMediaType(pMediaType);
|
|
}
|
|
|
|
// get the current format.
|
|
hr = pITFormatControl->GetCurrentFormat(&pMediaType);
|
|
if (FAILED(hr))
|
|
{
|
|
goto cleanup;
|
|
}
|
|
|
|
// set it back just for fun.
|
|
// hr = pITFormatControl->SetPreferredFormat(pMediaType);
|
|
|
|
DeleteMediaType(pMediaType);
|
|
|
|
cleanup:
|
|
pITFormatControl->Release();
|
|
return hr;
|
|
}
|
|
|
|
|
|
#if USE_VFW
|
|
HRESULT
|
|
CheckVfwDialog(
|
|
ITTerminal *pTerminal
|
|
)
|
|
{
|
|
ITVfwCaptureDialogs *pVfwCaptureDialogs;
|
|
HRESULT hr = pTerminal->QueryInterface(&pVfwCaptureDialogs);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
hr = pVfwCaptureDialogs->HasDialog(VfwCaptureDialog_Source);
|
|
|
|
pVfwCaptureDialogs->Release();
|
|
|
|
return hr;
|
|
}
|
|
#endif
|
|
|
|
/////////////////////////////////////////////////////////////////
|
|
// SelectTerminalsOnCall
|
|
//
|
|
// Creates and selects terminals for all streams on the given
|
|
// call.
|
|
/////////////////////////////////////////////////////////////////
|
|
|
|
HRESULT
|
|
SelectTerminalsOnCall(
|
|
ITBasicCallControl * pCall
|
|
)
|
|
{
|
|
HRESULT hr;
|
|
|
|
//
|
|
// get the ITStreamControl interface for this call
|
|
//
|
|
|
|
ITStreamControl * pStreamControl;
|
|
|
|
hr = pCall->QueryInterface(IID_ITStreamControl,
|
|
(void **) &pStreamControl);
|
|
|
|
if ( SUCCEEDED(hr) )
|
|
{
|
|
//
|
|
// enumerate the streams
|
|
//
|
|
|
|
IEnumStream * pEnumStreams;
|
|
|
|
hr = pStreamControl->EnumerateStreams(&pEnumStreams);
|
|
|
|
pStreamControl->Release();
|
|
|
|
if ( SUCCEEDED(hr) )
|
|
{
|
|
//
|
|
// for each stream
|
|
//
|
|
|
|
ITStream * pStream;
|
|
|
|
while ( S_OK == pEnumStreams->Next(1, &pStream, NULL) )
|
|
{
|
|
ITTerminal * pTerminal;
|
|
|
|
//
|
|
// Find out the media type and direction of this stream,
|
|
// and create the default terminal for this media type and
|
|
// direction.
|
|
//
|
|
|
|
hr = GetTerminal(pStream,
|
|
&pTerminal);
|
|
|
|
if ( SUCCEEDED(hr) )
|
|
{
|
|
//
|
|
// Select the terminal on the stream.
|
|
//
|
|
|
|
if ( IsAudioCaptureStream( pStream ) )
|
|
{
|
|
//EnableAEC( pStream );
|
|
}
|
|
|
|
hr = pStream->SelectTerminal(pTerminal);
|
|
|
|
if ( SUCCEEDED(hr) )
|
|
{
|
|
//
|
|
// Also enable preview on the video capture stream.
|
|
//
|
|
|
|
|
|
if ( IsVideoCaptureStream( pStream ) )
|
|
{
|
|
EnablePreview( pStream );
|
|
}
|
|
|
|
if ( IsVideoRenderStream( pStream ) )
|
|
{
|
|
pStream->AddRef();
|
|
gpVideoRenderStream = pStream;
|
|
|
|
if (gpLastVideoWindow) gpLastVideoWindow->Release();
|
|
gpLastVideoWindow = pTerminal;
|
|
gpLastVideoWindow->AddRef();
|
|
}
|
|
}
|
|
|
|
CheckFormats(pStream);
|
|
|
|
pTerminal->Release();
|
|
}
|
|
|
|
pStream->Release();
|
|
|
|
}
|
|
|
|
pEnumStreams->Release();
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT SetLocalInfo()
|
|
{
|
|
const WCHAR * const LocalInfo[] = {
|
|
L"My CName",
|
|
L"Mu Han",
|
|
L"[email protected]",
|
|
L"703-5484",
|
|
L"Redmond",
|
|
L"Test app",
|
|
L"New interface test",
|
|
L"Some randmon info"
|
|
};
|
|
|
|
ITLocalParticipant *pLocalParticipant;
|
|
HRESULT hr = gpCall->QueryInterface(
|
|
IID_ITLocalParticipant,
|
|
(void **)&pLocalParticipant
|
|
);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
for (int i = 0; i < PTI_PRIVATE; i ++)
|
|
{
|
|
BSTR info;
|
|
hr = pLocalParticipant->get_LocalParticipantTypedInfo(
|
|
(PARTICIPANT_TYPED_INFO)i, &info
|
|
);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
SysFreeString(info);
|
|
}
|
|
|
|
info = SysAllocString(LocalInfo[i]);
|
|
hr = pLocalParticipant->put_LocalParticipantTypedInfo(
|
|
(PARTICIPANT_TYPED_INFO)i, info
|
|
);
|
|
|
|
SysFreeString(info);
|
|
}
|
|
|
|
pLocalParticipant->Release();
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////
|
|
// MakeTheCall
|
|
//
|
|
// Sets up and makes a call
|
|
/////////////////////////////////////////////////////////////////
|
|
|
|
HRESULT
|
|
MakeTheCall(
|
|
DWORD dwAddressType,
|
|
PWCHAR szAddressToCall
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
BSTR bstrAddressToCall;
|
|
BSTR pAddressName;
|
|
|
|
|
|
// find an address object that
|
|
// we will use to make calls on
|
|
|
|
hr = FindAnAddress(dwAddressType, &pAddressName);
|
|
|
|
if ( FAILED(hr) )
|
|
{
|
|
DoMessage(L"Could not find a TAPI address for making calls.");
|
|
|
|
return hr;
|
|
}
|
|
|
|
SysFreeString(pAddressName);
|
|
|
|
//
|
|
// find out which media types this address supports
|
|
//
|
|
|
|
long lMediaTypes = 0;
|
|
|
|
if ( AddressSupportsMediaType(gpAddress, TAPIMEDIATYPE_AUDIO) )
|
|
{
|
|
lMediaTypes |= TAPIMEDIATYPE_AUDIO; // we will use audio
|
|
}
|
|
|
|
|
|
if ( AddressSupportsMediaType(gpAddress, TAPIMEDIATYPE_VIDEO) )
|
|
{
|
|
lMediaTypes |= TAPIMEDIATYPE_VIDEO; // we will use video
|
|
}
|
|
|
|
|
|
//
|
|
// Create the call.
|
|
//
|
|
|
|
if (dwAddressType == LINEADDRESSTYPE_SDP)
|
|
{
|
|
bstrAddressToCall = SysAllocString( SDP );
|
|
}
|
|
else
|
|
{
|
|
bstrAddressToCall = SysAllocString( szAddressToCall );
|
|
}
|
|
|
|
hr = gpAddress->CreateCall( bstrAddressToCall,
|
|
dwAddressType,
|
|
lMediaTypes,
|
|
&gpCall);
|
|
|
|
SysFreeString ( bstrAddressToCall );
|
|
|
|
if ( FAILED(hr) )
|
|
{
|
|
DoMessage(L"Could not create a call.");
|
|
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// Select our terminals on the call; if any of the selections fail we
|
|
// proceed without that terminal.
|
|
//
|
|
|
|
hr = SelectTerminalsOnCall( gpCall );
|
|
|
|
//
|
|
// We're now ready to call connect.
|
|
//
|
|
// the VARIANT_TRUE parameter indicates that this
|
|
// call is sychronous - that is, it won't
|
|
// return until the call is in the connected
|
|
// state (or fails to connect)
|
|
// Since this is called in the UI thread,
|
|
// this means that the app will appear
|
|
// to hang until this function returns.
|
|
// Some TAPI service providers may take a long
|
|
// time for a call to reach the connected state.
|
|
//
|
|
|
|
// SetLocalInfo();
|
|
|
|
hr = gpCall->Connect( VARIANT_TRUE );
|
|
|
|
if ( FAILED(hr) )
|
|
{
|
|
gpCall->Release();
|
|
gpCall = NULL;
|
|
|
|
DoMessage(L"Could not connect the call.");
|
|
|
|
return hr;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CheckBasicAudio(
|
|
ITTerminal * pTerminal
|
|
)
|
|
{
|
|
HRESULT hr;
|
|
|
|
ITBasicAudioTerminal *pITBasicAudioTerminal;
|
|
hr = pTerminal->QueryInterface(&pITBasicAudioTerminal);
|
|
if ( FAILED(hr) ) return hr;
|
|
|
|
long lVolume;
|
|
hr = pITBasicAudioTerminal->get_Volume(&lVolume);
|
|
if ( SUCCEEDED(hr) )
|
|
{
|
|
hr = pITBasicAudioTerminal->put_Volume(lVolume * 2);
|
|
}
|
|
|
|
pITBasicAudioTerminal->Release();
|
|
return hr;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////
|
|
// GetTerminal
|
|
//
|
|
// Creates the default terminal for the passed-in stream.
|
|
//
|
|
/////////////////////////////////////////////////////////
|
|
HRESULT
|
|
GetTerminal(
|
|
ITStream * pStream,
|
|
ITTerminal ** ppTerminal
|
|
)
|
|
{
|
|
//
|
|
// Determine the media type and direction of this stream.
|
|
//
|
|
|
|
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;
|
|
|
|
//
|
|
// Since video render is a dynamic terminal, the procedure for creating
|
|
// it is different.
|
|
//
|
|
|
|
if ( ( lMediaType == TAPIMEDIATYPE_VIDEO ) &&
|
|
( dir == TD_RENDER ) )
|
|
{
|
|
return GetVideoRenderTerminal(ppTerminal);
|
|
}
|
|
|
|
//
|
|
// For all other terminals we use GetDefaultStaticTerminal.
|
|
// First, get the terminal support interface.
|
|
//
|
|
|
|
ITTerminalSupport * pTerminalSupport;
|
|
|
|
hr = gpAddress->QueryInterface( IID_ITTerminalSupport,
|
|
(void **)&pTerminalSupport);
|
|
|
|
if ( SUCCEEDED(hr) )
|
|
{
|
|
//
|
|
// get the default terminal for this MediaType and direction
|
|
//
|
|
|
|
hr = pTerminalSupport->GetDefaultStaticTerminal(lMediaType,
|
|
dir,
|
|
ppTerminal);
|
|
|
|
pTerminalSupport->Release();
|
|
|
|
if (TAPIMEDIATYPE_AUDIO)
|
|
{
|
|
CheckBasicAudio(*ppTerminal);
|
|
}
|
|
|
|
}
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////
|
|
// FindCaptureTerminal
|
|
//
|
|
// Find the capture terminal on a stream.
|
|
//
|
|
/////////////////////////////////////////////////////////
|
|
HRESULT
|
|
FindCaptureTerminal(
|
|
ITStream * pStream,
|
|
ITTerminal ** ppTerminal
|
|
)
|
|
{
|
|
HRESULT hr;
|
|
TERMINAL_DIRECTION dir;
|
|
|
|
// enumerate all the terminals.
|
|
IEnumTerminal *pEnumTerminals;
|
|
hr = pStream->EnumerateTerminals(&pEnumTerminals);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
BOOL fFound = FALSE;
|
|
ITTerminal *pTerminal;
|
|
|
|
// find the capture terminal.
|
|
while (S_OK == pEnumTerminals->Next(1, &pTerminal, NULL))
|
|
{
|
|
hr = pTerminal->get_Direction( &dir );
|
|
if ( FAILED(hr) ) continue;
|
|
|
|
if ( ( dir == TD_CAPTURE ) )
|
|
{
|
|
fFound = TRUE;
|
|
break;
|
|
}
|
|
pTerminal->Release();
|
|
}
|
|
|
|
pEnumTerminals->Release();
|
|
|
|
if (fFound)
|
|
{
|
|
*ppTerminal = pTerminal;
|
|
return S_OK;
|
|
}
|
|
|
|
return E_FAIL;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////
|
|
// GetVideoRenderTerminal
|
|
//
|
|
// Creates a dynamic terminal for the Video Render mediatype / direction
|
|
//
|
|
/////////////////////////////////////////////////////////
|
|
HRESULT
|
|
GetVideoRenderTerminal(
|
|
ITTerminal ** ppTerminal
|
|
)
|
|
{
|
|
//
|
|
// Construct a BSTR for the correct IID.
|
|
//
|
|
|
|
LPOLESTR lpTerminalClass;
|
|
|
|
HRESULT hr;
|
|
|
|
hr = StringFromIID(CLSID_VideoWindowTerm,
|
|
&lpTerminalClass);
|
|
|
|
if ( SUCCEEDED(hr) )
|
|
{
|
|
BSTR bstrTerminalClass;
|
|
|
|
bstrTerminalClass = SysAllocString ( lpTerminalClass );
|
|
|
|
CoTaskMemFree( lpTerminalClass );
|
|
|
|
if ( bstrTerminalClass == NULL )
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
else
|
|
{
|
|
|
|
//
|
|
// Get the terminal support interface
|
|
//
|
|
|
|
ITTerminalSupport * pTerminalSupport;
|
|
|
|
hr = gpAddress->QueryInterface(IID_ITTerminalSupport,
|
|
(void **)&pTerminalSupport);
|
|
|
|
if ( SUCCEEDED(hr) )
|
|
{
|
|
//
|
|
// Create the video render terminal.
|
|
//
|
|
|
|
hr = pTerminalSupport->CreateTerminal(bstrTerminalClass,
|
|
TAPIMEDIATYPE_VIDEO,
|
|
TD_RENDER,
|
|
ppTerminal);
|
|
|
|
pTerminalSupport->Release();
|
|
|
|
if ( SUCCEEDED(hr) )
|
|
{
|
|
// Get the video window interface for the terminal
|
|
IVideoWindow *pVideoWindow = NULL;
|
|
|
|
hr = (*ppTerminal)->QueryInterface(IID_IVideoWindow,
|
|
(void**)&pVideoWindow);
|
|
|
|
if ( SUCCEEDED(hr) )
|
|
{
|
|
//
|
|
// Set the visible member to true
|
|
//
|
|
// Note that the visibility property is the only one
|
|
// we can use on this terminal's IVideoWindow and
|
|
// IBasicVideo interfaces before the CME_STREAM_ACTIVE
|
|
// event is received for the stream. All other methods
|
|
// will fail until CME_STREAM_ACTIVE has been sent.
|
|
// Applications that need to control more about a video
|
|
// window than just its visibility must listen for the
|
|
// CME_STREAM_ACTIVE event. See the "t3in.exe" sample
|
|
// for how to do this.
|
|
//
|
|
|
|
hr = pVideoWindow->put_AutoShow( VARIANT_TRUE );
|
|
|
|
pVideoWindow->Release();
|
|
}
|
|
}
|
|
}
|
|
|
|
SysFreeString( bstrTerminalClass );
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// DisconnectTheCall
|
|
//
|
|
// Disconnects the call
|
|
//////////////////////////////////////////////////////////////////////
|
|
HRESULT
|
|
DisconnectTheCall()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
if (gpVideoRenderStream)
|
|
{
|
|
gpVideoRenderStream->Release();
|
|
gpVideoRenderStream = NULL;
|
|
}
|
|
|
|
if (gpLastVideoWindow)
|
|
{
|
|
gpLastVideoWindow->Release();
|
|
gpLastVideoWindow = NULL;
|
|
}
|
|
|
|
if (NULL != gpCall)
|
|
{
|
|
hr = gpCall->Disconnect( DC_NORMAL );
|
|
|
|
gpCall->Release();
|
|
gpCall = NULL;
|
|
|
|
return hr;
|
|
}
|
|
|
|
return S_FALSE;
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////
|
|
//
|
|
// HELPER FUNCTIONS
|
|
//
|
|
///////////////////////////////////////////////////////////////////
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////
|
|
// DoMessage
|
|
///////////////////////////////////////////////////////////////////
|
|
void
|
|
DoMessage(
|
|
LPWSTR pszMessage
|
|
)
|
|
{
|
|
MessageBox(
|
|
ghDlg,
|
|
pszMessage,
|
|
gszTapi30,
|
|
MB_OK
|
|
);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////
|
|
// EnableButton
|
|
//
|
|
// Enable, make default, and setfocus to a button
|
|
///////////////////////////////////////////////////////////////
|
|
void
|
|
EnableButton(
|
|
HWND hDlg,
|
|
int ID
|
|
)
|
|
{
|
|
SendDlgItemMessage(
|
|
hDlg,
|
|
ID,
|
|
BM_SETSTYLE,
|
|
BS_DEFPUSHBUTTON,
|
|
0
|
|
);
|
|
EnableWindow(
|
|
GetDlgItem( hDlg, ID ),
|
|
TRUE
|
|
);
|
|
SetFocus(
|
|
GetDlgItem( hDlg, ID )
|
|
);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
// DisableButton
|
|
//
|
|
// Disable a button
|
|
//////////////////////////////////////////////////////////////
|
|
void
|
|
DisableButton(
|
|
HWND hDlg,
|
|
int ID
|
|
)
|
|
{
|
|
SendDlgItemMessage(
|
|
hDlg,
|
|
ID,
|
|
BM_SETSTYLE,
|
|
BS_PUSHBUTTON,
|
|
0
|
|
);
|
|
EnableWindow(
|
|
GetDlgItem( hDlg, ID ),
|
|
FALSE
|
|
);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
// AddressSupportsMediaType
|
|
//
|
|
// Finds out if the given address supports the given media
|
|
// type, and returns TRUE if it does.
|
|
//////////////////////////////////////////////////////////////
|
|
|
|
BOOL
|
|
AddressSupportsMediaType(
|
|
ITAddress * pAddress,
|
|
long lMediaType
|
|
)
|
|
{
|
|
VARIANT_BOOL bSupport = VARIANT_FALSE;
|
|
ITMediaSupport * pMediaSupport;
|
|
|
|
if ( SUCCEEDED( pAddress->QueryInterface( IID_ITMediaSupport,
|
|
(void **)&pMediaSupport ) ) )
|
|
{
|
|
// does it support this media type?
|
|
pMediaSupport->QueryMediaType(
|
|
lMediaType,
|
|
&bSupport
|
|
);
|
|
|
|
pMediaSupport->Release();
|
|
}
|
|
|
|
return (bSupport == VARIANT_TRUE);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
// ShowDialogs
|
|
//
|
|
// Puts up TAPI 3.1 configuration dialogs:
|
|
// Camera Control page
|
|
// Video Settings page
|
|
// Format Control page
|
|
// Bitrate and Frame Rate Control page
|
|
//////////////////////////////////////////////////////////////
|
|
|
|
void
|
|
ShowDialogs(ITBasicCallControl *pCall)
|
|
{
|
|
#define MAX_PAGES 9
|
|
HRESULT Hr;
|
|
PROPSHEETHEADER Psh;
|
|
HPROPSHEETPAGE Pages[MAX_PAGES];
|
|
ITStreamControl *pITStreamControl = NULL;
|
|
IEnumStream *pEnumStreams = NULL;
|
|
ITTerminal *pVideoCaptureTerminal = NULL;
|
|
ITStream *pVideoCaptureStream = NULL;
|
|
ITStream *pVideoRenderStream = NULL;
|
|
ITStream *pAudioCaptureStream = NULL;
|
|
BOOL bfMatch = FALSE;
|
|
|
|
// Only show settings in a call
|
|
if (!pCall)
|
|
return;
|
|
|
|
// Get the ITStreamControl interface for this call
|
|
if (FAILED(Hr = pCall->QueryInterface(IID_ITStreamControl, (void **) &pITStreamControl)))
|
|
return;
|
|
|
|
// Look for the video capture stream and terminal
|
|
Hr = pITStreamControl->EnumerateStreams(&pEnumStreams);
|
|
pITStreamControl->Release();
|
|
|
|
if (FAILED(Hr))
|
|
return;
|
|
|
|
// For each stream
|
|
ITStream *pStream;
|
|
while (S_OK == pEnumStreams->Next(1, &pStream, NULL))
|
|
{
|
|
// Find out the media type and direction of this stream,
|
|
if (IsVideoCaptureStream(pStream))
|
|
{
|
|
pVideoCaptureStream = pStream;
|
|
|
|
// find the capture terminal selected on this stream.
|
|
FindCaptureTerminal(pVideoCaptureStream, &pVideoCaptureTerminal);
|
|
}
|
|
else if (IsAudioCaptureStream(pStream))
|
|
{
|
|
pAudioCaptureStream = pStream;
|
|
}
|
|
else if (IsVideoRenderStream(pStream))
|
|
{
|
|
pVideoRenderStream = pStream;
|
|
}
|
|
else
|
|
{
|
|
pStream->Release();
|
|
}
|
|
}
|
|
|
|
pEnumStreams->Release();
|
|
|
|
CCameraControlProperties CamControlOut;
|
|
CCameraControlProperties CamControlIn;
|
|
CProcAmpProperties VideoSettingsIn;
|
|
CProcAmpProperties VideoSettingsOut;
|
|
CCaptureProperties CaptureSettings;
|
|
CVDeviceProperties VideoDevice;
|
|
CNetworkProperties NetworkSettings;
|
|
CSystemProperties SystemSettings;
|
|
CAudRecProperties AudRecSettings;
|
|
|
|
// Initialize property sheet header and common controls
|
|
Psh.dwSize = sizeof(Psh);
|
|
Psh.dwFlags = PSH_DEFAULT;
|
|
Psh.hInstance = ghInst;
|
|
Psh.hwndParent = ghDlg;
|
|
Psh.pszCaption = L"Settings";
|
|
Psh.nPages = 0;
|
|
Psh.nStartPage = 0;
|
|
Psh.pfnCallback = NULL;
|
|
Psh.phpage = Pages;
|
|
|
|
if (pVideoCaptureStream)
|
|
{
|
|
// Create the outgoing video settings property page
|
|
if (Pages[Psh.nPages] = VideoSettingsOut.OnCreate(L"Image Settings Out"))
|
|
Psh.nPages++;
|
|
|
|
// Connect page to the stream
|
|
VideoSettingsOut.OnConnect(pVideoCaptureStream);
|
|
|
|
// Create the outgoing camera control property page
|
|
if (Pages[Psh.nPages] = CamControlOut.OnCreate(L"Camera Control Out"))
|
|
Psh.nPages++;
|
|
|
|
// Connect page to the stream
|
|
CamControlOut.OnConnect(pVideoCaptureStream);
|
|
|
|
// Create the incoming video settings property page
|
|
if (Pages[Psh.nPages] = VideoSettingsIn.OnCreate(L"Image Settings In"))
|
|
Psh.nPages++;
|
|
|
|
// Connect page to the stream
|
|
VideoSettingsIn.OnConnect(pVideoRenderStream);
|
|
|
|
// Create the incoming camera control property page
|
|
if (Pages[Psh.nPages] = CamControlIn.OnCreate(L"Camera Control In"))
|
|
Psh.nPages++;
|
|
|
|
// Connect page to the stream
|
|
CamControlIn.OnConnect(pVideoRenderStream);
|
|
|
|
// Create the video stream control property page
|
|
if (Pages[Psh.nPages] = CaptureSettings.OnCreate())
|
|
Psh.nPages++;
|
|
|
|
// Connect page to the stream
|
|
CaptureSettings.OnConnect(pVideoCaptureStream);
|
|
|
|
// Create the video device control property page
|
|
if (Pages[Psh.nPages] = VideoDevice.OnCreate())
|
|
Psh.nPages++;
|
|
|
|
// Connect page to the stream
|
|
NetworkSettings.OnConnect(NULL, pVideoCaptureStream, NULL, NULL);
|
|
|
|
// Create the system settings property page
|
|
if (Pages[Psh.nPages] = SystemSettings.OnCreate())
|
|
Psh.nPages++;
|
|
|
|
// Connect page to the address object
|
|
SystemSettings.OnConnect(gpAddress);
|
|
|
|
}
|
|
|
|
if (pVideoCaptureTerminal)
|
|
{
|
|
// Connect page to the stream
|
|
VideoDevice.OnConnect(pVideoCaptureTerminal);
|
|
|
|
// Create the network control property page
|
|
if (Pages[Psh.nPages] = NetworkSettings.OnCreate())
|
|
Psh.nPages++;
|
|
}
|
|
|
|
if (pAudioCaptureStream)
|
|
{
|
|
// Connect page to the stream
|
|
AudRecSettings.OnConnect(pAudioCaptureStream);
|
|
|
|
// Create the network control property page
|
|
if (Pages[Psh.nPages] = AudRecSettings.OnCreate())
|
|
Psh.nPages++;
|
|
}
|
|
|
|
// Put up the property sheet
|
|
if (Psh.nPages)
|
|
PropertySheet(&Psh);
|
|
|
|
// Disconnect pages from the stream
|
|
VideoSettingsOut.OnDisconnect();
|
|
CamControlOut.OnDisconnect();
|
|
VideoSettingsIn.OnDisconnect();
|
|
CamControlIn.OnDisconnect();
|
|
CaptureSettings.OnDisconnect();
|
|
VideoDevice.OnDisconnect();
|
|
NetworkSettings.OnDisconnect();
|
|
SystemSettings.OnDisconnect();
|
|
AudRecSettings.OnDisconnect();
|
|
|
|
if (pVideoCaptureTerminal) pVideoCaptureTerminal->Release();
|
|
if (pVideoCaptureStream) pVideoCaptureStream->Release();
|
|
if (pVideoRenderStream) pVideoRenderStream->Release();
|
|
if (pAudioCaptureStream) pAudioCaptureStream->Release();
|
|
|
|
return;
|
|
}
|