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.
2976 lines
74 KiB
2976 lines
74 KiB
/////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Module: rdpsndc.c
|
|
//
|
|
// Purpose: Client-side audio redirection
|
|
//
|
|
// Copyright(C) Microsoft Corporation 2000
|
|
//
|
|
// History: 4-10-2000 vladimis [created]
|
|
//
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
#include "precom.h"
|
|
#include <sha.h>
|
|
#include <rc4.h>
|
|
#include "rdpsndc.h"
|
|
|
|
#undef ASSERT
|
|
#ifdef DBG
|
|
#undef TRC
|
|
#define TRC _DbgPrintMessage
|
|
#define ASSERT(Cond) if (!(Cond)) \
|
|
{ \
|
|
::_DbgPrintMessage( \
|
|
FATAL, \
|
|
_T("ASSERT in file: %s, line %d\n"), \
|
|
_T(__FILE__), \
|
|
__LINE__); \
|
|
DebugBreak(); \
|
|
}
|
|
#else // !DBG
|
|
#define TRC
|
|
#define ASSERT
|
|
#endif // !DBG
|
|
|
|
|
|
#ifdef UNICODE
|
|
#define _NAMEOFCLAS L"RDPSoundWnd"
|
|
#else // !UNICODE
|
|
#define _NAMEOFCLAS "RDPSoundWnd"
|
|
#endif
|
|
|
|
|
|
#define WM_WSOCK WM_USER
|
|
#define WM_RESAMPLE (WM_USER + 1)
|
|
|
|
#define WAVE_CLOSE_TIMEOUT 3000
|
|
|
|
//
|
|
// WMA codec description
|
|
//
|
|
#define WMAUDIO_DEC_KEY "1A0F78F0-EC8A-11d2-BBBE-006008320064"
|
|
#define WAVE_FORMAT_WMAUDIO2 0x161
|
|
#ifdef _WIN32
|
|
#include <pshpack1.h>
|
|
#else
|
|
#ifndef RC_INVOKED
|
|
#pragma pack(1)
|
|
#endif
|
|
#endif
|
|
typedef struct wmaudio2waveformat_tag {
|
|
WAVEFORMATEX wfx;
|
|
DWORD dwSamplesPerBlock;
|
|
WORD wEncodeOptions;
|
|
DWORD dwSuperBlockAlign;
|
|
} WMAUDIO2WAVEFORMAT;
|
|
#ifdef _WIN32
|
|
#include <poppack.h>
|
|
#else
|
|
#ifndef RC_INVOKED
|
|
#pragma pack()
|
|
#endif
|
|
#endif
|
|
|
|
// Trace levels
|
|
//
|
|
const TCHAR *ALV = _T("TSSNDC - ALV:");
|
|
const TCHAR *INF = _T("TSSNDC - INF:");
|
|
const TCHAR *WRN = _T("TSSNDC - WRN:");
|
|
const TCHAR *ERR = _T("TSSNDC - ERR:");
|
|
const TCHAR *FATAL = _T("TSSNDC - !FATAL!:");
|
|
|
|
/*
|
|
* Function declarations
|
|
*
|
|
*/
|
|
|
|
VOID
|
|
VCAPITYPE
|
|
OpenEventFnEx(
|
|
IN PVOID lpUserParam,
|
|
IN DWORD OpenHandle,
|
|
IN UINT event,
|
|
IN PVOID pData,
|
|
IN UINT32 dataLength,
|
|
IN UINT32 totalLength,
|
|
IN UINT32 dataFlags
|
|
);
|
|
|
|
INT
|
|
WSInit(
|
|
VOID
|
|
);
|
|
|
|
LRESULT
|
|
CALLBACK
|
|
MsgWndProc(
|
|
HWND hwnd,
|
|
UINT uiMessage,
|
|
WPARAM wParam,
|
|
LPARAM lParam
|
|
);
|
|
|
|
VOID
|
|
_cdecl
|
|
_DbgPrintMessage(
|
|
LPCTSTR level,
|
|
LPCTSTR format,
|
|
...
|
|
);
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
/*
|
|
* Function:
|
|
* OpenEventFn
|
|
*
|
|
* Description:
|
|
* Called by the TS client on channel read/write ready
|
|
*
|
|
* Parameters:
|
|
* lpUserParam - parameter passed from VirtualChannelEntryEx
|
|
* OpenaHandle - Channel handle, returned by VirtualChanneOpen
|
|
* event - event type
|
|
* pData - meaning depends on event type
|
|
* dataLength - size of pData
|
|
* dataFlags - information about the received data
|
|
*
|
|
*/
|
|
VOID
|
|
VCAPITYPE
|
|
OpenEventFnEx(
|
|
IN PVOID lpUserParam,
|
|
IN DWORD OpenHandle,
|
|
IN UINT event,
|
|
IN PVOID pData,
|
|
IN UINT32 dataLength,
|
|
IN UINT32 totalLength,
|
|
IN UINT32 dataFlags
|
|
)
|
|
{
|
|
CRDPSound *pSnd;
|
|
|
|
ASSERT( NULL != lpUserParam );
|
|
|
|
if ( NULL != lpUserParam )
|
|
{
|
|
pSnd = ((VCManager*)lpUserParam)->GetSound();
|
|
ASSERT( NULL != pSnd );
|
|
|
|
if ( NULL != pSnd )
|
|
pSnd->OpenEventFn(
|
|
OpenHandle,
|
|
event,
|
|
pData,
|
|
dataLength,
|
|
totalLength,
|
|
dataFlags
|
|
);
|
|
}
|
|
}
|
|
|
|
LRESULT
|
|
CALLBACK
|
|
MsgWndProc(
|
|
HWND hwnd,
|
|
UINT uiMessage,
|
|
WPARAM wParam,
|
|
LPARAM lParam
|
|
)
|
|
{
|
|
LRESULT rv = 0;
|
|
CRDPSound *pSnd;
|
|
|
|
pSnd = (CRDPSound *)
|
|
#ifndef OS_WINCE
|
|
GetWindowLongPtr( hwnd, GWLP_USERDATA );
|
|
#else // OS_WINCE
|
|
GetWindowLong( hwnd, GWL_USERDATA );
|
|
#endif
|
|
|
|
switch (uiMessage)
|
|
{
|
|
case MM_WOM_OPEN:
|
|
ASSERT( NULL != pSnd );
|
|
if ( NULL != pSnd )
|
|
pSnd->vcwaveCallback( (HWAVEOUT)wParam, WOM_OPEN, NULL );
|
|
break;
|
|
case MM_WOM_CLOSE:
|
|
ASSERT( NULL != pSnd );
|
|
if ( NULL != pSnd )
|
|
pSnd->vcwaveCallback( (HWAVEOUT)wParam, WOM_CLOSE, NULL );
|
|
break;
|
|
|
|
case MM_WOM_DONE:
|
|
ASSERT( NULL != pSnd );
|
|
if ( NULL != pSnd )
|
|
pSnd->vcwaveCallback((HWAVEOUT)wParam, WOM_DONE, (LPWAVEHDR)lParam);
|
|
break;
|
|
|
|
case WM_RESAMPLE:
|
|
ASSERT( NULL != pSnd );
|
|
if ( NULL != pSnd )
|
|
pSnd->vcwaveResample();
|
|
break;
|
|
case WM_WSOCK:
|
|
ASSERT( NULL != pSnd );
|
|
pSnd->DGramSocketMessage(wParam, lParam);
|
|
break;
|
|
|
|
case WM_DESTROY:
|
|
PostQuitMessage(0);
|
|
break;
|
|
|
|
default:
|
|
rv = DefWindowProc(hwnd, uiMessage, wParam, lParam);
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
/*
|
|
* Function:
|
|
* InitEventFn
|
|
*
|
|
* Description:
|
|
* Called by InitEventFn
|
|
*
|
|
* Parameters:
|
|
* pInitHandle - connection handle
|
|
* event - Event id
|
|
* pData - Data, meaning depends on event id
|
|
* dataLength - Length of data
|
|
* (see MSDN: VirtualChannelInitEvent)
|
|
*
|
|
*/
|
|
VOID
|
|
CRDPSound::InitEventFn(
|
|
PVOID pInitHandle,
|
|
UINT event,
|
|
PVOID pData,
|
|
UINT dataLength
|
|
)
|
|
{
|
|
UINT rc;
|
|
UNREFERENCED_PARAMETER(dataLength);
|
|
|
|
ASSERT( pInitHandle == m_pInitHandle );
|
|
switch(event)
|
|
{
|
|
case CHANNEL_EVENT_INITIALIZED:
|
|
TRC(ALV, _T("InitEventFnEx: CHANNEL_EVENT_INITILIZED\n"));
|
|
::WSInit();
|
|
/*
|
|
#ifdef OS_WINCE
|
|
#error create single instance
|
|
::WSAStartAsyncThread();
|
|
#endif // OS_WINCE
|
|
*/
|
|
CreateMsgWindow(m_hInst);
|
|
break;
|
|
case CHANNEL_EVENT_CONNECTED:
|
|
TRC(ALV, _T("InitEventFnEx: CHANNEL_EVENT_CONNECTED to %s\n"), pData);
|
|
ASSERT(m_ChannelEntries.pVirtualChannelOpenEx);
|
|
|
|
rc = (m_ChannelEntries.pVirtualChannelOpenEx)(
|
|
pInitHandle,
|
|
&m_dwChannel,
|
|
_SNDVC_NAME,
|
|
OpenEventFnEx
|
|
);
|
|
|
|
if (rc != CHANNEL_RC_OK)
|
|
{
|
|
TRC(WRN, _T("InitEventFnEx: VirtualChannelOpen returned %d\n"), rc);
|
|
m_dwChannel = INVALID_CHANNELID;
|
|
} else {
|
|
// All's OK, check the channel handle
|
|
//
|
|
ASSERT(m_dwChannel != INVALID_CHANNELID);
|
|
}
|
|
|
|
// Prepare for connectionless data
|
|
//
|
|
DGramInit();
|
|
|
|
break;
|
|
case CHANNEL_EVENT_DISCONNECTED:
|
|
TRC(ALV, _T("InitEventFnEx: CHANNEL_EVENT_DISCONNECTED\n"));
|
|
ASSERT(m_ChannelEntries.pVirtualChannelCloseEx);
|
|
|
|
if (m_dwChannel != INVALID_CHANNELID)
|
|
{
|
|
rc = m_ChannelEntries.pVirtualChannelCloseEx(
|
|
m_pInitHandle,
|
|
m_dwChannel
|
|
);
|
|
if (rc != CHANNEL_RC_OK)
|
|
{
|
|
TRC(WRN, _T("InitEventFnEx: VirtualChannelClose returned %d\n"), rc);
|
|
}
|
|
m_dwChannel = INVALID_CHANNELID;
|
|
|
|
if (NULL != m_hWave)
|
|
{
|
|
waveOutReset(m_hWave);
|
|
vcwaveClose();
|
|
}
|
|
}
|
|
|
|
// destroy the UDP socket
|
|
//
|
|
DGramDone();
|
|
|
|
m_bPrologReceived = FALSE;
|
|
m_dwBytesInProlog = 0;
|
|
m_dwBytesInBody = 0;
|
|
|
|
//
|
|
// dispose the cache format
|
|
//
|
|
vcwaveCleanSoundFormats();
|
|
|
|
vcwaveFreeAllWaves();
|
|
_FragSlotFreeAll();
|
|
break;
|
|
case CHANNEL_EVENT_V1_CONNECTED:
|
|
TRC(ALV, _T("InitEventFnEx: CHANNEL_EVENT_V1_CONNECTED\n"));
|
|
break;
|
|
case CHANNEL_EVENT_TERMINATED:
|
|
TRC(ALV, _T("InitEventFnEx: CHANNEL_EVENT_TERMINATED\n"));
|
|
WSACleanup();
|
|
if (NULL != m_hWave)
|
|
{
|
|
waveOutReset(m_hWave);
|
|
vcwaveClose();
|
|
}
|
|
|
|
DGramDone();
|
|
/*
|
|
#ifdef OS_WINCE
|
|
#error see previous error
|
|
::WSACloseAsyncThread();
|
|
#endif // OS_WINCE
|
|
*/
|
|
if ( NULL != m_pProlog )
|
|
{
|
|
free( m_pProlog );
|
|
m_pProlog = NULL;
|
|
}
|
|
|
|
if ( NULL != m_pMsgBody )
|
|
{
|
|
free( m_pMsgBody );
|
|
m_pMsgBody = NULL;
|
|
}
|
|
|
|
vcwaveCleanSoundFormats();
|
|
vcwaveFreeAllWaves();
|
|
_FragSlotFreeAll();
|
|
|
|
DestroyMsgWindow();
|
|
|
|
break;
|
|
default:
|
|
TRC(ALV, _T("Unhandled event in InitEventFnEx: %d\n"), event);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Function:
|
|
* OpenEventFn
|
|
*
|
|
* Description:
|
|
* Called by OpenEventFnEx
|
|
*
|
|
* Parameters:
|
|
* OpenaHandle - Channel handle, returned by VirtualChanneOpen
|
|
* event - event type
|
|
* pData - meaning depends on event type
|
|
* dataLength - size of pData
|
|
* dataFlags - information about the received data
|
|
*
|
|
*/
|
|
VOID
|
|
CRDPSound::OpenEventFn(
|
|
DWORD OpenHandle,
|
|
UINT event,
|
|
PVOID pData,
|
|
UINT32 dataLength,
|
|
UINT32 totalLength,
|
|
UINT32 dataFlags
|
|
)
|
|
{
|
|
UNREFERENCED_PARAMETER(OpenHandle);
|
|
|
|
switch (event)
|
|
{
|
|
case CHANNEL_EVENT_DATA_RECEIVED:
|
|
#if _IO_DBG
|
|
TRC(ALV,
|
|
"OpenEventFn: CHANNEL_EVENT_DATA_RECEIVED, dataSize=0x%x, total=0x%x\n",
|
|
dataLength,
|
|
totalLength);
|
|
#endif
|
|
|
|
//
|
|
// save the data in global buffers
|
|
//
|
|
if (!m_bPrologReceived)
|
|
{
|
|
//
|
|
// receive the prolog (1st message)
|
|
//
|
|
|
|
if ( 0 != ( dataFlags & CHANNEL_FLAG_FIRST ))
|
|
m_dwBytesInProlog = 0;
|
|
|
|
if ( NULL == m_pProlog )
|
|
{
|
|
ASSERT( 0 != ( dataFlags & CHANNEL_FLAG_FIRST ));
|
|
|
|
m_pProlog = malloc( totalLength );
|
|
|
|
if ( NULL == m_pProlog )
|
|
{
|
|
TRC( FATAL, _T("OpenEventFn: failed to allocate %d bytes\n"),
|
|
totalLength);
|
|
break;
|
|
}
|
|
|
|
m_dwPrologAllocated = totalLength;
|
|
}
|
|
|
|
if ( totalLength > m_dwPrologAllocated )
|
|
{
|
|
PVOID pNewProlog;
|
|
|
|
ASSERT( 0 != ( dataFlags & CHANNEL_FLAG_FIRST ));
|
|
|
|
pNewProlog = realloc( m_pProlog, totalLength );
|
|
|
|
if ( NULL == pNewProlog )
|
|
{
|
|
TRC( FATAL, _T("OpenEventFn: failed to allocate %d bytes\n"),
|
|
totalLength);
|
|
free ( m_pProlog );
|
|
m_pProlog = NULL;
|
|
m_dwPrologAllocated = 0;
|
|
break;
|
|
|
|
}
|
|
m_pProlog = pNewProlog;
|
|
m_dwPrologAllocated = totalLength;
|
|
}
|
|
|
|
if ( m_dwBytesInProlog + dataLength > m_dwPrologAllocated )
|
|
{
|
|
TRC( ERR, _T("An invalid VC packet received. Ignoring\n" ));
|
|
break;
|
|
}
|
|
|
|
memcpy( ((LPSTR)m_pProlog) + m_dwBytesInProlog, pData, dataLength );
|
|
m_dwBytesInProlog += dataLength;
|
|
|
|
ASSERT( m_dwBytesInProlog <= totalLength );
|
|
|
|
if ( 0 != ( dataFlags & CHANNEL_FLAG_LAST ))
|
|
{
|
|
m_bPrologReceived = TRUE;
|
|
|
|
ASSERT( sizeof(SNDPROLOG) <= m_dwBytesInProlog );
|
|
|
|
//
|
|
// check if we expect a body
|
|
//
|
|
ASSERT( m_dwBytesInProlog - sizeof(SNDPROLOG) <=
|
|
((PSNDPROLOG)m_pProlog)->BodySize);
|
|
|
|
if ( m_dwBytesInProlog - sizeof(SNDPROLOG) ==
|
|
((PSNDPROLOG)m_pProlog)->BodySize )
|
|
{
|
|
// no, proceed the message
|
|
//
|
|
DataArrived(
|
|
(PSNDPROLOG)m_pProlog,
|
|
((PSNDPROLOG)m_pProlog) + 1
|
|
);
|
|
|
|
m_bPrologReceived = FALSE;
|
|
m_dwBytesInProlog = 0;
|
|
m_dwBytesInBody = 0;
|
|
}
|
|
}
|
|
} else {
|
|
//
|
|
// receive the body (2nd message)
|
|
//
|
|
|
|
if ( 0 != ( dataFlags & CHANNEL_FLAG_FIRST ))
|
|
m_dwBytesInBody = 0;
|
|
|
|
if ( NULL == m_pMsgBody )
|
|
{
|
|
ASSERT( 0 != ( dataFlags & CHANNEL_FLAG_FIRST ));
|
|
|
|
m_pMsgBody = malloc( totalLength );
|
|
|
|
if ( NULL == m_pMsgBody )
|
|
{
|
|
TRC( FATAL, _T("OpenEventFn: failed to allocate %d bytes\n"),
|
|
totalLength);
|
|
break;
|
|
}
|
|
|
|
m_dwBodyAllocated = totalLength;
|
|
}
|
|
|
|
if ( totalLength > m_dwBodyAllocated )
|
|
{
|
|
PVOID pNewBody;
|
|
|
|
ASSERT( 0 != ( dataFlags & CHANNEL_FLAG_FIRST ));
|
|
|
|
pNewBody = realloc( m_pMsgBody, totalLength );
|
|
|
|
if ( NULL == pNewBody )
|
|
{
|
|
TRC( FATAL, _T("OpenEventFn: failed to allocate %d bytes\n"),
|
|
totalLength);
|
|
free ( m_pMsgBody );
|
|
m_pMsgBody = NULL;
|
|
m_dwBodyAllocated = 0;
|
|
break;
|
|
|
|
}
|
|
m_pMsgBody = pNewBody;
|
|
m_dwBodyAllocated = totalLength;
|
|
}
|
|
|
|
if ( m_dwBytesInBody + dataLength > m_dwBodyAllocated )
|
|
{
|
|
TRC( ERR, _T("An invalid VC packet received. Ignoring\n" ));
|
|
break;
|
|
}
|
|
|
|
memcpy( ((LPSTR)m_pMsgBody) + m_dwBytesInBody, pData, dataLength );
|
|
m_dwBytesInBody += dataLength;
|
|
|
|
ASSERT( m_dwBytesInBody <= totalLength );
|
|
|
|
if ( 0 != ( dataFlags & CHANNEL_FLAG_LAST ))
|
|
{
|
|
UINT32 *pdw;
|
|
|
|
//
|
|
// here comes the magic
|
|
// the server sends two packets, but it is possible
|
|
// in case of switching between sessions (or shadowing ?!)
|
|
// that the client can receive first packet from one session
|
|
// and the second from another
|
|
// that's why we have a valid message type for each packet
|
|
// by replacing the first word of the second message
|
|
// this word is kept in the end of the first message
|
|
//
|
|
if ( NULL != m_pMsgBody &&
|
|
SNDC_NONE != *((UINT32 *)m_pMsgBody) )
|
|
{
|
|
LPVOID pSwap;
|
|
DWORD dwSwap;
|
|
|
|
TRC(ERR, _T("OpenEventFn: messages not synchronized. ")
|
|
_T("Trying to fix it\n"));
|
|
|
|
pSwap = m_pProlog;
|
|
m_pProlog = m_pMsgBody;
|
|
m_pMsgBody = pSwap;
|
|
|
|
dwSwap = m_dwPrologAllocated;
|
|
m_dwPrologAllocated = m_dwBodyAllocated;
|
|
m_dwBodyAllocated = dwSwap;
|
|
|
|
m_dwBytesInProlog = m_dwBytesInBody;
|
|
m_dwBytesInBody = 0;
|
|
|
|
// we swapped the body and the prolog
|
|
// now, wait for the actual body
|
|
//
|
|
break;
|
|
}
|
|
|
|
//
|
|
// from the end of the prolog message, remove
|
|
// UINT32 word and place it at the begining of the body
|
|
//
|
|
|
|
ASSERT( sizeof(SNDPROLOG) + sizeof(UINT32) <= m_dwBytesInProlog);
|
|
if ( sizeof(SNDPROLOG) + sizeof(UINT32) > m_dwBytesInProlog )
|
|
{
|
|
TRC( ERR, _T("An invalid VC packet received. Ignoring\n" ));
|
|
break;
|
|
}
|
|
|
|
pdw = (UINT32 *)(((LPSTR)m_pProlog) +
|
|
m_dwBytesInProlog - sizeof(UINT32));
|
|
*((UINT32 *)m_pMsgBody) = *pdw;
|
|
|
|
//
|
|
// cut the prolog length by UINT32 size
|
|
//
|
|
m_dwBytesInProlog -= sizeof(UINT32);
|
|
|
|
DataArrived(
|
|
(PSNDPROLOG)m_pProlog,
|
|
m_pMsgBody
|
|
);
|
|
|
|
m_bPrologReceived = FALSE;
|
|
m_dwBytesInProlog = 0;
|
|
m_dwBytesInBody = 0;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case CHANNEL_EVENT_WRITE_COMPLETE:
|
|
{
|
|
TRC(ALV, _T("OpenEventFn: CHANNEL_EVENT_WRITE_COMPLETE, ptr=0x%p\n"),
|
|
pData);
|
|
|
|
free(pData);
|
|
|
|
}
|
|
break;
|
|
case CHANNEL_EVENT_WRITE_CANCELLED:
|
|
{
|
|
TRC(WRN, _T("OpenEventFn: CHANNEL_EVENT_WRITE_CANCELED. Cleaning up\n"));
|
|
free(pData);
|
|
}
|
|
break;
|
|
default:
|
|
TRC(ALV, _T("Unhandled event in OpenEventFn: %d\n"), event);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Function:
|
|
* ChannelWriteNCopy
|
|
*
|
|
* Description:
|
|
* Allocates a chunk of memory and sends it using ChannelWrite
|
|
* Allows the caller to free or reuse the buffer
|
|
*
|
|
* Parameters:
|
|
* pBuffer - Chunk pointer
|
|
* uiSize - Chunk size
|
|
*
|
|
* Returns:
|
|
* TRUE on success
|
|
*
|
|
*/
|
|
BOOL
|
|
CRDPSound::ChannelWriteNCopy(
|
|
LPVOID pBuffer,
|
|
UINT32 uiSize
|
|
)
|
|
{
|
|
BOOL rv = FALSE;
|
|
LPVOID pSendBuffer = NULL;
|
|
|
|
if ( INVALID_CHANNELID == m_dwChannel )
|
|
{
|
|
TRC(ERR, _T("ChannelWriteNCopy: invalid handle\n"));
|
|
goto exitpt;
|
|
}
|
|
|
|
if (NULL == pBuffer)
|
|
{
|
|
TRC(ERR, _T("ChannelWriteNCopy: buffer is NULL\n"));
|
|
goto exitpt;
|
|
}
|
|
|
|
pSendBuffer = malloc(uiSize);
|
|
|
|
if (pSendBuffer)
|
|
{
|
|
memcpy(pSendBuffer, pBuffer, uiSize);
|
|
rv = ChannelWrite( pSendBuffer, uiSize );
|
|
}
|
|
|
|
exitpt:
|
|
if (!rv)
|
|
{
|
|
if (pSendBuffer)
|
|
free(pSendBuffer);
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
/*
|
|
* Function:
|
|
* DataArrived
|
|
*
|
|
* Description:
|
|
* Processes a message arrived from the channel
|
|
*
|
|
* Parameters:
|
|
* pProlog - the message prolog, type and body size
|
|
* pBody - pointer to the message body
|
|
*
|
|
*/
|
|
VOID
|
|
CRDPSound::DataArrived(
|
|
PSNDPROLOG pProlog,
|
|
LPVOID pBody
|
|
)
|
|
{
|
|
ASSERT(pProlog);
|
|
ASSERT(pBody);
|
|
switch(pProlog->Type)
|
|
{
|
|
case SNDC_CLOSE:
|
|
|
|
TRC(ALV, _T("DataArrived: SNDC_CLOSE\n"));
|
|
|
|
if (m_hWave)
|
|
{
|
|
vcwaveClose();
|
|
}
|
|
break;
|
|
|
|
case SNDC_SETVOLUME:
|
|
{
|
|
PSNDSETVOLUME pSetVolume;
|
|
|
|
TRC(ALV, _T("DataArrived: SNDC_SETVOLUME\n"));
|
|
|
|
if ( pProlog->BodySize < sizeof( *pSetVolume ) - sizeof( *pProlog ))
|
|
{
|
|
TRC( ERR, _T("DataArrived: Invalid SNDC_SETVOLUME message\n" ));
|
|
break;
|
|
}
|
|
|
|
pSetVolume = (PSNDSETVOLUME)
|
|
(((LPSTR)pBody) - sizeof(*pProlog));
|
|
|
|
ASSERT(pProlog->BodySize == sizeof(*pSetVolume) - sizeof(*pProlog));
|
|
|
|
if (m_hWave)
|
|
waveOutSetVolume(m_hWave, pSetVolume->dwVolume);
|
|
}
|
|
break;
|
|
|
|
case SNDC_WAVE:
|
|
{
|
|
PSNDWAVE pWave;
|
|
|
|
//
|
|
// disable dgram response
|
|
//
|
|
m_dwRemoteDGramPort = 0;
|
|
m_ulRemoteDGramAddress = 0;
|
|
|
|
pWave = (PSNDWAVE)pProlog;
|
|
|
|
TRC(ALV, _T("DataArrived: SNDC_WAVE, block no: %d\n"),
|
|
pWave->cBlockNo);
|
|
|
|
if ( pProlog->BodySize < sizeof( *pWave ) - sizeof( *pProlog ))
|
|
{
|
|
TRC( ERR, _T("DataArrived: Invalid SNDC_WAVE message\n" ));
|
|
break;
|
|
}
|
|
|
|
vcwaveWrite(pWave->cBlockNo,
|
|
pWave->wFormatNo,
|
|
pWave->wTimeStamp,
|
|
pWave->Prolog.BodySize - sizeof(SNDWAVE) + sizeof(SNDPROLOG),
|
|
pBody);
|
|
|
|
}
|
|
break;
|
|
|
|
case SNDC_TRAINING:
|
|
{
|
|
SNDTRAINING SndTraining;
|
|
PSNDTRAINING pRecvTraining;
|
|
|
|
if ( pProlog->BodySize < sizeof( *pRecvTraining ) - sizeof( *pProlog ))
|
|
{
|
|
TRC( ERR, _T("DataArrived: Invalid SNDC_TRAINING message\n" ));
|
|
break;
|
|
}
|
|
|
|
//
|
|
// disable dgram response
|
|
//
|
|
m_dwRemoteDGramPort = 0;
|
|
m_ulRemoteDGramAddress = 0;
|
|
|
|
pRecvTraining = (PSNDTRAINING)
|
|
(((LPSTR)pBody) - sizeof(*pProlog));
|
|
|
|
TRC(ALV, _T("DataArrived: training, sending a response\n"));
|
|
SndTraining.Prolog.Type = SNDC_TRAINING;
|
|
SndTraining.Prolog.BodySize = sizeof(SndTraining) -
|
|
sizeof(SndTraining.Prolog);
|
|
SndTraining.wTimeStamp = pRecvTraining->wTimeStamp;
|
|
SndTraining.wPackSize = pRecvTraining->wPackSize;
|
|
|
|
//
|
|
// send the response immediatly
|
|
//
|
|
ChannelWriteNCopy( &SndTraining, sizeof(SndTraining) );
|
|
|
|
}
|
|
break;
|
|
|
|
case SNDC_FORMATS:
|
|
{
|
|
PSNDFORMATMSG pSndFormats;
|
|
PSNDFORMATMSG pSndFormatsResp;
|
|
PSNDFORMATITEM pSndSuppFormats = NULL;
|
|
DWORD dwRespSize;
|
|
DWORD dwListSize;
|
|
DWORD dwNumFormats;
|
|
BOOL bSuccess;
|
|
DWORD dwPacketSize;
|
|
DWORD count;
|
|
PSNDFORMATITEM pFmt;
|
|
|
|
pSndFormats = (PSNDFORMATMSG)
|
|
(((LPSTR)pBody) - sizeof(*pProlog));
|
|
|
|
TRC(ALV, _T("DataArrived: SNDC_FORMATS, number of formats: %d\n"),
|
|
pSndFormats->wNumberOfFormats);
|
|
|
|
if ( pProlog->BodySize < sizeof( *pSndFormats ) - sizeof( *pProlog ))
|
|
{
|
|
TRC( ERR, _T("DataArrived: Invalid SNDC_FORMATS message\n" ));
|
|
break;
|
|
}
|
|
|
|
//
|
|
// validate the packet length
|
|
//
|
|
dwPacketSize = pProlog->BodySize - sizeof( *pSndFormats ) + sizeof( *pProlog );
|
|
pFmt = (PSNDFORMATITEM) (pSndFormats + 1);
|
|
for( count = 0; count < pSndFormats->wNumberOfFormats; count ++ )
|
|
{
|
|
DWORD adv = sizeof( *pFmt ) + pFmt->cbSize;
|
|
if ( adv > dwPacketSize )
|
|
{
|
|
TRC( ERR, _T("DataArrived: Invalid SNDC_FORMATS, invalid format list\n" ));
|
|
goto break_sndformat;
|
|
}
|
|
pFmt = (PSNDFORMATITEM)((LPSTR)pFmt + adv);
|
|
dwPacketSize -= adv;
|
|
}
|
|
ASSERT( 0 == dwPacketSize );
|
|
|
|
|
|
m_cLastReceivedBlock = pSndFormats->cLastBlockConfirmed;
|
|
vcwaveCleanSoundFormats();
|
|
|
|
bSuccess = vcwaveChooseSoundFormat(
|
|
pSndFormats->wNumberOfFormats,
|
|
(PSNDFORMATITEM) (pSndFormats + 1),
|
|
&pSndSuppFormats,
|
|
&dwListSize,
|
|
&dwNumFormats
|
|
);
|
|
|
|
if (bSuccess)
|
|
{
|
|
ASSERT( NULL != pSndSuppFormats );
|
|
ASSERT( 0 != dwListSize );
|
|
ASSERT( 0 != dwNumFormats );
|
|
|
|
bSuccess = vcwaveSaveSoundFormats(
|
|
pSndSuppFormats,
|
|
dwNumFormats);
|
|
if (!bSuccess)
|
|
{
|
|
free(pSndSuppFormats);
|
|
pSndSuppFormats = NULL;
|
|
dwNumFormats = 0;
|
|
dwListSize = 0;
|
|
}
|
|
} else {
|
|
ASSERT( NULL == pSndSuppFormats );
|
|
ASSERT( 0 == dwListSize );
|
|
ASSERT( 0 == dwNumFormats );
|
|
}
|
|
|
|
dwRespSize = sizeof( *pSndFormatsResp ) + dwListSize;
|
|
|
|
__try {
|
|
pSndFormatsResp = (PSNDFORMATMSG)alloca( dwRespSize );
|
|
}
|
|
__except((EXCEPTION_STACK_OVERFLOW == GetExceptionCode()) ?
|
|
EXCEPTION_EXECUTE_HANDLER :
|
|
EXCEPTION_CONTINUE_SEARCH)
|
|
{
|
|
_resetstkoflw();
|
|
pSndFormatsResp = NULL;
|
|
TRC(ERR, _T("alloca threw exception: 0x%x\n"),
|
|
GetExceptionCode());
|
|
}
|
|
if ( NULL == pSndFormatsResp )
|
|
goto break_sndformat;
|
|
|
|
pSndFormatsResp->Prolog.Type = SNDC_FORMATS;
|
|
pSndFormatsResp->Prolog.BodySize = (UINT16)(
|
|
dwRespSize -
|
|
sizeof( pSndFormatsResp->Prolog ));
|
|
pSndFormatsResp->wNumberOfFormats = (WORD)dwNumFormats;
|
|
vcwaveGetDevCaps( pSndFormatsResp );
|
|
|
|
//
|
|
// Beta 1 compatability, fake we are version 1 if the server is version 1
|
|
//
|
|
m_wServerVersion = pSndFormats->wVersion;
|
|
if ( 1 == pSndFormats->wVersion )
|
|
pSndFormatsResp->wVersion = 1;
|
|
else
|
|
pSndFormatsResp->wVersion = RDPSND_PROTOCOL_VERSION;
|
|
|
|
//
|
|
// copy the format list
|
|
//
|
|
memcpy( (PSNDFORMATITEM) ( pSndFormatsResp + 1 ),
|
|
pSndSuppFormats,
|
|
dwListSize );
|
|
|
|
ChannelWriteNCopy( pSndFormatsResp, dwRespSize );
|
|
break_sndformat:
|
|
if ( NULL != pSndSuppFormats )
|
|
free ( pSndSuppFormats );
|
|
}
|
|
break;
|
|
|
|
case SNDC_CRYPTKEY:
|
|
{
|
|
PSNDCRYPTKEY pKey = (PSNDCRYPTKEY)(((LPSTR)pBody) - sizeof(*pProlog));
|
|
|
|
if ( pProlog->BodySize < sizeof( *pKey ) - sizeof( *pProlog ))
|
|
{
|
|
TRC( ERR, _T("DataArrived: Invalid SNDC_CRYPTKEY message\n"));
|
|
break;
|
|
}
|
|
memcpy( m_EncryptKey, pKey->Seed, RANDOM_KEY_LENGTH );
|
|
}
|
|
break;
|
|
|
|
default:
|
|
TRC(ERR, _T("DataArrived: Invalid message type received: %d\n"), pProlog->Type);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Function:
|
|
* vcwaveResample
|
|
*
|
|
* Description:
|
|
* Reopens the device with new codec
|
|
*
|
|
*/
|
|
VOID
|
|
CRDPSound::vcwaveResample(
|
|
VOID
|
|
)
|
|
{
|
|
if ( 0 != m_dwWavesPlaying || NULL == m_pFirstWave )
|
|
goto exitpt;
|
|
|
|
if ( NULL == m_hWave ||
|
|
m_dwCurrentFormat != (DWORD)PtrToLong((PVOID)m_pFirstWave->reserved))
|
|
{
|
|
TRC(INF, _T("vcwaveResample: Resampling\n"));
|
|
m_dwCurrentFormat = (DWORD)PtrToLong((PVOID)m_pFirstWave->reserved);
|
|
|
|
if ( m_dwCurrentFormat >= m_dwNumFormats )
|
|
{
|
|
TRC(ERR, _T("vcwaveResample: invalid format no\n"));
|
|
vcwaveFreeAllWaves();
|
|
|
|
goto exitpt;
|
|
}
|
|
|
|
vcwaveOpen( (LPWAVEFORMATEX)m_ppFormats[ m_dwCurrentFormat ] );
|
|
}
|
|
|
|
//
|
|
// stuff all pending blocks
|
|
//
|
|
while ( NULL != m_pFirstWave &&
|
|
m_dwCurrentFormat == (DWORD)PtrToLong( (PVOID)m_pFirstWave->reserved ))
|
|
{
|
|
LPWAVEHDR lpNext = m_pFirstWave->lpNext;
|
|
MMRESULT mmres;
|
|
|
|
mmres = vcwaveOutWrite( m_pFirstWave );
|
|
if ( MMSYSERR_NOERROR != mmres )
|
|
{
|
|
vcwaveFreeAllWaves();
|
|
} else {
|
|
|
|
m_pFirstWave = lpNext;
|
|
|
|
if ( NULL == m_pFirstWave )
|
|
m_pLastWave = NULL;
|
|
}
|
|
}
|
|
|
|
exitpt:
|
|
;
|
|
}
|
|
|
|
/*
|
|
* Function:
|
|
* DGramSocketMessage
|
|
*
|
|
* Description:
|
|
* Callback from UDP
|
|
*
|
|
*/
|
|
VOID
|
|
CRDPSound::DGramSocketMessage(
|
|
WPARAM wParam,
|
|
LPARAM lParam
|
|
)
|
|
{
|
|
if (WSAGETSELECTERROR(lParam))
|
|
{
|
|
TRC(ERR, _T("WM_WSOCK: Winsock error: %d\n"),
|
|
WSAGETSELECTERROR(lParam));
|
|
goto exitpt;
|
|
}
|
|
|
|
if (m_hDGramSocket != (SOCKET)wParam)
|
|
{
|
|
TRC(WRN, _T("WM_WSOCK: message for unknown socket\n"));
|
|
goto exitpt;
|
|
}
|
|
|
|
if (WSAGETSELECTEVENT(lParam) == FD_CLOSE)
|
|
{
|
|
TRC(ERR, _T("WM_WSOCK: socket closed\n"));
|
|
DGramDone();
|
|
|
|
goto exitpt;
|
|
}
|
|
|
|
if (WSAGETSELECTEVENT(lParam) != FD_READ)
|
|
{
|
|
TRC(WRN, _T("WM_WSOCK: unknown event received\n"));
|
|
|
|
goto exitpt;
|
|
}
|
|
|
|
TRC(ALV, _T("WM_WSOCK: data available\n"));
|
|
{
|
|
BOOL bSuccess;
|
|
PSNDWAVE pSndWave;
|
|
UINT structsize;
|
|
|
|
structsize = sizeof(*pSndWave) + TSSND_BLOCKSIZE + RDPSND_SIGNATURE_SIZE;
|
|
|
|
__try
|
|
{
|
|
pSndWave = (PSNDWAVE)alloca(structsize);
|
|
}
|
|
__except((EXCEPTION_STACK_OVERFLOW == GetExceptionCode()) ?
|
|
EXCEPTION_EXECUTE_HANDLER :
|
|
EXCEPTION_CONTINUE_SEARCH)
|
|
{
|
|
_resetstkoflw();
|
|
pSndWave = NULL;
|
|
TRC(ERR, _T("WM_SOCK: alloca threw exception: 0x%x\n"),
|
|
GetExceptionCode());
|
|
}
|
|
|
|
if (NULL == pSndWave)
|
|
goto exitpt;
|
|
|
|
bSuccess = DGramRecvWave(
|
|
pSndWave,
|
|
structsize
|
|
);
|
|
|
|
if (bSuccess)
|
|
{
|
|
PBYTE pWave = (PBYTE)(pSndWave+1);
|
|
UINT uiWaveSize = pSndWave->Prolog.BodySize -
|
|
sizeof(SNDWAVE) + sizeof(SNDPROLOG);
|
|
|
|
if ( IsDGramWaveSigned( m_wServerVersion ))
|
|
{
|
|
DWORD dw;
|
|
BYTE Signature[ RDPSND_SIGNATURE_SIZE ];
|
|
|
|
//
|
|
// wave starts with signature
|
|
//
|
|
if ( uiWaveSize < RDPSND_SIGNATURE_SIZE )
|
|
{
|
|
TRC( ERR, _T("Insufficient data for signature\n" ));
|
|
goto exitpt;
|
|
}
|
|
|
|
if ( !IsDGramWaveAudioSigned( m_wServerVersion ))
|
|
{
|
|
SL_Signature( Signature, pSndWave->dwBlockNo );
|
|
} else {
|
|
SL_AudioSignature( Signature,
|
|
pSndWave->dwBlockNo,
|
|
pWave + RDPSND_SIGNATURE_SIZE,
|
|
uiWaveSize - RDPSND_SIGNATURE_SIZE );
|
|
}
|
|
dw = memcmp( pWave, Signature, RDPSND_SIGNATURE_SIZE );
|
|
if ( 0 != dw )
|
|
{
|
|
TRC( ERR, _T("Invalid signature\n" ));
|
|
goto exitpt;
|
|
}
|
|
pWave += RDPSND_SIGNATURE_SIZE;
|
|
uiWaveSize -= RDPSND_SIGNATURE_SIZE;
|
|
}
|
|
vcwaveWrite(pSndWave->cBlockNo,
|
|
pSndWave->wFormatNo,
|
|
pSndWave->wTimeStamp,
|
|
uiWaveSize,
|
|
pWave);
|
|
}
|
|
}
|
|
|
|
exitpt:
|
|
;
|
|
}
|
|
|
|
/*
|
|
* Function:
|
|
* CreateMsgWindow
|
|
*
|
|
* Description:
|
|
* Creates a window
|
|
*
|
|
*/
|
|
BOOL
|
|
CRDPSound::CreateMsgWindow(
|
|
HINSTANCE hInstance
|
|
)
|
|
{
|
|
WNDCLASS wc;
|
|
BOOL rv = FALSE;
|
|
DWORD dwLastErr;
|
|
LONG_PTR dwUser;
|
|
|
|
memset(&wc, 0, sizeof(wc));
|
|
|
|
wc.lpfnWndProc = MsgWndProc;
|
|
wc.hInstance = hInstance;
|
|
wc.lpszClassName = _NAMEOFCLAS;
|
|
|
|
if (!RegisterClass (&wc))
|
|
{
|
|
dwLastErr = GetLastError();
|
|
if(dwLastErr != ERROR_CLASS_ALREADY_EXISTS)
|
|
{
|
|
TRC(ERR,
|
|
_T("CreateMsgWindow: Can't register class. GetLastError=%d\n"),
|
|
GetLastError());
|
|
goto exitpt;
|
|
}
|
|
}
|
|
|
|
|
|
m_hMsgWindow = CreateWindow(
|
|
_NAMEOFCLAS,
|
|
NULL, // Window name
|
|
0, // dwStyle
|
|
0, // x
|
|
0, // y
|
|
0, // nWidth
|
|
0, // nHeight
|
|
NULL, // hWndParent
|
|
NULL, // hMenu
|
|
hInstance,
|
|
NULL); // lpParam
|
|
|
|
if (!m_hMsgWindow)
|
|
{
|
|
TRC(ERR, _T("CreateMsgWindow: Failed to create message window. GetLastError=%d\n"),
|
|
GetLastError());
|
|
goto exitpt;
|
|
}
|
|
|
|
//
|
|
// safe the class pointer in the window structure
|
|
//
|
|
#ifndef OS_WINCE
|
|
dwUser = SetWindowLongPtr(
|
|
m_hMsgWindow,
|
|
GWLP_USERDATA,
|
|
(LONG_PTR)this
|
|
);
|
|
#else
|
|
dwUser = SetWindowLong(
|
|
m_hMsgWindow,
|
|
GWL_USERDATA,
|
|
(LONG)this
|
|
);
|
|
#endif
|
|
|
|
ASSERT( 0 == dwUser );
|
|
|
|
rv = TRUE;
|
|
exitpt:
|
|
return rv;
|
|
}
|
|
|
|
/*
|
|
* Function:
|
|
* DestroyMsgWindow
|
|
*
|
|
* Description:
|
|
* Destroy our window
|
|
*
|
|
*/
|
|
VOID
|
|
CRDPSound::DestroyMsgWindow(
|
|
VOID
|
|
)
|
|
{
|
|
if (NULL != m_hMsgWindow)
|
|
DestroyWindow(m_hMsgWindow);
|
|
|
|
UnregisterClass(_NAMEOFCLAS, m_hInst);
|
|
}
|
|
|
|
|
|
/*
|
|
* Function:
|
|
* ChannelWrite
|
|
*
|
|
* Description:
|
|
* Sends or queues a chunk of data to the virtual channel
|
|
*
|
|
* Parameters:
|
|
* hGlobMem - handle to a HGLOBAL
|
|
* uiBlockSize - size of the chunk
|
|
*
|
|
* Returns:
|
|
* TRUE on success
|
|
*
|
|
*/
|
|
BOOL
|
|
CRDPSound::ChannelWrite(
|
|
LPVOID pData,
|
|
UINT32 uiBlockSize
|
|
)
|
|
{
|
|
BOOL rv = FALSE;
|
|
DWORD Handle = m_dwChannel;
|
|
UINT rc;
|
|
|
|
ASSERT(Handle != INVALID_CHANNELID);
|
|
ASSERT(m_ChannelEntries.pVirtualChannelWriteEx);
|
|
|
|
// parameters check
|
|
//
|
|
if (INVALID_CHANNELID == Handle)
|
|
{
|
|
TRC(ERR, _T("ChannelWrite: invalid handle\n"));
|
|
goto exitpt;
|
|
}
|
|
|
|
TRC(ALV, _T("Sending ptr=%p, Size=%d\n"), pData, uiBlockSize);
|
|
|
|
rc = m_ChannelEntries.pVirtualChannelWriteEx(
|
|
m_pInitHandle,
|
|
Handle,
|
|
pData,
|
|
uiBlockSize,
|
|
pData);
|
|
|
|
if (rc != CHANNEL_RC_OK)
|
|
{
|
|
TRC(INF, _T("VirtualChannelWrite failed rv=%d"), rc);
|
|
goto exitpt;
|
|
}
|
|
|
|
rv = TRUE;
|
|
|
|
exitpt:
|
|
|
|
if (!rv)
|
|
{
|
|
TRC(ERR, _T("ChannelWrite: Failed to send data\n"));
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
/*
|
|
* Function:
|
|
* vcwaveCallbacl
|
|
*
|
|
* Description:
|
|
* WaveOut callbacks
|
|
*
|
|
*/
|
|
VOID
|
|
CRDPSound::vcwaveCallback(
|
|
HWAVEOUT hWave,
|
|
UINT uMsg,
|
|
LPWAVEHDR lpWaveHdr
|
|
)
|
|
{
|
|
switch (uMsg)
|
|
{
|
|
case WOM_OPEN:
|
|
TRC(ALV, _T("vcwaveCallback: WOM_OPEN\n"));
|
|
break;
|
|
case WOM_CLOSE:
|
|
TRC(ALV, _T("vcwaveCallback: WOM_CLOSE\n"));
|
|
break;
|
|
|
|
case WOM_DONE:
|
|
{
|
|
SNDWAVECONFIRM WaveConfirm;
|
|
|
|
ASSERT( 0 != ( lpWaveHdr->dwFlags & WHDR_PREPARED ));
|
|
|
|
if ( 0 == ( lpWaveHdr->dwFlags & WHDR_PREPARED ))
|
|
{
|
|
TRC( ERR, _T("vcwaveCallback: buffer already unprepared\n") );
|
|
} else if ( m_hWave != hWave )
|
|
{
|
|
TRC( ERR, _T("vcwaveCallback: can't unprepare header, beacuse ")
|
|
_T("the stream is already closed\n"));
|
|
} else {
|
|
MMRESULT mmres;
|
|
|
|
mmres = waveOutUnprepareHeader(
|
|
hWave,
|
|
lpWaveHdr,
|
|
sizeof(*lpWaveHdr)
|
|
);
|
|
|
|
ASSERT( mmres == MMSYSERR_NOERROR );
|
|
#if DBG
|
|
m_lPrepdBlocks --;
|
|
#endif // DBG
|
|
|
|
}
|
|
|
|
if (lpWaveHdr)
|
|
{
|
|
DWORD dw = PtrToLong((PVOID)lpWaveHdr->dwUser);
|
|
|
|
WaveConfirm.cConfirmedBlockNo = (BYTE)(dw >> 16);
|
|
//
|
|
// adjust completly the time stamp
|
|
// see vcwaveWrite
|
|
//
|
|
WaveConfirm.wTimeStamp = (WORD)
|
|
((dw & 0xffff) + GetTickCount());
|
|
#if _STAT_DBG
|
|
TRC(INF, _T("blockno=%d, adjusted time stamp=%x\n"),
|
|
WaveConfirm.cConfirmedBlockNo, WaveConfirm.wTimeStamp);
|
|
#endif
|
|
|
|
TRC(ALV, _T("vcwaveCallback: WOM_DONE, block no %d\n"),
|
|
WaveConfirm.cConfirmedBlockNo);
|
|
|
|
if (lpWaveHdr->lpData)
|
|
{
|
|
#ifdef OS_WINCE
|
|
lpWaveHdr->lpData = (char *)UnMapPtr(lpWaveHdr->lpData);
|
|
if (lpWaveHdr->lpData)
|
|
#endif
|
|
free(lpWaveHdr->lpData);
|
|
}
|
|
else
|
|
TRC(ERR, _T("vcwaveCallback: WOM_DONE: lpWaveHdr->lpData is NULL\n"));
|
|
|
|
free(lpWaveHdr);
|
|
} else
|
|
TRC(ERR, _T("vcwaveCallback: WOM_DONE: lpWaveHdr is NULL\n"));
|
|
|
|
// Send the confirmation
|
|
//
|
|
WaveConfirm.Prolog.Type = SNDC_WAVECONFIRM;
|
|
WaveConfirm.Prolog.BodySize = sizeof(WaveConfirm) -
|
|
sizeof(WaveConfirm.Prolog);
|
|
|
|
if ( 0 != m_ulRemoteDGramAddress )
|
|
DGramSend( &WaveConfirm, sizeof(WaveConfirm));
|
|
else
|
|
ChannelWriteNCopy( &WaveConfirm, sizeof( WaveConfirm ));
|
|
|
|
m_dwWavesPlaying --;
|
|
ASSERT( -1 != m_dwWavesPlaying );
|
|
|
|
if ( 0 == m_dwWavesPlaying && NULL != m_pFirstWave )
|
|
{
|
|
TRC(INF, _T("vcwaveCallback: WOM_DONE: attempt to resample\n"));
|
|
PostMessage( m_hMsgWindow, WM_RESAMPLE, 0, 0 );
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Function:
|
|
* vcwaveGetDevCaps
|
|
*
|
|
* Description:
|
|
* Queries for device capabilities
|
|
*
|
|
*/
|
|
VOID
|
|
CRDPSound::vcwaveGetDevCaps(
|
|
PSNDFORMATMSG pFmtMsg
|
|
)
|
|
{
|
|
WAVEFORMATEX Format;
|
|
MMRESULT mmres;
|
|
DWORD dw;
|
|
|
|
ASSERT( NULL != pFmtMsg );
|
|
|
|
pFmtMsg->dwFlags = 0;
|
|
pFmtMsg->dwVolume = 0;
|
|
pFmtMsg->dwPitch = 0;
|
|
pFmtMsg->wDGramPort = 0;
|
|
|
|
Format.wFormatTag = WAVE_FORMAT_PCM;
|
|
Format.nChannels = TSSND_NATIVE_CHANNELS;
|
|
Format.nSamplesPerSec = TSSND_NATIVE_SAMPLERATE;
|
|
Format.nAvgBytesPerSec = TSSND_NATIVE_AVGBYTESPERSEC;
|
|
Format.nBlockAlign = TSSND_NATIVE_BLOCKALIGN;
|
|
Format.wBitsPerSample = TSSND_NATIVE_BITSPERSAMPLE;
|
|
Format.cbSize = 0;
|
|
|
|
if (!vcwaveOpen(&Format))
|
|
{
|
|
TRC(ERR, _T("vcwaveGetDevCaps: can't open device\n"));
|
|
goto exitpt;
|
|
}
|
|
ASSERT( NULL != m_hWave );
|
|
|
|
pFmtMsg->dwFlags = TSSNDCAPS_ALIVE;
|
|
pFmtMsg->wDGramPort = DGramGetLocalPort();
|
|
|
|
mmres = waveOutGetVolume(m_hWave, &dw);
|
|
if (MMSYSERR_NOERROR == mmres)
|
|
{
|
|
pFmtMsg->dwFlags |= TSSNDCAPS_VOLUME;
|
|
pFmtMsg->dwVolume = dw;
|
|
}
|
|
else
|
|
TRC(ERR, _T("vcwaveGetDevCaps: device doesn't support volume control\n"));
|
|
|
|
mmres = waveOutGetPitch(m_hWave, &dw);
|
|
if (MMSYSERR_NOERROR == mmres)
|
|
{
|
|
pFmtMsg->dwFlags |= TSSNDCAPS_PITCH;
|
|
pFmtMsg->dwPitch = dw;
|
|
}
|
|
else
|
|
TRC(INF, _T("vcwaveGetDevCaps: device doesn't support pitch control\n"));
|
|
|
|
vcwaveClose();
|
|
|
|
exitpt:
|
|
;
|
|
}
|
|
|
|
/*
|
|
* Function:
|
|
* vcwaveChooseSoundFormat
|
|
*
|
|
* Description:
|
|
* Queries the local device for different formats
|
|
*
|
|
*/
|
|
BOOL
|
|
CRDPSound::vcwaveChooseSoundFormat(
|
|
DWORD dwNumberOfFormats,
|
|
PSNDFORMATITEM pSndFormats,
|
|
PSNDFORMATITEM *ppSndFormatFound,
|
|
DWORD *pdwListSize,
|
|
DWORD *pdwNumFormats
|
|
)
|
|
{
|
|
PSNDFORMATITEM pSndFormat;
|
|
|
|
//
|
|
// query the waveout device with different wave formats
|
|
// returns the list of successfull formats in pSndFormatFound
|
|
// FALSE in any other case
|
|
// the caller takes responsibility for freeing the data in
|
|
// *ppSndFormatFound
|
|
//
|
|
BOOL rv = FALSE;
|
|
DWORD i;
|
|
PSNDFORMATITEM pSndFormatFound = NULL;
|
|
LPSTR pFmtCopy;
|
|
DWORD dwListSize = 0;
|
|
DWORD dwNumFormats = 0;
|
|
|
|
ASSERT( NULL != pSndFormats );
|
|
ASSERT( NULL != ppSndFormatFound );
|
|
ASSERT( NULL != pdwListSize );
|
|
ASSERT( NULL != pdwNumFormats );
|
|
|
|
for ( i = 0, pSndFormat = pSndFormats;
|
|
i < dwNumberOfFormats;
|
|
i++, pSndFormat = (PSNDFORMATITEM)
|
|
(((LPSTR)pSndFormat) + sizeof( *pSndFormat ) + pSndFormat->cbSize)
|
|
)
|
|
{
|
|
MMRESULT mmres;
|
|
PSNDFORMATITEM pFmtToOpen = pSndFormat;
|
|
PSNDFORMATITEM pFixedFormat = NULL;
|
|
|
|
//
|
|
// fix the format for WMA audio
|
|
// copy and modify it in another structure, so we don't disrupt
|
|
// the original
|
|
//
|
|
if ( WAVE_FORMAT_WMAUDIO2 == pSndFormat->wFormatTag )
|
|
{
|
|
DWORD dwTotalSize = sizeof( WMAUDIO2WAVEFORMAT ) -
|
|
sizeof( WAVEFORMATEX ) + sizeof( WMAUDIO_DEC_KEY );
|
|
|
|
ASSERT( pSndFormat->cbSize == dwTotalSize );
|
|
if ( pSndFormat->cbSize == dwTotalSize )
|
|
{
|
|
pFixedFormat = (PSNDFORMATITEM)malloc( sizeof( WMAUDIO2WAVEFORMAT ) +
|
|
sizeof( WMAUDIO_DEC_KEY ));
|
|
if ( NULL != pFixedFormat )
|
|
{
|
|
memcpy( pFixedFormat, pSndFormat, sizeof( pSndFormat ) + pSndFormat->cbSize );
|
|
strncpy((CHAR *)(((WMAUDIO2WAVEFORMAT *) pFixedFormat) + 1),
|
|
WMAUDIO_DEC_KEY, sizeof( WMAUDIO_DEC_KEY ));
|
|
pFmtToOpen = pFixedFormat;
|
|
}
|
|
}
|
|
}
|
|
|
|
mmres = waveOutOpen(
|
|
NULL,
|
|
WAVE_MAPPER,
|
|
(LPWAVEFORMATEX)pFmtToOpen,
|
|
0,
|
|
0,
|
|
WAVE_FORMAT_QUERY
|
|
);
|
|
|
|
if ( NULL != pFixedFormat )
|
|
{
|
|
free( pFixedFormat );
|
|
}
|
|
|
|
TRC(ALV, _T("FormatTag - %d\n"), pSndFormat->wFormatTag);
|
|
TRC(ALV, _T("Channels - %d\n"), pSndFormat->nChannels);
|
|
TRC(ALV, _T("SamplesPerSec - %d\n"), pSndFormat->nSamplesPerSec);
|
|
TRC(ALV, _T("AvgBytesPerSec - %d\n"), pSndFormat->nAvgBytesPerSec);
|
|
TRC(ALV, _T("BlockAlign - %d\n"), pSndFormat->nBlockAlign);
|
|
TRC(ALV, _T("BitsPerSample - %d\n"), pSndFormat->wBitsPerSample);
|
|
TRC(ALV, _T("cbSize - %d\n"), pSndFormat->cbSize);
|
|
|
|
if ( MMSYSERR_NOERROR == mmres )
|
|
{
|
|
//
|
|
// this format is supported
|
|
//
|
|
|
|
TRC(ALV, _T("vcwaveChooseSoundFormat: format found\n"));
|
|
|
|
dwListSize += sizeof( *pSndFormat ) + pSndFormat->cbSize;
|
|
dwNumFormats ++;
|
|
} else {
|
|
//
|
|
// if not supported,
|
|
// zero it's AvgBytesPerSec member
|
|
//
|
|
TRC(INF, _T("vcwaveChooseSoundFormat: format not supported\n"));
|
|
pSndFormat->nAvgBytesPerSec = 0;
|
|
}
|
|
|
|
}
|
|
|
|
if ( 0 == dwListSize )
|
|
{
|
|
TRC(WRN, _T("vcwaveChooseSoundFormat: no formats found\n"));
|
|
goto exitpt;
|
|
}
|
|
|
|
pSndFormatFound = (PSNDFORMATITEM)malloc( dwListSize );
|
|
if ( NULL == pSndFormatFound )
|
|
{
|
|
TRC( ERR, _T("vcwaveChooseSoundFormat: can't allocate %d bytes\n"),
|
|
dwListSize );
|
|
|
|
dwListSize = 0;
|
|
dwNumFormats = 0;
|
|
goto exitpt;
|
|
}
|
|
|
|
//
|
|
// copy the list of supported formats
|
|
//
|
|
for ( i = 0, pSndFormat = pSndFormats, pFmtCopy = (LPSTR)pSndFormatFound;
|
|
i < dwNumberOfFormats;
|
|
i++, pSndFormat = (PSNDFORMATITEM)
|
|
(((LPSTR)pSndFormat) + sizeof( *pSndFormat ) + pSndFormat->cbSize)
|
|
)
|
|
{
|
|
if ( 0 != pSndFormat->nAvgBytesPerSec )
|
|
{
|
|
memcpy( pFmtCopy,
|
|
pSndFormat,
|
|
sizeof( *pSndFormat ) + pSndFormat->cbSize);
|
|
pFmtCopy += sizeof( *pSndFormat ) + pSndFormat->cbSize;
|
|
}
|
|
}
|
|
|
|
rv = TRUE;
|
|
|
|
exitpt:
|
|
*ppSndFormatFound = pSndFormatFound;
|
|
*pdwListSize = dwListSize;
|
|
*pdwNumFormats = dwNumFormats;
|
|
|
|
return rv;
|
|
}
|
|
|
|
/*
|
|
* Function:
|
|
* vcwaveCleanSoundForamt
|
|
*
|
|
* Description:
|
|
* Cleans the list of negotiated formats
|
|
*
|
|
*/
|
|
VOID
|
|
CRDPSound::vcwaveCleanSoundFormats(
|
|
VOID
|
|
)
|
|
{
|
|
DWORD i;
|
|
|
|
if ( NULL == m_ppFormats )
|
|
goto exitpt;
|
|
//
|
|
// dispose the allocated structures
|
|
//
|
|
for (i = 0; i < m_dwNumFormats; i++)
|
|
{
|
|
if ( NULL != m_ppFormats[i] )
|
|
free( m_ppFormats[i] );
|
|
}
|
|
free( m_ppFormats );
|
|
m_ppFormats = NULL;
|
|
m_dwNumFormats = 0;
|
|
exitpt:
|
|
;
|
|
}
|
|
|
|
/*
|
|
* Function:
|
|
* vcwaveSafeSoundFormats
|
|
*
|
|
* Description:
|
|
* Saves the list of negotiated formats
|
|
*
|
|
*/
|
|
BOOL
|
|
CRDPSound::vcwaveSaveSoundFormats(
|
|
PSNDFORMATITEM pSndFormats,
|
|
DWORD dwNumFormats
|
|
)
|
|
{
|
|
BOOL rv = FALSE;
|
|
DWORD i;
|
|
DWORD dwAllocSize;
|
|
LPSTR p;
|
|
|
|
ASSERT( NULL != pSndFormats );
|
|
ASSERT( 0 != dwNumFormats );
|
|
|
|
if ( NULL != m_ppFormats )
|
|
{
|
|
vcwaveCleanSoundFormats();
|
|
}
|
|
|
|
ASSERT( NULL == m_ppFormats );
|
|
ASSERT( 0 == m_dwNumFormats );
|
|
|
|
dwAllocSize = sizeof( PSNDFORMATITEM ) * dwNumFormats;
|
|
m_ppFormats = (PSNDFORMATITEM *)malloc( dwAllocSize );
|
|
if ( NULL == m_ppFormats )
|
|
{
|
|
TRC(ERR, _T("Failed to allocate %d bytes\n"),
|
|
dwAllocSize );
|
|
goto exitpt;
|
|
}
|
|
|
|
memset( m_ppFormats, 0, dwAllocSize );
|
|
|
|
for (i = 0, p = (LPSTR)pSndFormats;
|
|
i < dwNumFormats;
|
|
i++, p += sizeof(SNDFORMATITEM) + ((PSNDFORMATITEM)p)->cbSize)
|
|
{
|
|
PSNDFORMATITEM pFmt;
|
|
|
|
pFmt = (PSNDFORMATITEM) p;
|
|
dwAllocSize = sizeof(SNDFORMATITEM) + pFmt->cbSize;
|
|
m_ppFormats[i] = (PSNDFORMATITEM)malloc( dwAllocSize );
|
|
|
|
if ( NULL == (PVOID)m_ppFormats[i] )
|
|
{
|
|
TRC(ERR, _T("Failed to allocate %d bytes\n"),
|
|
dwAllocSize );
|
|
goto exitpt;
|
|
}
|
|
|
|
//
|
|
// copy the format
|
|
//
|
|
memcpy( m_ppFormats[i], pFmt, dwAllocSize );
|
|
}
|
|
|
|
m_dwNumFormats = dwNumFormats;
|
|
|
|
rv = TRUE;
|
|
|
|
exitpt:
|
|
|
|
if (!rv && NULL != m_ppFormats)
|
|
{
|
|
// dispose the allocated structures
|
|
//
|
|
for (i = 0; i < dwNumFormats; i++)
|
|
{
|
|
if ( NULL != m_ppFormats[i] )
|
|
free( m_ppFormats[i] );
|
|
}
|
|
free( m_ppFormats );
|
|
m_ppFormats = NULL;
|
|
m_dwNumFormats = 0;
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
/*
|
|
* Function:
|
|
* vcwaveClose
|
|
*
|
|
* Description:
|
|
* Closes the local device
|
|
*
|
|
*/
|
|
VOID
|
|
CRDPSound::vcwaveClose(
|
|
VOID
|
|
)
|
|
{
|
|
MSG msg;
|
|
UINT_PTR idTimer;
|
|
|
|
if ( NULL == m_hWave )
|
|
goto exitpt;
|
|
|
|
//
|
|
// process all MM_WOM_DONE messages
|
|
//
|
|
ASSERT( NULL != m_hMsgWindow );
|
|
|
|
//
|
|
// start a timeout timer
|
|
//
|
|
idTimer = SetTimer( m_hMsgWindow, 1, WAVE_CLOSE_TIMEOUT, NULL );
|
|
|
|
while( 0 != m_dwWavesPlaying &&
|
|
GetMessage( &msg, m_hMsgWindow, 0, 0 ))
|
|
{
|
|
if ( WM_TIMER == msg.message && msg.wParam == idTimer )
|
|
{
|
|
//
|
|
// cancel outstanding io
|
|
//
|
|
TRC( WRN, _T("TIMEDOUT waiting for the playing to complete. Resetting\n" ));
|
|
waveOutReset( m_hWave );
|
|
}
|
|
DispatchMessage(&msg);
|
|
}
|
|
|
|
if ( 0 != idTimer )
|
|
{
|
|
KillTimer( m_hMsgWindow, idTimer );
|
|
}
|
|
|
|
waveOutClose(m_hWave);
|
|
|
|
#ifdef DBG
|
|
if ( 0 != InterlockedDecrement(&m_lTimesWaveOutOpened))
|
|
{
|
|
TRC(FATAL, _T("Device was closed too many times\n"));
|
|
ASSERT(0);
|
|
}
|
|
|
|
ASSERT( 0 == m_dwWavesPlaying );
|
|
ASSERT( 0 == m_lPrepdBlocks );
|
|
#endif
|
|
m_hWave = NULL;
|
|
|
|
exitpt:
|
|
;
|
|
}
|
|
|
|
/*
|
|
* Function:
|
|
* vcwaveOpen
|
|
*
|
|
* Description:
|
|
* Opens the device in given format
|
|
*
|
|
*/
|
|
BOOL
|
|
CRDPSound::vcwaveOpen(
|
|
LPWAVEFORMATEX pFormat
|
|
)
|
|
{
|
|
BOOL rv = FALSE;
|
|
MMRESULT mmres;
|
|
|
|
|
|
if (m_hWave)
|
|
{
|
|
TRC(WRN, _T("vcwaveOpen: device is already opened. Reopening\n"));
|
|
|
|
waveOutReset(m_hWave);
|
|
vcwaveClose();
|
|
}
|
|
|
|
if (INVALID_SOCKET == m_hDGramSocket)
|
|
{
|
|
TRC(ERR, _T("vcwaveOpen: no datagram connection, falling to VC\n"));
|
|
}
|
|
|
|
// 11.025 kHz, 8 bit, mono
|
|
|
|
if (NULL == m_hMsgWindow && !CreateMsgWindow(m_hInst))
|
|
goto exitpt;
|
|
|
|
//
|
|
// fix the format for WMA audio
|
|
//
|
|
if ( WAVE_FORMAT_WMAUDIO2 == pFormat->wFormatTag )
|
|
{
|
|
DWORD dwTotalSize = sizeof( WMAUDIO2WAVEFORMAT ) -
|
|
sizeof( WAVEFORMATEX ) + sizeof( WMAUDIO_DEC_KEY );
|
|
|
|
ASSERT( pFormat->cbSize == dwTotalSize );
|
|
if ( pFormat->cbSize == dwTotalSize )
|
|
{
|
|
strncpy((CHAR *)(((WMAUDIO2WAVEFORMAT *) pFormat) + 1),
|
|
WMAUDIO_DEC_KEY, sizeof( WMAUDIO_DEC_KEY ));
|
|
}
|
|
}
|
|
mmres = waveOutOpen(
|
|
&m_hWave,
|
|
WAVE_MAPPER,
|
|
pFormat,
|
|
(DWORD_PTR)m_hMsgWindow,
|
|
0, // CallbackInstance
|
|
CALLBACK_WINDOW
|
|
);
|
|
|
|
if (MMSYSERR_NOERROR != mmres)
|
|
{
|
|
TRC(WRN, _T("vcwaveOpen: failed to open WaveOut device: MMRESULT=%d\n"),
|
|
mmres);
|
|
goto exitpt;
|
|
}
|
|
|
|
#ifdef OS_WINCE
|
|
TRC(ALV, _T("waveOutOpen succeeded with following format %d\n"), pFormat->wFormatTag);
|
|
TRC(ALV, _T("FormatTag - %d\n"), pFormat->wFormatTag);
|
|
TRC(ALV, _T("Channels - %d\n"), pFormat->nChannels);
|
|
TRC(ALV, _T("SamplesPerSec - %d\n"), pFormat->nSamplesPerSec);
|
|
TRC(ALV, _T("AvgBytesPerSec - %d\n"), pFormat->nAvgBytesPerSec);
|
|
TRC(ALV, _T("BlockAlign - %d\n"), pFormat->nBlockAlign);
|
|
TRC(ALV, _T("BitsPerSample - %d\n"), pFormat->wBitsPerSample);
|
|
TRC(ALV, _T("cbSize - %d\n"), pFormat->cbSize);
|
|
#endif
|
|
|
|
#ifdef DBG
|
|
// Win95 InterlockedIncrement() difference.
|
|
InterlockedIncrement(&m_lTimesWaveOutOpened);
|
|
|
|
if ( 1 != m_lTimesWaveOutOpened )
|
|
{
|
|
TRC(FATAL, _T("Device was opened %d times\n"),
|
|
m_lTimesWaveOutOpened);
|
|
ASSERT(0);
|
|
}
|
|
#endif
|
|
|
|
rv = TRUE;
|
|
|
|
exitpt:
|
|
|
|
return rv;
|
|
}
|
|
|
|
/*
|
|
* Function:
|
|
* vcwaveFreeAllWaves
|
|
*
|
|
* Description:
|
|
* Frees all queued data
|
|
*
|
|
*/
|
|
VOID
|
|
CRDPSound::vcwaveFreeAllWaves(
|
|
VOID
|
|
)
|
|
{
|
|
while ( NULL != m_pFirstWave )
|
|
{
|
|
LPWAVEHDR lpNext = m_pFirstWave->lpNext;
|
|
|
|
free( m_pFirstWave->lpData );
|
|
free( m_pFirstWave );
|
|
|
|
m_pFirstWave = lpNext;
|
|
}
|
|
|
|
m_pFirstWave = m_pLastWave = NULL;
|
|
}
|
|
|
|
/*
|
|
* Function:
|
|
* vcwaveOutWrite
|
|
*
|
|
* Description:
|
|
* Writes a data to the output sound device
|
|
*
|
|
*/
|
|
MMRESULT
|
|
CRDPSound::vcwaveOutWrite(
|
|
LPWAVEHDR lpWaveHdr
|
|
)
|
|
{
|
|
MMRESULT mmres;
|
|
|
|
mmres = waveOutPrepareHeader(
|
|
m_hWave,
|
|
lpWaveHdr,
|
|
sizeof(*lpWaveHdr)
|
|
);
|
|
|
|
if (MMSYSERR_NOERROR != mmres)
|
|
{
|
|
TRC(WRN, _T("vcwaveOpen: failed to prepare buffer: MMRESULT=%d\n"),
|
|
mmres);
|
|
goto exitpt;
|
|
}
|
|
|
|
m_dwWavesPlaying++;
|
|
#if DBG
|
|
m_lPrepdBlocks ++;
|
|
#endif
|
|
|
|
mmres = waveOutWrite(
|
|
m_hWave,
|
|
lpWaveHdr,
|
|
sizeof(*lpWaveHdr)
|
|
);
|
|
|
|
if (MMSYSERR_NOERROR != mmres)
|
|
{
|
|
TRC(WRN, _T("vcwaveOpen: failed to write in WaveOut device: MMRESULT=%d\n"),
|
|
mmres);
|
|
|
|
waveOutUnprepareHeader(
|
|
m_hWave,
|
|
lpWaveHdr,
|
|
sizeof(*lpWaveHdr)
|
|
);
|
|
|
|
m_dwWavesPlaying --;
|
|
#if DBG
|
|
m_lPrepdBlocks --;
|
|
#endif
|
|
|
|
goto exitpt;
|
|
}
|
|
|
|
exitpt:
|
|
return mmres;
|
|
}
|
|
|
|
/*
|
|
* Function:
|
|
* vcwaveWrite
|
|
*
|
|
* Description:
|
|
* If the format id is not changes calls vcwaveOutWrite
|
|
* otherwise queues the data for later
|
|
*
|
|
*/
|
|
BOOL
|
|
CRDPSound::vcwaveWrite(
|
|
BYTE cBlockNo,
|
|
DWORD dwFormatNo,
|
|
DWORD dwTimeStamp,
|
|
DWORD dwWaveDataLen,
|
|
LPVOID pData
|
|
)
|
|
{
|
|
BOOL rv = FALSE;
|
|
LPVOID lpWaveData = NULL;
|
|
LPWAVEHDR lpWaveHdr = NULL;
|
|
MMRESULT mmres;
|
|
BOOL bDontPlay = FALSE;
|
|
|
|
//
|
|
// put the stamp here to remove the delay of opening the device
|
|
//
|
|
DWORD dwStartStamp = GetTickCount() & 0xffff;
|
|
|
|
if ( NULL != m_hWave &&
|
|
m_dwCurrentFormat != dwFormatNo )
|
|
{
|
|
bDontPlay = TRUE;
|
|
}
|
|
|
|
if (NULL == m_hWave)
|
|
{
|
|
TRC(ALV, _T("wcwaveWrite: attempting to open the device\n"));
|
|
if (dwFormatNo >= m_dwNumFormats)
|
|
{
|
|
TRC(FATAL, _T("Invalid format no: %d\n"),
|
|
dwFormatNo);
|
|
goto exitpt;
|
|
}
|
|
if ( NULL == m_ppFormats )
|
|
{
|
|
TRC(FATAL, _T("No formats available(NULL)\n"));
|
|
goto exitpt;
|
|
}
|
|
if ( !vcwaveOpen( (LPWAVEFORMATEX)m_ppFormats[dwFormatNo] ))
|
|
TRC(ERR, _T("Can't open the device\n"));
|
|
|
|
m_dwCurrentFormat = dwFormatNo;
|
|
}
|
|
|
|
if ((BYTE)( m_cLastReceivedBlock - cBlockNo ) < TSSND_BLOCKSONTHENET)
|
|
{
|
|
TRC(WRN, _T("wcwaveWrite: received old block, ")
|
|
_T("the last one is %d, this one is %d. Discarding\n"),
|
|
m_cLastReceivedBlock,
|
|
cBlockNo);
|
|
goto exitpt;
|
|
} else
|
|
m_cLastReceivedBlock = cBlockNo;
|
|
|
|
lpWaveData = malloc(dwWaveDataLen);
|
|
if (!lpWaveData)
|
|
{
|
|
TRC(ERR, _T("vcwaveWrite: malloc failed to allocate %d bytes\n"),
|
|
dwWaveDataLen);
|
|
goto exitpt;
|
|
}
|
|
|
|
lpWaveHdr = (LPWAVEHDR)malloc(sizeof(*lpWaveHdr));
|
|
if (!lpWaveHdr)
|
|
{
|
|
TRC(ERR, _T("vcwaveWrite: malloc failed for %d bytes\n"),
|
|
sizeof(*lpWaveHdr));
|
|
goto exitpt;
|
|
}
|
|
|
|
memset(lpWaveHdr, 0, sizeof(*lpWaveHdr));
|
|
|
|
memcpy(lpWaveData, pData, dwWaveDataLen);
|
|
|
|
lpWaveHdr->lpData = (LPSTR)lpWaveData;
|
|
lpWaveHdr->dwBufferLength = dwWaveDataLen;
|
|
lpWaveHdr->dwFlags = 0;
|
|
lpWaveHdr->dwLoops = 0;
|
|
|
|
//
|
|
// here, we'll do a little magic with the time stamp
|
|
// in order to exclude the time when time packet is in the wave queue
|
|
// we'll subtract the current time now and add the time when
|
|
// confirmation is to be send
|
|
//
|
|
#if _STAT_DBG
|
|
TRC(INF, _T("blockno=%d, time stamp=%x\n"),
|
|
cBlockNo, dwTimeStamp );
|
|
#endif
|
|
|
|
lpWaveHdr->dwUser = (cBlockNo << 16) +
|
|
((dwTimeStamp - dwStartStamp ) & 0xffff);
|
|
|
|
if ( bDontPlay )
|
|
{
|
|
//
|
|
// format change
|
|
// add this wave to the list
|
|
//
|
|
lpWaveHdr->reserved = dwFormatNo;
|
|
lpWaveHdr->lpNext = NULL;
|
|
|
|
if ( NULL != m_pLastWave )
|
|
m_pLastWave->lpNext = lpWaveHdr;
|
|
else
|
|
m_pFirstWave = lpWaveHdr;
|
|
|
|
m_pLastWave = lpWaveHdr;
|
|
|
|
ASSERT( NULL != lpWaveHdr->lpData );
|
|
|
|
TRC(INF, _T("vcwaveWrite: posting WM_RESAMPLE\n"));
|
|
PostMessage( m_hMsgWindow, WM_RESAMPLE, 0, 0 );
|
|
|
|
goto leavept;
|
|
}
|
|
|
|
mmres = vcwaveOutWrite( lpWaveHdr );
|
|
|
|
if (MMSYSERR_NOERROR != mmres)
|
|
{
|
|
TRC(WRN, _T("vcwaveOpen: failed to play a buffer: MMRESULT=%d\n"),
|
|
mmres);
|
|
goto exitpt;
|
|
}
|
|
|
|
leavept:
|
|
|
|
rv = TRUE;
|
|
|
|
exitpt:
|
|
|
|
if (!rv)
|
|
{
|
|
if (lpWaveData)
|
|
free(lpWaveData);
|
|
|
|
if (lpWaveHdr)
|
|
free(lpWaveHdr);
|
|
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
/*
|
|
* Function:
|
|
* DGramInit
|
|
*
|
|
* Description:
|
|
* Init our UDP socket
|
|
*
|
|
*/
|
|
BOOL
|
|
CRDPSound::DGramInit(
|
|
VOID
|
|
)
|
|
{
|
|
BOOL rv = FALSE;
|
|
#ifndef OS_WINCE
|
|
INT optval;
|
|
#endif
|
|
INT rc;
|
|
struct sockaddr_in sin;
|
|
|
|
ASSERT(INVALID_SOCKET == m_hDGramSocket);
|
|
|
|
m_hDGramSocket = socket(AF_INET, SOCK_DGRAM, 0);
|
|
if (INVALID_SOCKET == m_hDGramSocket)
|
|
{
|
|
TRC(ERR, _T("DGramInit: can't create dgram socket: %d\n"),
|
|
WSAGetLastError());
|
|
goto exitpt;
|
|
}
|
|
|
|
// bind the socket to any port/address
|
|
//
|
|
sin.sin_family = PF_INET;
|
|
sin.sin_port = htons(0); // any port between 1024 and 50000
|
|
sin.sin_addr.s_addr = INADDR_ANY;
|
|
|
|
rc = bind(m_hDGramSocket, (struct sockaddr *)(&sin), sizeof(sin));
|
|
if (SOCKET_ERROR == rc)
|
|
{
|
|
TRC(ERR, _T("DGramInit: can't bind the socket: %d\n"),
|
|
WSAGetLastError());
|
|
goto exitpt;
|
|
}
|
|
|
|
#ifndef OS_WINCE
|
|
//
|
|
// assign an appropriate buffer length
|
|
//
|
|
optval = TSSND_BLOCKSONTHENET * (sizeof(SNDWAVE) + TSSND_BLOCKSIZE);
|
|
rc = setsockopt(m_hDGramSocket,
|
|
SOL_SOCKET,
|
|
SO_RCVBUF,
|
|
(LPSTR)&optval,
|
|
sizeof(optval));
|
|
|
|
if (SOCKET_ERROR == rc)
|
|
{
|
|
TRC(ERR, _T("DGramInit: setsockopt failed: %d\n"),
|
|
WSAGetLastError());
|
|
goto exitpt;
|
|
}
|
|
#endif
|
|
|
|
// select the socket on the message window
|
|
//
|
|
if (NULL != m_hMsgWindow &&
|
|
SOCKET_ERROR ==
|
|
WSAAsyncSelect(m_hDGramSocket, m_hMsgWindow,
|
|
WM_WSOCK, FD_READ))
|
|
{
|
|
TRC(ERR, _T("DGramInit: WSAAsyncSelect failed: %d\n"),
|
|
WSAGetLastError());
|
|
goto exitpt;
|
|
}
|
|
|
|
rv = TRUE;
|
|
|
|
exitpt:
|
|
if (!rv && INVALID_SOCKET != m_hDGramSocket)
|
|
{
|
|
closesocket(m_hDGramSocket);
|
|
m_hDGramSocket = INVALID_SOCKET;
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
/*
|
|
* Function:
|
|
* DGramDone
|
|
*
|
|
* Description:
|
|
* Destroy our UDP socket
|
|
*
|
|
*/
|
|
VOID
|
|
CRDPSound::DGramDone(
|
|
VOID
|
|
)
|
|
{
|
|
if (INVALID_SOCKET != m_hDGramSocket)
|
|
{
|
|
closesocket(m_hDGramSocket);
|
|
m_hDGramSocket = INVALID_SOCKET;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Function:
|
|
* DGramGetLocalPort
|
|
*
|
|
* Description:
|
|
* Retreives the local UDP port
|
|
*
|
|
*/
|
|
u_short
|
|
CRDPSound::DGramGetLocalPort(
|
|
VOID
|
|
)
|
|
{
|
|
u_short rv = 0;
|
|
struct sockaddr_in sin;
|
|
INT rc;
|
|
INT nSinSize;
|
|
|
|
if (INVALID_SOCKET == m_hDGramSocket)
|
|
{
|
|
TRC(ERR, _T("DGramGetLocalPort: invalid socket\n"));
|
|
goto exitpt;
|
|
}
|
|
|
|
nSinSize = sizeof(sin);
|
|
rc = getsockname(m_hDGramSocket,
|
|
(struct sockaddr *)&sin,
|
|
&nSinSize);
|
|
|
|
if (SOCKET_ERROR == rc)
|
|
{
|
|
TRC(ERR, _T("DGramGetLocalPort: getsockname failed: %d\n"),
|
|
WSAGetLastError());
|
|
goto exitpt;
|
|
}
|
|
|
|
ASSERT(PF_INET == sin.sin_family);
|
|
|
|
rv = sin.sin_port;
|
|
exitpt:
|
|
return rv;
|
|
}
|
|
|
|
/*
|
|
* Function:
|
|
* DGramSend
|
|
*
|
|
* Description:
|
|
* Send an UDP packet
|
|
*
|
|
*/
|
|
BOOL
|
|
CRDPSound::DGramSend(
|
|
LPVOID pData,
|
|
DWORD dwSize
|
|
)
|
|
{
|
|
INT rc = SOCKET_ERROR;
|
|
struct sockaddr_in sin;
|
|
|
|
ASSERT( 0 != m_dwRemoteDGramPort );
|
|
ASSERT( 0 != m_ulRemoteDGramAddress );
|
|
|
|
if ( INVALID_SOCKET == m_hDGramSocket )
|
|
{
|
|
TRC(ERR, _T("DGramSend: invalid socket handle\n"));
|
|
goto exitpt;
|
|
}
|
|
|
|
sin.sin_family = PF_INET;
|
|
sin.sin_port = (u_short)m_dwRemoteDGramPort;
|
|
sin.sin_addr.s_addr = m_ulRemoteDGramAddress;
|
|
rc = sendto(
|
|
m_hDGramSocket,
|
|
(LPSTR)pData,
|
|
dwSize,
|
|
0,
|
|
(struct sockaddr *)&sin,
|
|
sizeof( sin )
|
|
);
|
|
|
|
if ( SOCKET_ERROR == rc )
|
|
{
|
|
TRC(WRN, _T("DGramSend: sendto failed=%d\n"),
|
|
WSAGetLastError());
|
|
}
|
|
|
|
exitpt:
|
|
return ( rc != SOCKET_ERROR );
|
|
}
|
|
|
|
/*
|
|
* Function:
|
|
* DGramRecvWave
|
|
*
|
|
* Description:
|
|
* Recieve data from UDP
|
|
*
|
|
*/
|
|
BOOL
|
|
CRDPSound::DGramRecvWave(
|
|
PSNDWAVE pSndWave,
|
|
DWORD dwSize
|
|
)
|
|
{
|
|
BOOL rv = FALSE;
|
|
INT recvd;
|
|
struct sockaddr_in sin_from;
|
|
INT nSinFrom;
|
|
|
|
// parameters check
|
|
//
|
|
if (NULL == pSndWave)
|
|
{
|
|
TRC(ERR, _T("DGramRecvWave: pSndWave is NULL\n"));
|
|
goto exitpt;
|
|
}
|
|
|
|
if (dwSize <= sizeof(*pSndWave))
|
|
{
|
|
TRC(ERR, _T("DGramRecvWave: no enough space to get the wave\n"));
|
|
goto exitpt;
|
|
}
|
|
|
|
// receive the message
|
|
//
|
|
nSinFrom = sizeof( sin_from );
|
|
recvd = recvfrom(
|
|
m_hDGramSocket,
|
|
(LPSTR)pSndWave,
|
|
dwSize,
|
|
0,
|
|
(struct sockaddr *)&sin_from,
|
|
&nSinFrom
|
|
);
|
|
|
|
if (recvd < sizeof(pSndWave->Prolog) || ((DWORD)recvd) > dwSize)
|
|
{
|
|
if (SOCKET_ERROR == recvd)
|
|
TRC(ERR, _T("WM_WSOCK: recvfrom failed: %d\n"),
|
|
WSAGetLastError());
|
|
else
|
|
TRC(WRN,
|
|
_T("WM_WSOCK: received %d bytes instead %d. Discarding\n"),
|
|
recvd, sizeof(pSndWave->Prolog));
|
|
goto exitpt;
|
|
}
|
|
|
|
ASSERT( PF_INET == sin_from.sin_family );
|
|
if ( PF_INET != sin_from.sin_family )
|
|
{
|
|
TRC(WRN, _T("WM_WSOCK: message from invalid protocol( %x )\n"),
|
|
sin_from.sin_family );
|
|
goto exitpt;
|
|
}
|
|
if ( sizeof( sin_from ) > nSinFrom )
|
|
{
|
|
TRC(WRN, _T("WM_WSOCK: from address invalid, len=%d\n"),
|
|
nSinFrom );
|
|
goto exitpt;
|
|
}
|
|
|
|
m_dwRemoteDGramPort = sin_from.sin_port;
|
|
m_ulRemoteDGramAddress = sin_from.sin_addr.s_addr;
|
|
|
|
//
|
|
// Check for line-training request
|
|
//
|
|
if (SNDC_TRAINING == pSndWave->Prolog.Type)
|
|
{
|
|
SNDTRAINING SndTraining;
|
|
PSNDTRAINING pRecvTraining;
|
|
|
|
TRC(ALV, _T("DGramRecvWave: training, sending a response\n"));
|
|
|
|
pRecvTraining = (PSNDTRAINING)pSndWave;
|
|
|
|
SndTraining.Prolog.Type = SNDC_TRAINING;
|
|
SndTraining.Prolog.BodySize = sizeof(SndTraining) -
|
|
sizeof(SndTraining.Prolog);
|
|
SndTraining.wTimeStamp = pRecvTraining->wTimeStamp;
|
|
SndTraining.wPackSize = pRecvTraining->wPackSize;
|
|
|
|
//
|
|
// send the response immediatly
|
|
//
|
|
DGramSend( &SndTraining, sizeof(SndTraining) );
|
|
|
|
goto exitpt;
|
|
}
|
|
|
|
if ( SNDC_UDPWAVE == pSndWave->Prolog.Type)
|
|
{
|
|
rv = ConstructFromDGramFrags(
|
|
(PSNDUDPWAVE)pSndWave,
|
|
recvd,
|
|
pSndWave,
|
|
dwSize
|
|
);
|
|
goto exitpt;
|
|
|
|
} else if ( SNDC_UDPWAVELAST == pSndWave->Prolog.Type)
|
|
{
|
|
rv = ConstructFromDGramLastFrag(
|
|
(PSNDUDPWAVELAST)pSndWave,
|
|
recvd,
|
|
pSndWave,
|
|
dwSize
|
|
);
|
|
goto exitpt;
|
|
}
|
|
|
|
if (SNDC_WAVEENCRYPT == pSndWave->Prolog.Type)
|
|
{
|
|
|
|
if ( !SL_Encrypt(
|
|
(PBYTE)(pSndWave + 1),
|
|
pSndWave->dwBlockNo,
|
|
pSndWave->Prolog.BodySize - sizeof(SNDWAVE) + sizeof(SNDPROLOG) ))
|
|
goto exitpt;
|
|
|
|
}
|
|
else if (SNDC_WAVE != pSndWave->Prolog.Type)
|
|
{
|
|
TRC(ERR, _T("DGramRecvWave: invalid message type: %d\n"),
|
|
pSndWave->Prolog.Type);
|
|
goto exitpt;
|
|
}
|
|
|
|
if ( recvd < sizeof(SNDPROLOG) ||
|
|
pSndWave->Prolog.BodySize + sizeof(SNDPROLOG) != (DWORD)recvd)
|
|
{
|
|
TRC(WRN,
|
|
_T("WM_WSOCK: received %d bytes instead %d. Discarding\n"),
|
|
recvd, pSndWave->Prolog.BodySize + sizeof(SNDPROLOG) );
|
|
goto exitpt;
|
|
}
|
|
|
|
TRC(ALV, _T("DGramRecvWave: block no: %d\n"),
|
|
pSndWave->cBlockNo);
|
|
|
|
rv = TRUE;
|
|
|
|
exitpt:
|
|
return rv;
|
|
}
|
|
|
|
BOOL
|
|
CRDPSound::ConstructFromDGramFrags(
|
|
PSNDUDPWAVE pWave,
|
|
DWORD dwSize,
|
|
PSNDWAVE pReady,
|
|
DWORD dwReadySize
|
|
)
|
|
{
|
|
BOOL rv = FALSE;
|
|
PBYTE pRecvdData;
|
|
DWORD dwFragNo;
|
|
DWORD dwEstimatedSize;
|
|
DWORD dwDataSize;
|
|
PFRAGSLOT pSlot;
|
|
|
|
if ( dwSize <= sizeof( *pWave ))
|
|
{
|
|
TRC( ERR, _T("ConstructFromDGramFrags: packet too small %d\n"),
|
|
dwSize );
|
|
goto exitpt;
|
|
}
|
|
|
|
pRecvdData = (PBYTE)(pWave + 1);
|
|
dwFragNo = pWave->cFragNo & (~RDPSND_FRAGNO_EXT);
|
|
dwDataSize = dwSize - sizeof( *pWave );
|
|
|
|
if ( pWave->cFragNo & RDPSND_FRAGNO_EXT )
|
|
{
|
|
dwFragNo <<= 8;
|
|
dwFragNo += *pRecvdData;
|
|
pRecvdData++;
|
|
dwDataSize--;
|
|
}
|
|
dwEstimatedSize = ( dwFragNo + 1 ) * dwDataSize;
|
|
if ( dwEstimatedSize >= MAX_UDP_SIZE )
|
|
{
|
|
TRC( ERR, _T("ConstructFromDGramFrags: estimated size >=64K (0x%x)\n"),
|
|
dwEstimatedSize );
|
|
goto exitpt;
|
|
}
|
|
|
|
if (!_FragSlotFind( &pSlot, pWave->cBlockNo, dwEstimatedSize ))
|
|
{
|
|
goto exitpt;
|
|
}
|
|
|
|
if ( 0 != pSlot->dwFragSize && pSlot->dwFragSize != dwDataSize )
|
|
{
|
|
TRC( ERR, _T("ConstructFromDGramFrags: received frag with different size: %d, expect %d\n"),
|
|
dwDataSize, pSlot->dwFragSize );
|
|
_FragSlotClear( pSlot );
|
|
goto exitpt;
|
|
}
|
|
memcpy( pSlot->pData + dwFragNo * dwDataSize, pRecvdData, dwDataSize );
|
|
pSlot->dwFragSize = dwDataSize;
|
|
pSlot->dwTotalSize += dwDataSize;
|
|
|
|
// TRC( INF, _T("Fragment received: block#=0x%x, frag#=0x%x, total=0x%x\n"),
|
|
// pWave->cBlockNo, dwFragNo, pSlot->dwTotalSize );
|
|
if ( pSlot->dwTotalSize != pSlot->dwExpectedTotalSize )
|
|
{
|
|
goto exitpt;
|
|
}
|
|
//
|
|
// wave is ready, convert
|
|
//
|
|
if (!_FragSlotToWave( pSlot, pReady, dwReadySize ))
|
|
{
|
|
goto exitpt;
|
|
}
|
|
rv = TRUE;
|
|
|
|
exitpt:
|
|
|
|
return rv;
|
|
}
|
|
|
|
BOOL
|
|
CRDPSound::ConstructFromDGramLastFrag(
|
|
PSNDUDPWAVELAST pLast,
|
|
DWORD dwSize,
|
|
PSNDWAVE pReady,
|
|
DWORD dwReadySize
|
|
)
|
|
{
|
|
BOOL rv = FALSE;
|
|
DWORD dwDataSize;
|
|
PFRAGSLOT pSlot;
|
|
|
|
if ( dwSize < sizeof( *pLast ))
|
|
{
|
|
TRC( ERR, _T("ConstructFromDGramLastFrag: too small packet (%d)\n"), dwSize );
|
|
}
|
|
|
|
if (!_FragSlotFind( &pSlot, pLast->cBlockNo, pLast->wTotalSize ))
|
|
{
|
|
TRC( WRN, _T("ConstructFromDGramLastFrag: Failed to find a slot for last fragment\n" ));
|
|
goto exitpt;
|
|
}
|
|
|
|
pSlot->dwExpectedTotalSize = pLast->wTotalSize;
|
|
pSlot->wTimeStamp = pLast->wTimeStamp;
|
|
pSlot->wFormatNo = pLast->wFormatNo;
|
|
pSlot->dwBlockNo = pLast->dwBlockNo;
|
|
dwDataSize = dwSize - sizeof( *pLast );
|
|
memcpy( pSlot->pData + pSlot->dwExpectedTotalSize - dwDataSize, pLast + 1, dwDataSize );
|
|
|
|
pSlot->dwTotalSize += dwDataSize;
|
|
|
|
// TRC( INF, _T("Fragment LAST block#=0x%x, total=0x%x\n"),
|
|
// pLast->cBlockNo, pSlot->dwTotalSize );
|
|
if ( pSlot->dwTotalSize != pSlot->dwExpectedTotalSize )
|
|
{
|
|
goto exitpt;
|
|
}
|
|
//
|
|
// ready, set, go
|
|
//
|
|
if (!_FragSlotToWave( pSlot, pReady, dwReadySize ))
|
|
{
|
|
goto exitpt;
|
|
}
|
|
rv = TRUE;
|
|
|
|
exitpt:
|
|
return rv;
|
|
}
|
|
|
|
BOOL
|
|
CRDPSound::_FragSlotFind(
|
|
PFRAGSLOT *ppFragSlot,
|
|
BYTE cBlockNo,
|
|
DWORD dwEstimatedSize
|
|
)
|
|
{
|
|
BOOL rv = FALSE;
|
|
PFRAGSLOT pIter = m_pFragSlots;
|
|
PFRAGSLOT pLastFree = NULL;
|
|
PFRAGSLOT pFound = NULL;
|
|
DWORD dwNewSize;
|
|
|
|
while( pIter )
|
|
{
|
|
//
|
|
// check if the slot hasn't been used for a while
|
|
//
|
|
if ( pIter->bUsed &&
|
|
(BYTE)( m_cLastReceivedBlock - pIter->cBlockNo ) < TSSND_BLOCKSONTHENET)
|
|
{
|
|
TRC( WRN, _T("Old frag found id %d, last id %d\n"),
|
|
pIter->cBlockNo, m_cLastReceivedBlock );
|
|
_FragSlotClear( pIter );
|
|
}
|
|
|
|
if ( !pIter->bUsed && NULL == pLastFree )
|
|
{
|
|
pLastFree = pIter;
|
|
|
|
}
|
|
if ( pIter->bUsed && pIter->cBlockNo == cBlockNo )
|
|
{
|
|
// found a block with same number
|
|
pFound = pIter;
|
|
break;
|
|
}
|
|
pIter = pIter->pNext;
|
|
}
|
|
|
|
if ( NULL == pFound && NULL != pLastFree )
|
|
{
|
|
//
|
|
// found a free block
|
|
//
|
|
pLastFree->bUsed = TRUE;
|
|
pLastFree->cBlockNo = cBlockNo;
|
|
pFound = pLastFree;
|
|
}
|
|
|
|
if ( NULL != pFound &&
|
|
pFound->dwAllocatedSize < dwEstimatedSize )
|
|
{
|
|
dwNewSize = dwEstimatedSize * 2;
|
|
|
|
PBYTE pNewData = (PBYTE)malloc( dwEstimatedSize * 2 );
|
|
if ( NULL == pNewData )
|
|
{
|
|
//
|
|
// we can't use this slot anymore
|
|
//
|
|
_FragSlotClear( pFound );
|
|
pFound = NULL;
|
|
goto exitpt;
|
|
}
|
|
memcpy( pNewData, pFound->pData, pFound->dwAllocatedSize );
|
|
pFound->dwAllocatedSize = dwNewSize;
|
|
free( pFound->pData );
|
|
pFound->pData = pNewData;
|
|
|
|
}
|
|
if ( NULL == pFound )
|
|
{
|
|
// allocate a new block
|
|
//
|
|
pFound = (PFRAGSLOT)malloc( sizeof( *pFound ));
|
|
if ( NULL == pFound )
|
|
{
|
|
goto exitpt;
|
|
}
|
|
|
|
dwNewSize = ( dwEstimatedSize > TSSND_BLOCKSIZE + RDPSND_SIGNATURE_SIZE )
|
|
? dwEstimatedSize : TSSND_BLOCKSIZE + RDPSND_SIGNATURE_SIZE;
|
|
|
|
pFound->pData = (PBYTE)malloc( dwNewSize );
|
|
if ( NULL == pFound->pData )
|
|
{
|
|
free( pFound );
|
|
goto exitpt;
|
|
}
|
|
|
|
pFound->dwAllocatedSize = dwNewSize;
|
|
_FragSlotClear( pFound );
|
|
pFound->bUsed = TRUE;
|
|
pFound->cBlockNo = cBlockNo;
|
|
|
|
//
|
|
// add it to the list
|
|
//
|
|
pFound->pNext = m_pFragSlots;
|
|
m_pFragSlots = pFound;
|
|
}
|
|
|
|
*ppFragSlot = pFound;
|
|
|
|
rv = TRUE;
|
|
|
|
exitpt:
|
|
|
|
return rv;
|
|
}
|
|
|
|
VOID
|
|
CRDPSound::_FragSlotClear(
|
|
PFRAGSLOT pSlot
|
|
)
|
|
{
|
|
DWORD dwAllocated = pSlot->dwAllocatedSize;
|
|
PBYTE pData = pSlot->pData;
|
|
PFRAGSLOT pNext = pSlot->pNext;
|
|
|
|
memset( pSlot, 0, sizeof( *pSlot ));
|
|
pSlot->dwAllocatedSize = dwAllocated;
|
|
pSlot->pData = pData;
|
|
pSlot->pNext = pNext;
|
|
}
|
|
|
|
VOID
|
|
CRDPSound::_FragSlotFreeAll(
|
|
VOID
|
|
)
|
|
{
|
|
//
|
|
// free the fragment slots too
|
|
//
|
|
while( NULL != m_pFragSlots )
|
|
{
|
|
PFRAGSLOT pNext = m_pFragSlots->pNext;
|
|
|
|
free( m_pFragSlots->pData );
|
|
free( m_pFragSlots );
|
|
|
|
m_pFragSlots = pNext;
|
|
}
|
|
}
|
|
|
|
BOOL
|
|
CRDPSound::_FragSlotToWave(
|
|
PFRAGSLOT pSlot,
|
|
PSNDWAVE pWave,
|
|
DWORD dwWaveSize
|
|
)
|
|
{
|
|
BOOL rv = FALSE;
|
|
DWORD dwAvailDataSize;
|
|
DWORD dwBodySize;
|
|
|
|
ASSERT( pSlot->dwExpectedTotalSize == pSlot->dwTotalSize );
|
|
|
|
dwAvailDataSize = dwWaveSize - sizeof( *pWave );
|
|
if ( dwAvailDataSize < pSlot->dwTotalSize )
|
|
{
|
|
TRC( ERR, _T("_FragSlotToWave insufficient size to move the wave")
|
|
_T(" need %d, available %d\n"),
|
|
pSlot->dwTotalSize, dwAvailDataSize );
|
|
goto exitpt;
|
|
}
|
|
|
|
//
|
|
// when we support fragments, packets are encrypted
|
|
//
|
|
pWave->Prolog.Type = SNDC_WAVEENCRYPT;
|
|
dwBodySize = pSlot->dwTotalSize + sizeof( *pWave ) -
|
|
sizeof( pWave->Prolog );
|
|
ASSERT( dwBodySize < MAX_UDP_SIZE - sizeof( *pWave ));
|
|
pWave->Prolog.BodySize = (UINT16)dwBodySize;
|
|
pWave->wTimeStamp = pSlot->wTimeStamp;
|
|
pWave->wFormatNo = pSlot->wFormatNo;
|
|
pWave->dwBlockNo = pSlot->dwBlockNo;
|
|
memcpy( pWave + 1, pSlot->pData, pSlot->dwTotalSize );
|
|
|
|
//
|
|
// release the slot
|
|
//
|
|
_FragSlotClear( pSlot );
|
|
|
|
rv = TRUE;
|
|
|
|
exitpt:
|
|
return rv;
|
|
}
|
|
|
|
INT
|
|
WSInit(
|
|
VOID
|
|
)
|
|
{
|
|
WORD versionRequested;
|
|
WSADATA wsaData;
|
|
int intRC;
|
|
|
|
versionRequested = MAKEWORD(1, 1);
|
|
|
|
intRC = WSAStartup(versionRequested, &wsaData);
|
|
|
|
if (intRC != 0)
|
|
{
|
|
TRC(ERR, _T("Failed to initialize WinSock rc:%d\n"), intRC);
|
|
}
|
|
return intRC;
|
|
}
|
|
|
|
/*
|
|
* create signature bits
|
|
*/
|
|
VOID
|
|
CRDPSound::SL_Signature(
|
|
PBYTE pSig,
|
|
DWORD dwBlockNo
|
|
)
|
|
{
|
|
BYTE ShaBits[A_SHA_DIGEST_LEN];
|
|
A_SHA_CTX SHACtx;
|
|
|
|
ASSERT( A_SHA_DIGEST_LEN > RDPSND_SIGNATURE_SIZE );
|
|
|
|
A_SHAInit(&SHACtx);
|
|
*((DWORD *)(m_EncryptKey + RANDOM_KEY_LENGTH)) = dwBlockNo;
|
|
A_SHAUpdate(&SHACtx, (PBYTE)m_EncryptKey, sizeof(m_EncryptKey));
|
|
A_SHAFinal(&SHACtx, ShaBits);
|
|
memcpy( pSig, ShaBits, RDPSND_SIGNATURE_SIZE );
|
|
}
|
|
|
|
/*
|
|
* signature which verifies the audio bits
|
|
*/
|
|
VOID
|
|
CRDPSound::SL_AudioSignature(
|
|
PBYTE pSig,
|
|
DWORD dwBlockNo,
|
|
PBYTE pData,
|
|
DWORD dwDataSize
|
|
)
|
|
{
|
|
BYTE ShaBits[A_SHA_DIGEST_LEN];
|
|
A_SHA_CTX SHACtx;
|
|
|
|
A_SHAInit(&SHACtx);
|
|
*((DWORD *)(m_EncryptKey + RANDOM_KEY_LENGTH)) = dwBlockNo;
|
|
A_SHAUpdate(&SHACtx, (PBYTE)m_EncryptKey, sizeof(m_EncryptKey));
|
|
A_SHAUpdate(&SHACtx, pData, dwDataSize );
|
|
A_SHAFinal(&SHACtx, ShaBits);
|
|
memcpy( pSig, ShaBits, RDPSND_SIGNATURE_SIZE );
|
|
}
|
|
|
|
/*
|
|
* encrypt/decrypt a block of data
|
|
*
|
|
*/
|
|
BOOL
|
|
CRDPSound::SL_Encrypt( PBYTE pBits, DWORD BlockNo, DWORD dwBitsLen )
|
|
{
|
|
BYTE ShaBits[A_SHA_DIGEST_LEN];
|
|
RC4_KEYSTRUCT rc4key;
|
|
#ifndef OS_WINCE
|
|
DWORD i;
|
|
#endif
|
|
PBYTE pbBuffer;
|
|
A_SHA_CTX SHACtx;
|
|
DWORD dw;
|
|
DWORD_PTR *pdwBits;
|
|
|
|
A_SHAInit(&SHACtx);
|
|
|
|
// SHA the static bits
|
|
*((DWORD *)(m_EncryptKey + RANDOM_KEY_LENGTH)) = BlockNo;
|
|
A_SHAUpdate(&SHACtx, (PBYTE)m_EncryptKey, sizeof(m_EncryptKey));
|
|
|
|
A_SHAFinal(&SHACtx, ShaBits);
|
|
|
|
rc4_key(&rc4key, A_SHA_DIGEST_LEN, ShaBits);
|
|
rc4(&rc4key, dwBitsLen, pBits);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* Function:
|
|
* _DbgPrintMessage
|
|
*
|
|
* Description:
|
|
* A tracing function
|
|
*
|
|
* Parameters:
|
|
* level - current message trace level
|
|
* format - message format
|
|
* ... - parameters
|
|
*
|
|
*/
|
|
VOID
|
|
_cdecl
|
|
_DbgPrintMessage(LPCTSTR level, LPCTSTR format, ...)
|
|
{
|
|
TCHAR szBuffer[256];
|
|
va_list arglist;
|
|
|
|
if (ALV == level)
|
|
return;
|
|
|
|
va_start (arglist, format);
|
|
StringCchVPrintf(szBuffer, SIZE_TCHARS(szBuffer), format, arglist);
|
|
va_end (arglist);
|
|
|
|
#ifndef OS_WINCE
|
|
OutputDebugString(level);
|
|
OutputDebugString(szBuffer);
|
|
#endif // !OS_WINCE
|
|
}
|