///////////////////////////////////////////////////////////////////// // // Module: sndchan.c // // Purpose: Server-side audio redirection communication // module // // Copyright(C) Microsoft Corporation 2000 // // History: 4-10-2000 vladimis [created] // ///////////////////////////////////////////////////////////////////// #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // // Include security headers for RNG functions // #define NO_INCLUDE_LICENSING 1 #include #include "sndchan.h" #include "sndknown.h" #define TSSND_REG_MAXBANDWIDTH_KEY L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Drivers32\\Terminal Server\\RDP" #define TSSND_REG_MAXBANDWIDTH_VAL L"MaxBandwidth" #define TSSND_REG_MINBANDWIDTH_VAL L"MinBandwidth" #define TSSND_REG_DISABLEDGRAM_VAL L"DisableDGram" #define TSSND_REG_ENABLEMP3_VAL L"EnableMP3Codec" #define TSSND_REG_ALLOWCODECS L"AllowCodecs" #define TSSND_REG_MAXDGRAM L"MaxDGram" #define DEFAULT_RESPONSE_TIMEOUT 5000 #define TSSND_TRAINING_BLOCKSIZE 1024 // // --- READ THIS IF YOU ARE ADDING FEATURES --- // right now the encryption works only from server to client // there's no data send from server to client // if you read this in the future and you are planning to add // data stream from client to server, PLEASE ENCRYPT IT !!! // use SL_Encrypt function for that // #define MIN_ENCRYPT_LEVEL 2 #define STAT_COUNT 32 #define STAT_COUNT_INIT (STAT_COUNT - 8) #define READ_EVENT 0 #define DISCONNECT_EVENT 1 #define RECONNECT_EVENT 2 #define DATAREADY_EVENT 3 #define DGRAM_EVENT 4 #define POWERWAKEUP_EVENT 5 #define POWERSUSPEND_EVENT 6 #define TOTAL_EVENTS 7 #define NEW_CODEC_COVER 90 // minimum percentage a new codec has to cover // i.e if we are at 7kbps and the new meassurement is // for 10kbps we are not switching to codec which // does have more than NEW_CODEC_COVER * 10k / 100 bandwith // requirement // // Data for enabling private codecs // BUGBUG // Legal issue ?! // #ifndef G723MAGICWORD1 #define G723MAGICWORD1 0xf7329ace #endif #ifndef G723MAGICWORD2 #define G723MAGICWORD2 0xacdeaea2 #endif #ifndef VOXWARE_KEY #define VOXWARE_KEY "35243410-F7340C0668-CD78867B74DAD857-AC71429AD8CAFCB5-E4E1A99E7FFD-371" #endif #define _RDPSNDWNDCLASS L"RDPSound window" #ifdef _WIN32 #include #else #ifndef RC_INVOKED #pragma pack(1) #endif #endif typedef struct msg723waveformat_tag { WAVEFORMATEX wfx; WORD wConfigWord; DWORD dwCodeword1; DWORD dwCodeword2; } MSG723WAVEFORMAT; typedef struct intelg723waveformat_tag { WAVEFORMATEX wfx; WORD wConfigWord; DWORD dwCodeword1; DWORD dwCodeword2; } INTELG723WAVEFORMAT; typedef struct tagVOXACM_WAVEFORMATEX { WAVEFORMATEX wfx; DWORD dwCodecId; DWORD dwMode; char szKey[72]; } VOXACM_WAVEFORMATEX, *PVOXACM_WAVEFORMATEX, FAR *LPVOXACM_WAVEFORMATEX; #define WAVE_FORMAT_WMAUDIO2 0x161 #ifdef _WIN32 #include #else #ifndef RC_INVOKED #pragma pack() #endif #endif typedef struct { SNDPROLOG Prolog; UINT uiPrologReceived; PVOID pBody; UINT uiBodyAllocated; UINT uiBodyReceived; } SNDMESSAGE, *PSNDMESSAGE; typedef struct _VCSNDFORMATLIST { struct _VCSNDFORMATLIST *pNext; HACMDRIVERID hacmDriverId; WAVEFORMATEX Format; // additional data for the format } VCSNDFORMATLIST, *PVCSNDFORMATLIST; typedef VOID (*PFNCONVERTER)( INT16 *, DWORD, DWORD * ); static HANDLE g_hVC = NULL; // virtual channel handle BYTE g_Buffer[CHANNEL_CHUNK_LENGTH]; // receive buffer UINT g_uiBytesInBuffer = 0; // UINT g_uiBufferOffset = 0; OVERLAPPED g_OverlappedRead; // overlapped structure HANDLE g_hDataReadyEvent = NULL; // set by the client apps HANDLE g_hStreamIsEmptyEvent = NULL; // set by this code HANDLE g_hStreamMutex = NULL; // guard the stream data HANDLE g_hStream = NULL; // stream handle HANDLE g_hDisconnectEvent = NULL; // set for this VC PSNDSTREAM g_Stream; // stream data pointer BOOL g_bRunning = TRUE; // TRUE if running BOOL g_bDeviceOpened = FALSE; // TRUE if device opened BOOL g_bDisconnected = FALSE; // TRUE if disconnected DWORD g_dwLineBandwidth = 0; // current bandwidth DWORD g_dwCodecChangeThreshold = 10; // how mach the bandwith has to change in order // to change the codec ( in percents ) // this number changes up to 50% PSNDFORMATITEM *g_ppNegotiatedFormats = NULL; // list of formats DWORD g_dwNegotiatedFormats = 0; // number of formats DWORD g_dwCurrentFormat = 0; // current format Id HACMDRIVERID g_hacmDriverId = NULL; // codec handles HACMDRIVER g_hacmDriver = NULL; HACMSTREAM g_hacmStream = NULL; PFNCONVERTER g_pfnConverter = NULL; // intermidiate converter DWORD g_dwDataRemain = 0; BYTE g_pCnvPrevData[ TSSND_BLOCKSIZE ]; PVCSNDFORMATLIST g_pAllCodecsFormatList = NULL; // all available codecs DWORD g_dwAllCodecsNumber = 0; DWORD g_dwMaxBandwidth = (DWORD) -1; // options DWORD g_dwMinBandwidth = 0; DWORD g_dwDisableDGram = 0; DWORD g_dwEnableMP3Codec = 0; DWORD *g_AllowCodecs = NULL; DWORD g_AllowCodecsSize = 0; DWORD g_dwStatPing = 0; // statistics DWORD g_dwStatLatency = 0; DWORD g_dwBlocksOnTheNet = TSSND_BLOCKSONTHENET; DWORD g_dwStatCount = STAT_COUNT_INIT; DWORD g_dwPacketSize = 0; HANDLE g_hPowerWakeUpEvent = NULL; // power events HANDLE g_hPowerSuspendEvent = NULL; BOOL g_bSuspended = FALSE; BOOL g_bDeviceFailed = FALSE; // // datagram control // SOCKET g_hDGramSocket = INVALID_SOCKET; DWORD g_dwDGramPort = 0; DWORD g_dwDGramSize = 1460; // number good which is ok for LAN u_long g_ulDGramAddress = 0; DWORD g_EncryptionLevel = 3; DWORD g_wClientVersion = 0; DWORD g_HiBlockNo = 0; BYTE g_EncryptKey[RANDOM_KEY_LENGTH + 4]; WSABUF g_wsabuf; BYTE g_pDGramRecvData[128]; WSAOVERLAPPED g_WSAOverlapped; const CHAR *ALV = "TSSNDD::ALV - "; const CHAR *INF = "TSSNDD::INF - "; const CHAR *WRN = "TSSNDD::WRN - "; const CHAR *ERR = "TSSNDD::ERR - "; const CHAR *FATAL = "TSSNDD::FATAL - "; static HANDLE g_hThread = NULL; // // internal functions // BOOL ChannelBlockWrite( PVOID pBlock, ULONG ulBlockSize ); BOOL VCSndAcquireStream( VOID ); BOOL VCSndReleaseStream( VOID ); BOOL _VCSndOpenConverter( VOID ); VOID _VCSndCloseConverter( VOID ); VOID _VCSndOrderFormatList( PVCSNDFORMATLIST *ppFormatList, DWORD *pdwNum ); DWORD _VCSndChooseProperFormat( DWORD dwBandwidth ); BOOL _VCSndGetACMDriverId( PSNDFORMATITEM pSndFmt ); VOID DGramRead( HANDLE hDGramEvent, PVOID *ppBuff, DWORD *pdwRecvd ); VOID DGramReadComplete( PVOID *ppBuff, DWORD *pdwRecvd ); #if !( TSSND_NATIVE_SAMPLERATE - 22050 ) // // converters // convert to the native format // #define CONVERTFROMNATIVETOMONO(_speed_) \ VOID \ _Convert##_speed_##Mono( \ INT16 *pSrc, \ DWORD dwSrcSize, \ DWORD *pdwDstSize ) \ { \ DWORD dwDstSize; \ DWORD i; \ DWORD dwLeap; \ INT16 *pDest = pSrc; \ \ ASSERT( TSSND_NATIVE_SAMPLERATE >= _speed_ ); \ ASSERT( TSSND_NATIVE_CHANNELS == 2 ); \ \ dwDstSize = dwSrcSize * _speed_ / \ ( TSSND_NATIVE_BLOCKALIGN * TSSND_NATIVE_SAMPLERATE ); \ \ for (i = 0, dwLeap = 0; \ i < dwDstSize; \ i ++) \ { \ INT sum; \ \ sum = pSrc[0] + pSrc[1]; \ \ if (sum > 0x7FFF) \ sum = 0x7FFF; \ if (sum < -0x8000) \ sum = -0x8000; \ \ pDest[0] = (INT16)sum; \ pDest ++; \ \ dwLeap += 2 * TSSND_NATIVE_SAMPLERATE; \ pSrc += dwLeap / _speed_; \ dwLeap %= _speed_; \ } \ \ *pdwDstSize = dwDstSize * 2; \ } #define CONVERTFROMNATIVETOSTEREO(_speed_) \ VOID \ _Convert##_speed_##Stereo( \ INT16 *pSrc, \ DWORD dwSrcSize, \ DWORD *pdwDstSize ) \ { \ DWORD dwDstSize; \ DWORD i; \ DWORD dwLeap; \ INT16 *pDest = pSrc; \ \ ASSERT( TSSND_NATIVE_SAMPLERATE >= _speed_ ); \ \ dwDstSize = dwSrcSize * _speed_ / \ ( TSSND_NATIVE_BLOCKALIGN * TSSND_NATIVE_SAMPLERATE ); \ for (i = 0, dwLeap = 0; \ i < dwDstSize; \ i ++) \ { \ INT sum; \ \ pDest[0] = pSrc[0]; \ pDest ++; \ pDest[0] = pSrc[1]; \ pDest ++; \ \ dwLeap += 2 * TSSND_NATIVE_SAMPLERATE; \ pSrc += dwLeap / _speed_; \ dwLeap %= _speed_; \ } \ \ *pdwDstSize = dwDstSize * 4; \ } VOID _Convert11025Mono( INT16 *pSrc, DWORD dwSrcSize, DWORD *pdwDstSize ) { DWORD dwDstSize; INT16 *pDest = pSrc; ASSERT( TSSND_NATIVE_SAMPLERATE >= 11025 ); dwDstSize = dwSrcSize / ( TSSND_NATIVE_BLOCKALIGN * 2 ); *pdwDstSize = 2 * dwDstSize; for (; dwDstSize; dwDstSize --) { INT sum = pSrc[0] + pSrc[1]; if (sum > 0x7FFF) sum = 0x7FFF; if (sum < -0x8000) sum = -0x8000; pDest[0] = (INT16)sum; pDest ++; pSrc += 4; } } VOID _Convert22050Mono( INT16 *pSrc, DWORD dwSrcSize, DWORD *pdwDstSize ) { DWORD dwDstSize; INT16 *pDest = pSrc; ASSERT( TSSND_NATIVE_SAMPLERATE >= 22050 ); dwDstSize = dwSrcSize / ( TSSND_NATIVE_BLOCKALIGN ); *pdwDstSize = 2 * dwDstSize; for (; dwDstSize; dwDstSize --) { INT sum = pSrc[0] + pSrc[1]; if (sum > 0x7FFF) sum = 0x7FFF; if (sum < -0x8000) sum = -0x8000; pDest[0] = (INT16)sum; pDest ++; pSrc += 2; } } VOID _Convert11025Stereo( INT16 *pSrc, DWORD dwSrcSize, DWORD *pdwDstSize ) { DWORD dwDstSize; INT16 *pDest = pSrc; ASSERT( TSSND_NATIVE_SAMPLERATE >= 22050 ); dwDstSize = dwSrcSize / ( TSSND_NATIVE_BLOCKALIGN * 2 ); *pdwDstSize = 4 * dwDstSize; for (; dwDstSize; dwDstSize --) { pDest[0] = pSrc[0]; pSrc ++; pDest ++; pDest[0] = pSrc[0]; pDest ++; pSrc ++; pSrc += 2; } } // // Make the actual code // CONVERTFROMNATIVETOMONO( 8000 ) CONVERTFROMNATIVETOMONO( 12000 ) CONVERTFROMNATIVETOMONO( 16000 ) CONVERTFROMNATIVETOSTEREO( 8000 ) CONVERTFROMNATIVETOSTEREO( 12000 ) CONVERTFROMNATIVETOSTEREO( 16000 ) #else #pragma error #endif u_long inet_addrW( LPCWSTR szAddressW ) { CHAR szAddressA[32]; *szAddressA = 0; WideCharToMultiByte( CP_ACP, 0, szAddressW, -1, szAddressA, sizeof(szAddressA), NULL, NULL); return inet_addr(szAddressA); } /* * create signature bits */ VOID 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 *)(g_EncryptKey + RANDOM_KEY_LENGTH)) = dwBlockNo; A_SHAUpdate(&SHACtx, (PBYTE)g_EncryptKey, sizeof(g_EncryptKey)); A_SHAFinal(&SHACtx, ShaBits); memcpy( pSig, ShaBits, RDPSND_SIGNATURE_SIZE ); } /* * signature which verifies the audio bits */ VOID SL_AudioSignature( PBYTE pSig, DWORD dwBlockNo, PBYTE pData, DWORD dwDataSize ) { BYTE ShaBits[A_SHA_DIGEST_LEN]; A_SHA_CTX SHACtx; A_SHAInit(&SHACtx); *((DWORD *)(g_EncryptKey + RANDOM_KEY_LENGTH)) = dwBlockNo; A_SHAUpdate(&SHACtx, (PBYTE)g_EncryptKey, sizeof(g_EncryptKey)); A_SHAUpdate(&SHACtx, pData, dwDataSize ); A_SHAFinal(&SHACtx, ShaBits); memcpy( pSig, ShaBits, RDPSND_SIGNATURE_SIZE ); } /* * encrypt/decrypt a block of data * */ BOOL SL_Encrypt( PBYTE pBits, DWORD BlockNo, DWORD dwBitsLen ) { BYTE ShaBits[A_SHA_DIGEST_LEN]; RC4_KEYSTRUCT rc4key; DWORD i; PBYTE pbBuffer; A_SHA_CTX SHACtx; DWORD dw; DWORD_PTR *pdwBits; A_SHAInit(&SHACtx); // SHA the bits *((DWORD *)(g_EncryptKey + RANDOM_KEY_LENGTH)) = BlockNo; A_SHAUpdate(&SHACtx, (PBYTE)g_EncryptKey, sizeof(g_EncryptKey)); A_SHAFinal(&SHACtx, ShaBits); rc4_key(&rc4key, A_SHA_DIGEST_LEN, ShaBits); rc4(&rc4key, dwBitsLen, pBits); return TRUE; } BOOL SL_SendKey( VOID ) { SNDCRYPTKEY Key; Key.Prolog.Type = SNDC_CRYPTKEY; Key.Prolog.BodySize = sizeof( Key ) - sizeof( Key.Prolog ); Key.Reserved = 0; memcpy( Key.Seed, g_EncryptKey, sizeof( Key.Seed )); return ChannelBlockWrite( &Key, sizeof( Key )); } /* * Function: * _StatsCollect * * Description: * Collects statistics for the line quality * */ VOID _StatsCollect( DWORD dwTimeStamp ) { DWORD dwTimeDiff; #if _DBG_STATS TRC(INF, "_StatsCollect: time now=%x, stamp=%x\n", GetTickCount() & 0xffff, dwTimeStamp); #endif dwTimeDiff = (( GetTickCount() & 0xffff ) - dwTimeStamp ) & 0xffff; // it is possible to receive time stamp // with time before the packet was sent, // this is because the client does adjusments to the time stamp // i.e. subtracts the time when the packet was played // catch and ignore this case // if ( dwTimeDiff > 0xf000 ) { dwTimeDiff = 1; } if ( 0 == dwTimeDiff ) dwTimeDiff = 1; if ( 0 == g_dwStatLatency ) g_dwStatLatency = dwTimeDiff; else { // // increase by 30% // g_dwStatLatency = (( 7 * g_dwStatLatency ) + ( 3 * dwTimeDiff )) / 10; } g_dwStatCount ++; } /* * Function: * _StatsSendPing * * Description: * Sends a ping packet to the client * */ VOID _StatSendPing( VOID ) { // // send a ping request // SNDTRAINING SndTraining; SndTraining.Prolog.Type = SNDC_TRAINING; SndTraining.Prolog.BodySize = sizeof( SndTraining ) - sizeof( SndTraining.Prolog ); SndTraining.wTimeStamp = (UINT16)GetTickCount(); SndTraining.wPackSize = 0; if ( INVALID_SOCKET != g_hDGramSocket && 0 != g_dwDGramPort && 0 != g_ulDGramAddress ) { struct sockaddr_in sin; INT rc; sin.sin_family = PF_INET; sin.sin_port = (u_short)g_dwDGramPort; sin.sin_addr.s_addr = g_ulDGramAddress; rc = sendto( g_hDGramSocket, (LPSTR)&SndTraining, sizeof( SndTraining ), 0, (struct sockaddr *)&sin, // to address sizeof(sin) ); if (SOCKET_ERROR == rc) { TRC(ERR, "_StatsSendPing: sendto failed: %d\n", WSAGetLastError()); } } else { BOOL bSuccess; bSuccess = ChannelBlockWrite( &SndTraining, sizeof( SndTraining )); if (!bSuccess) { TRC(ERR, "_StatSendPing: ChannelBlockWrite failed: %d\n", GetLastError()); } } } /* * Function: * _StatsCheckResample * * Description: * Looks in the statistics and eventually changes the current * codec */ BOOL _StatsCheckResample( VOID ) { BOOL rv = FALSE; DWORD dwNewFmt; DWORD dwNewLatency; DWORD dwNewBandwidth; DWORD dwLatDiff; DWORD dwMsPerBlock; DWORD dwBlocksOnTheNet; DWORD dwCurrBandwith; if (( g_dwStatCount % STAT_COUNT ) == STAT_COUNT / 2 ) _StatSendPing(); if ( g_dwStatCount < STAT_COUNT ) goto exitpt; if ( g_dwStatPing >= g_dwStatLatency ) g_dwStatPing = g_dwStatLatency - 1; dwNewLatency = ( g_dwStatLatency - g_dwStatPing / 2 ); if ( 0 == g_dwPacketSize ) { TRC(INF, "_StatsCheckResample: invalid packet size\n"); goto resetpt; } dwNewBandwidth = g_dwPacketSize * 1000 / dwNewLatency; if ( 0 == dwNewBandwidth ) { TRC(INF, "_StatsCheckResample: invalid bandwidth\n"); goto resetpt; } TRC(INF, "_StatsCheckResample: latency=%d, bandwidth=%d\n", dwNewLatency, dwNewBandwidth ); // // g_dwBlocksOnTheNet is the latency in number of blocks // dwMsPerBlock = TSSND_BLOCKSIZE * 1000 / TSSND_NATIVE_AVGBYTESPERSEC; dwBlocksOnTheNet = ((g_dwStatLatency + dwMsPerBlock / 2) / dwMsPerBlock + 2); if ( dwBlocksOnTheNet > TSSND_BLOCKSONTHENET ) { g_dwBlocksOnTheNet = TSSND_BLOCKSONTHENET; } else { g_dwBlocksOnTheNet = dwBlocksOnTheNet; } TRC( INF, "BlocksOnTheNet=%d\n", g_dwBlocksOnTheNet ); // // check for at least 10% difference in the bandwidth // if ( dwNewBandwidth > g_dwMaxBandwidth ) dwNewBandwidth = g_dwMaxBandwidth; if ( dwNewBandwidth < g_dwMinBandwidth ) dwNewBandwidth = g_dwMinBandwidth; dwCurrBandwith = ( NULL != g_ppNegotiatedFormats[ g_dwCurrentFormat ] )? g_ppNegotiatedFormats[ g_dwCurrentFormat ]->nAvgBytesPerSec: g_dwLineBandwidth; if ( dwCurrBandwith > dwNewBandwidth ) dwLatDiff = dwCurrBandwith - dwNewBandwidth; else dwLatDiff = dwNewBandwidth - dwCurrBandwith; if ( dwLatDiff < g_dwCodecChangeThreshold * dwCurrBandwith / 100 ) goto resetpt; // // increment the threshold up to 50% // if ( g_dwCodecChangeThreshold < 50 ) { g_dwCodecChangeThreshold += 5; } // // try to choose another format // dwNewFmt = _VCSndChooseProperFormat( dwNewBandwidth ); if ( (DWORD)-1 != dwNewFmt && dwNewFmt != g_dwCurrentFormat ) { INT step; DWORD dwNextFmt; // // don't jump directly to the new format, just move // towards it // step = ( dwNewFmt > g_dwCurrentFormat )?1:-1; dwNextFmt = g_dwCurrentFormat + step; while( dwNextFmt != dwNewFmt && NULL == g_ppNegotiatedFormats[dwNextFmt] ) { dwNextFmt += step; } dwNewFmt = dwNextFmt; } if ( dwNewFmt == (DWORD)-1 || dwNewFmt == g_dwCurrentFormat ) goto resetpt; TRC(INF, "_StatsCheckResample: new bandwidth=%d resampling\n", dwNewBandwidth); // // resample, NOW // _VCSndCloseConverter(); if ( _VCSndGetACMDriverId( g_ppNegotiatedFormats[dwNewFmt] )) { g_dwLineBandwidth = dwNewBandwidth; g_dwCurrentFormat = dwNewFmt; g_dwDataRemain = 0; } _VCSndOpenConverter(); rv = TRUE; resetpt: g_dwStatLatency = 0; g_dwStatCount = 0; exitpt: return rv; } /* * Function: * _StatReset * * Description: * Resets the statistics * */ VOID _StatReset( VOID ) { g_dwStatLatency = 0; g_dwStatPing = 0; g_dwStatCount = STAT_COUNT_INIT; } /* * Function: * ChannelOpen * * Description: * Opens the virtual channel * * */ BOOL ChannelOpen( VOID ) { BOOL rv = FALSE; if (!g_hVC) g_hVC = WinStationVirtualOpen( NULL, LOGONID_CURRENT, _SNDVC_NAME ); rv = (g_hVC != NULL); return rv; } /* * Function: * ChannelClose * * Description: * Closes the virtual channel */ VOID ChannelClose( VOID ) { if (g_hVC) { CloseHandle(g_hVC); g_hVC = NULL; } g_uiBytesInBuffer = 0; g_uiBufferOffset = 0; } /* * Function: * ChannelBlockWrite * * Description: * Writes a block thru the virtual channel * */ BOOL ChannelBlockWrite( PVOID pBlock, ULONG ulBlockSize ) { BOOL bSuccess = TRUE; PCHAR pData = (PCHAR) pBlock; ULONG ulBytesWritten; ULONG ulBytesToWrite = ulBlockSize; HANDLE hVC; hVC = g_hVC; if (!hVC) { TRC(ERR, "ChannelBlockWrite: vc handle is NULL\n"); bSuccess = FALSE; goto exitpt; } while (bSuccess && ulBytesToWrite) { OVERLAPPED Overlapped; Overlapped.hEvent = NULL; Overlapped.Offset = 0; Overlapped.OffsetHigh = 0; bSuccess = WriteFile( hVC, pData, ulBytesToWrite, &ulBytesWritten, &Overlapped ); if (!bSuccess && ERROR_IO_PENDING == GetLastError()) bSuccess = GetOverlappedResult( hVC, &Overlapped, &ulBytesWritten, TRUE); if (bSuccess) { TRC(ALV, "VirtualChannelWrite: Wrote %d bytes\n", ulBytesWritten); ulBytesToWrite -= ulBytesWritten; pData += ulBytesWritten; } else { TRC(ERR, "VirtualChannelWrite failed, GetLastError=%d\n", GetLastError()); } } exitpt: return bSuccess; } /* * Function: * ChannelMessageWrite * * Description: * Writes a two pieces message as a single one (uses ChannelBlockWrite) * */ BOOL ChannelMessageWrite( PVOID pProlog, ULONG ulPrologSize, PVOID pBody, ULONG ulBodySize ) { BOOL rv = FALSE; if ( 0 != ulBodySize ) { // // create a new prolog message // in which a UINT32 word is added at the end // this word is the same as the first word of the prolog // the client is aware of this and will reconstruct // to the correct messages // PVOID pNewProlog; __try { pNewProlog = alloca( ulPrologSize + sizeof(UINT32) ); } __except((EXCEPTION_STACK_OVERFLOW == GetExceptionCode()) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) { _resetstkoflw(); pNewProlog = NULL; } if ( NULL == pNewProlog ) { TRC(ERR, "ChannelMessageWrite: alloca failed for %d bytes\n", ulPrologSize + sizeof(UINT32) ); goto exitpt; } memcpy(pNewProlog, pProlog, ulPrologSize); // replace the word, put SNDC_NONE in the body // ASSERT( ulBodySize >= sizeof(UINT32)); *(DWORD *)(((LPSTR)pNewProlog) + ulPrologSize) = *(DWORD *)pBody; *(DWORD *)pBody = SNDC_NONE; pProlog = pNewProlog; ulPrologSize += sizeof(UINT32); } rv = ChannelBlockWrite( pProlog, ulPrologSize ); if (!rv) { TRC(ERR, "ChannelMessageWrite: failed while sending the prolog\n"); goto exitpt; } rv = ChannelBlockWrite( pBody, ulBodySize ); if (!rv) { TRC(ERR, "ChannelMessageWrite: failed while sending the body\n"); } exitpt: return rv; } /* * Function: * ChannelBlockRead * * Description: * Read a block, as much as possible * */ BOOL ChannelBlockRead( PVOID pBlock, ULONG ulBlockSize, ULONG *pulBytesRead, ULONG ulTimeout, HANDLE hEvent ) { BOOL bSuccess = FALSE; PCHAR pData = (PCHAR) pBlock; ULONG ulBytesRead = 0; HANDLE hVC; hVC = g_hVC; if (NULL == hVC) { TRC(ERR, "ChannelBlockRead: vc handle is invalid(NULL)\n"); goto exitpt; } if (NULL == pulBytesRead) { TRC(ERR, "ChannelBlockRead: pulBytesRead is NULL\n"); goto exitpt; } if (!g_uiBytesInBuffer) { g_OverlappedRead.hEvent = hEvent; g_OverlappedRead.Offset = 0; g_OverlappedRead.OffsetHigh = 0; bSuccess = ReadFile( hVC, g_Buffer, sizeof(g_Buffer), (LPDWORD) &g_uiBytesInBuffer, &g_OverlappedRead ); if (ERROR_IO_PENDING == GetLastError()) { bSuccess = FALSE; goto exitpt; } if (!bSuccess) { TRC(ERR, "VirtualChannelRead failed, " "GetLastError=%d\n", GetLastError()); g_uiBytesInBuffer = 0; } else { TRC(ALV, "VirtualChannelRead: read %d bytes\n", g_uiBytesInBuffer); SetLastError(ERROR_SUCCESS); } } if (g_uiBytesInBuffer) { ulBytesRead = (g_uiBytesInBuffer < ulBlockSize) ? g_uiBytesInBuffer : ulBlockSize; memcpy(pData, g_Buffer + g_uiBufferOffset, ulBytesRead); g_uiBufferOffset += ulBytesRead; g_uiBytesInBuffer -= ulBytesRead; bSuccess = TRUE; } // if the buffer is completed, zero the offset // if (0 == g_uiBytesInBuffer) g_uiBufferOffset = 0; TRC(ALV, "ChannelBlockRead: block size %d was read\n", ulBlockSize); exitpt: if (NULL != pulBytesRead) *pulBytesRead = ulBytesRead; return bSuccess; } /* * Function: * ChannelBlockReadComplete * * Description: * Read completion * */ BOOL ChannelBlockReadComplete( VOID ) { BOOL bSuccess = FALSE; if (!g_hVC) { TRC(ERR, "ChannelBlockReadComplete: vc handle is invalid(NULL)\n"); goto exitpt; } bSuccess = GetOverlappedResult( g_hVC, &g_OverlappedRead, (LPDWORD) &g_uiBytesInBuffer, FALSE ); if (bSuccess) { TRC(ALV, "VirtualChannelRead: read %d bytes\n", g_uiBytesInBuffer); ; } else { TRC(ERR, "GetOverlappedResult failed, " "GetLastError=%d\n", GetLastError()); } exitpt: return bSuccess; } /* * Function: * ChannelCancelIo * * Description: * Cancel the current IO * */ BOOL ChannelCancelIo( VOID ) { BOOL rv = FALSE; if (!g_hVC) { TRC(ERR, "ChannelCancelIo: vc handle is invalid(NULL)\n"); goto exitpt; } rv = CancelIo(g_hVC); if (rv) SetLastError(ERROR_IO_INCOMPLETE); exitpt: return rv; } /* * Function: * ChannelReceiveMessage * * Description: * Attempts to read two piece message, * returns TRUE if the whole message is received * */ BOOL ChannelReceiveMessage( PSNDMESSAGE pSndMessage, HANDLE hReadEvent ) { BOOL rv = FALSE; HANDLE hVC = g_hVC; UINT uiBytesReceived = 0; ASSERT( NULL != pSndMessage ); ASSERT( NULL != hReadEvent ); if (NULL == hVC) { TRC(ERR, "ChannelReceiveMessage: VC is NULL\n"); goto exitpt; } // // loop until PENDING or message is received // do { if (pSndMessage->uiPrologReceived < sizeof(pSndMessage->Prolog)) { if (ChannelBlockRead( ((LPSTR)(&pSndMessage->Prolog)) + pSndMessage->uiPrologReceived, sizeof(pSndMessage->Prolog) - pSndMessage->uiPrologReceived, (ULONG*) &uiBytesReceived, DEFAULT_VC_TIMEOUT, hReadEvent )) { pSndMessage->uiPrologReceived += uiBytesReceived; } else { if (ERROR_IO_PENDING != GetLastError()) { // Perform cleanup // pSndMessage->uiPrologReceived = 0; } goto exitpt; } } // Reallocate a new body if needed // if (pSndMessage->uiBodyAllocated < pSndMessage->Prolog.BodySize) { PVOID pBody; pBody = (NULL == pSndMessage->pBody)? TSMALLOC(pSndMessage->Prolog.BodySize): TSREALLOC(pSndMessage->pBody, pSndMessage->Prolog.BodySize); if ( NULL == pBody && NULL != pSndMessage->pBody ) { TSFREE( pSndMessage->pBody ); } pSndMessage->pBody = pBody; if (!pSndMessage->pBody) { TRC(ERR, "ChannelMessageRead: can't allocate %d bytes\n", pSndMessage->Prolog.BodySize); pSndMessage->uiBodyAllocated = 0; goto exitpt; } else pSndMessage->uiBodyAllocated = pSndMessage->Prolog.BodySize; } // Receive the body // if (pSndMessage->uiBodyReceived < pSndMessage->Prolog.BodySize) { if (ChannelBlockRead( ((LPSTR)(pSndMessage->pBody)) + pSndMessage->uiBodyReceived, pSndMessage->Prolog.BodySize - pSndMessage->uiBodyReceived, (ULONG*) &uiBytesReceived, DEFAULT_VC_TIMEOUT, hReadEvent )) { pSndMessage->uiBodyReceived += uiBytesReceived; } else { if (ERROR_IO_PENDING != GetLastError()) { // Perform cleanup // pSndMessage->uiPrologReceived = 0; pSndMessage->uiBodyReceived = 0; } goto exitpt; } } // check if the message is received // } while (pSndMessage->uiBodyReceived != pSndMessage->Prolog.BodySize); rv = TRUE; exitpt: return rv; } /* * Function: * VCSndDataArrived * * Description: * Arrived message demultiplexer * */ VOID VCSndDataArrived( PSNDMESSAGE pSndMessage ) { if (pSndMessage->Prolog.BodySize && NULL == pSndMessage->pBody) { TRC(ERR, "_VCSndDataArrived: pBody is NULL\n"); goto exitpt; } // first, get the stream // if (!VCSndAcquireStream()) { TRC(FATAL, "VCSndDataArrived: somebody is holding the " "Stream mutext for too long\n"); ASSERT(0); goto exitpt; } switch (pSndMessage->Prolog.Type) { case SNDC_WAVECONFIRM: { PSNDWAVECONFIRM pSndConfirm; if ( pSndMessage->Prolog.BodySize < sizeof( *pSndConfirm ) - sizeof( SNDPROLOG )) { TRC( ERR, "VCSndDataArrived: Invalid confirmation received\n" ); break; } pSndConfirm = (PSNDWAVECONFIRM) (((LPSTR)pSndMessage->pBody) - sizeof(pSndMessage->Prolog)); _StatsCollect( pSndConfirm->wTimeStamp ); TRC(ALV, "VCSndDataArrived: SNDC_WAVECONFIRM, block no %d\n", pSndConfirm->cConfirmedBlockNo); if ( (BYTE)(g_Stream->cLastBlockSent - pSndConfirm->cConfirmedBlockNo) > TSSND_BLOCKSONTHENET ) { TRC(WRN, "VCSndDataArrived: confirmation for block #%d " "which wasn't sent. Last sent=%d. DROPPING !!!\n", pSndConfirm->cConfirmedBlockNo, g_Stream->cLastBlockSent); break; } if ( (BYTE)(pSndConfirm->cConfirmedBlockNo - g_Stream->cLastBlockConfirmed) < TSSND_BLOCKSONTHENET ) { // move the mark // g_Stream->cLastBlockConfirmed = pSndConfirm->cConfirmedBlockNo + 1; } else { TRC(WRN, "VCSndDataArrived: difference in confirmed blocks " "last=%d, this one=%d\n", g_Stream->cLastBlockConfirmed, pSndConfirm->cConfirmedBlockNo ); } PulseEvent(g_hStreamIsEmptyEvent); } break; case SNDC_TRAINING: { PSNDTRAINING pSndTraining; DWORD dwLatency; if ( pSndMessage->Prolog.BodySize < sizeof ( *pSndTraining ) - sizeof ( pSndTraining->Prolog )) { TRC(ERR, "VCSndDataArrived: SNDC_TRAINING invalid length " "for the body=%d\n", pSndMessage->Prolog.BodySize ); break; } pSndTraining = (PSNDTRAINING) (((LPSTR)pSndMessage->pBody) - sizeof(pSndMessage->Prolog)); if ( 0 != pSndTraining->wPackSize ) { TRC(INF, "VCSndDataArrived: SNDC_TRAINING received (ignoring)\n"); // // these type of messages are handled // in _VCSndLineVCTraining(), bail out // break; } dwLatency = (GetTickCount() & 0xffff) - pSndTraining->wTimeStamp; TRC(INF, "VCSndDataArrived: SNDC_TRAINING Latency=%d\n", dwLatency ); // // increase by 30% // if ( 0 == g_dwStatPing ) g_dwStatPing = dwLatency; else g_dwStatPing = (( 7 * g_dwStatPing ) + ( 3 * dwLatency )) / 10; } break; case SNDC_FORMATS: // // this is handled in VCSndNegotiateWaveFormat() // TRC(INF, "VCSndDataArrived: SNDC_FORMATS reveived (ignoring)\n"); break; default: { TRC(ERR, "_VCSndDataArrived: unknow message received: %d\n", pSndMessage->Prolog.Type); ASSERT(0); } } VCSndReleaseStream(); exitpt: ; } /* * Function: * VCSndAcquireStream * * Description: * Locks the stream * */ BOOL VCSndAcquireStream( VOID ) { BOOL rv = FALSE; DWORD dwres; if (NULL == g_hStream || NULL == g_Stream) { TRC(FATAL, "VCSndAcquireStream: the stream handle is NULL\n"); goto exitpt; } if (NULL == g_hStreamMutex) { TRC(FATAL, "VCSndAcquireStream: the stream mutex is NULL\n"); goto exitpt; } dwres = WaitForSingleObject(g_hStreamMutex, DEFAULT_VC_TIMEOUT); if (WAIT_TIMEOUT == dwres || WAIT_ABANDONED == dwres ) { TRC(ERR, "VCSndAcquireStream: " "timed out waiting for the stream mutex or owner crashed=%d\n", dwres ); // // possible app crash // ASSERT(0); goto exitpt; } rv = TRUE; exitpt: return rv; } /* * Function: * VCSndReleaseStream * * Description: * Release the stream data * */ BOOL VCSndReleaseStream( VOID ) { BOOL rv = TRUE; ASSERT(NULL != g_hStream); ASSERT(NULL != g_Stream); ASSERT(NULL != g_hStreamMutex); if (!ReleaseMutex(g_hStreamMutex)) rv = FALSE; return rv; } /* * Function: * _DGramOpen * * Description: * Opens a datagram socket * */ VOID _DGramOpen( VOID ) { // create a datagram socket if needed // if (INVALID_SOCKET == g_hDGramSocket) { g_hDGramSocket = socket(AF_INET, SOCK_DGRAM, 0); if (INVALID_SOCKET == g_hDGramSocket) TRC(ERR, "_DGramOpen: failed to crate dgram socket: %d\n", WSAGetLastError()); else TRC(ALV, "_DGramOpen: datagram socket created\n"); } // get the max datagram size // if (INVALID_SOCKET != g_hDGramSocket) { UINT optval = 0; UINT optlen = sizeof(optval); getsockopt(g_hDGramSocket, SOL_SOCKET, SO_MAX_MSG_SIZE, (LPSTR)(&optval), (int *) &optlen); TRC(ALV, "_DGramOpen: max allowed datagram: %d\n", optval); optval = (optval < TSSND_BLOCKSIZE)?optval:TSSND_BLOCKSIZE; // align the dgram to DWORD // optval /= sizeof(DWORD); optval *= sizeof(DWORD); if ( optval < RDPSND_MIN_FRAG_SIZE ) { g_dwDGramSize = 0; } else if ( optval < g_dwDGramSize ) { g_dwDGramSize = optval; TRC( INF, "DGram size downgraded to %d\n", g_dwDGramSize ); } TRC(ALV, "_DGramOpen: max datagram size: %d\n", optval); // get client's ip address // { WINSTATIONCLIENT ClientData; ULONG ulReturnLength; BOOL rc; u_long ulDGramClientAddress; rc = WinStationQueryInformation( SERVERNAME_CURRENT, LOGONID_CURRENT, WinStationClient, &ClientData, sizeof(ClientData), &ulReturnLength); if (rc) { g_EncryptionLevel = ClientData.EncryptionLevel; if (PF_INET == ClientData.ClientAddressFamily) { TRC(ALV, "_VCSndSendOpenDevice: client address is: %S\n", ClientData.ClientAddress); ulDGramClientAddress = inet_addrW(ClientData.ClientAddress); if (INADDR_NONE != ulDGramClientAddress) g_ulDGramAddress = ulDGramClientAddress; else TRC(ERR, "_VCSndSendOpenDevice: client address is NONE\n"); } else TRC(ERR, "_VCSndSendOpenDevice: " "Invalid address family: %d\n", ClientData.ClientAddressFamily); } else TRC(ERR, "_VCSndSendOpenDevice: " "WinStationQueryInformation failed. %d\n", GetLastError()); } } } VOID _FillWithGarbage( PVOID pBuff, DWORD dwSize ) { PBYTE pbBuff = (PBYTE)pBuff; for ( ; dwSize; pbBuff++, dwSize-- ) { pbBuff[0] = (BYTE)rand(); } } /* * Function: * _VCSndReadRegistry * * Description: * Reads current options */ VOID _VCSndReadRegistry( VOID ) { DWORD rv = (DWORD) -1; DWORD sysrc; HKEY hkey = NULL; DWORD dwKeyType; DWORD dwKeyLen; WINSTATIONCONFIG config; ULONG Length = 0; DWORD dw; sysrc = RegOpenKeyEx( HKEY_LOCAL_MACHINE, TSSND_REG_MAXBANDWIDTH_KEY, 0, // reserved KEY_READ, &hkey); if ( ERROR_SUCCESS != sysrc ) { TRC(WRN, "_VCSndReadRegistry: " "RegOpenKeyEx failed: %d\n", sysrc ); goto exitpt; } dwKeyType = REG_DWORD; dwKeyLen = sizeof( rv ); sysrc = RegQueryValueEx( hkey, TSSND_REG_MAXBANDWIDTH_VAL, NULL, // reserved &dwKeyType, (LPBYTE)&rv, &dwKeyLen); if ( ERROR_SUCCESS != sysrc ) { TRC(WRN, "_VCSndReadRegistry: " "RegQueryValueEx failed: %d\n", sysrc ); } else { g_dwMaxBandwidth = rv; } sysrc = RegQueryValueEx( hkey, TSSND_REG_MINBANDWIDTH_VAL, NULL, // reserved &dwKeyType, (LPBYTE)&rv, &dwKeyLen); if ( ERROR_SUCCESS != sysrc ) { TRC(ALV, "_VCSndReadRegistry: " "RegQueryValueEx failed: %d\n", sysrc ); } else { g_dwMinBandwidth = rv; } sysrc = RegQueryValueEx( hkey, TSSND_REG_DISABLEDGRAM_VAL, NULL, // reserved &dwKeyType, (LPBYTE)&rv, &dwKeyLen); if ( ERROR_SUCCESS != sysrc ) { TRC(ALV, "_VCSndReadRegistry: " "RegQueryValueEx failed: %d\n", sysrc ); } else { g_dwDisableDGram = rv; } sysrc = RegQueryValueEx( hkey, TSSND_REG_ENABLEMP3_VAL, NULL, // reserved &dwKeyType, (LPBYTE)&rv, &dwKeyLen); if ( ERROR_SUCCESS != sysrc ) { TRC(WRN, "_VCSndReadRegistry: " "RegQueryValueEx failed: %d\n", sysrc ); } else { g_dwEnableMP3Codec = rv; } sysrc = RegQueryValueEx( hkey, TSSND_REG_MAXDGRAM, NULL, &dwKeyType, (LPBYTE)&rv, &dwKeyLen ); if ( ERROR_SUCCESS != sysrc ) { TRC( WRN, "_VCSndReadRegistry: " "RegQueryValueEx failed for \"%s\": %d\n", TSSND_REG_MAXDGRAM, sysrc ); } else { if ( rv < g_dwDGramSize && rv >= RDPSND_MIN_FRAG_SIZE ) { g_dwDGramSize = rv; TRC( INF, "DGram size forced to %d\n", g_dwDGramSize ); } } dwKeyLen = 0; sysrc = RegQueryValueEx( hkey, TSSND_REG_ALLOWCODECS, NULL, &dwKeyType, NULL, &dwKeyLen ); if ( ERROR_MORE_DATA != sysrc || REG_BINARY != dwKeyType ) { TRC( ALV, "_VCSndReadRegistry: " "RegQueryValueEx failed for AllowCodecs: %d\n", sysrc ); } else { if ( NULL != g_AllowCodecs ) TSFREE( g_AllowCodecs ); g_AllowCodecs = (DWORD *)TSMALLOC( dwKeyLen ); if ( NULL == g_AllowCodecs ) { TRC( WRN, "_VCSndReadRegistry: " "malloc failed for %d bytes\n", dwKeyLen ); } else { sysrc = RegQueryValueEx( hkey, TSSND_REG_ALLOWCODECS, NULL, &dwKeyType, (LPBYTE)g_AllowCodecs, &dwKeyLen ); if ( ERROR_SUCCESS != sysrc ) { TRC( WRN, "_VCSndReadRegistry: " "RegQueryValueEx failed: %d\n", sysrc ); TSFREE( g_AllowCodecs ); g_AllowCodecs = NULL; g_AllowCodecsSize = 0; } else { g_AllowCodecsSize = dwKeyLen; } } } exitpt: if ( NULL != hkey ) RegCloseKey( hkey ); } /* * Function: * _VCSndLineVCTraining * * Description: * Meassures the line speed thru the virtual channel * */ DWORD _VCSndLineVCTraining( HANDLE hReadEvent ) { PSNDTRAINING pSndTraining; SNDMESSAGE SndMessage; DWORD dwSuggestedBaudRate; DWORD dwLatency; PSNDTRAINING pSndTrainingResp; memset(&SndMessage, 0, sizeof(SndMessage)); dwLatency = 0; if (NULL == hReadEvent) { TRC(ERR, "_VCSndLineVCTraining: hReadEvent is NULL\n"); goto exitpt; } __try { pSndTraining = (PSNDTRAINING) alloca( TSSND_TRAINING_BLOCKSIZE ); } __except((EXCEPTION_STACK_OVERFLOW == GetExceptionCode()) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) { _resetstkoflw(); pSndTraining = NULL; } if (NULL == pSndTraining) { TRC(ERR, "_VCSndLineVCTraining: can't alloca %d bytes\n", TSSND_TRAINING_BLOCKSIZE); goto exitpt; } _FillWithGarbage( pSndTraining, TSSND_TRAINING_BLOCKSIZE); pSndTraining->Prolog.Type = SNDC_TRAINING; pSndTraining->Prolog.BodySize = TSSND_TRAINING_BLOCKSIZE - sizeof (pSndTraining->Prolog); pSndTraining->wTimeStamp = (UINT16)GetTickCount(); pSndTraining->wPackSize = (UINT16)TSSND_TRAINING_BLOCKSIZE; // // send the packet // if (!ChannelBlockWrite(pSndTraining, TSSND_TRAINING_BLOCKSIZE)) { TRC(ERR, "_VCSndLineVCTraining: failed to send a block: %d\n", GetLastError()); goto exitpt; } // // wait for response to arrive // do { SndMessage.uiPrologReceived = 0; SndMessage.uiBodyReceived = 0; while(!ChannelReceiveMessage(&SndMessage, hReadEvent)) { if (ERROR_IO_PENDING == GetLastError()) { DWORD dwres; HANDLE ahEvents[2]; ahEvents[0] = hReadEvent; ahEvents[1] = g_hDisconnectEvent; dwres = WaitForMultipleObjects( sizeof(ahEvents)/sizeof(ahEvents[0]), // count ahEvents, // events FALSE, // wait all DEFAULT_RESPONSE_TIMEOUT); if (WAIT_TIMEOUT == dwres || WAIT_OBJECT_0 + 1 == dwres) { TRC(WRN, "_VCSndLineVCTraining: timeout " "waiting for response\n"); ChannelCancelIo(); ResetEvent(hReadEvent); goto exitpt; } ChannelBlockReadComplete(); ResetEvent(hReadEvent); } else if (ERROR_SUCCESS != GetLastError()) { TRC(ERR, "_VCSndLineVCTraining: " "ChannelReceiveMessage failed: %d\n", GetLastError()); goto exitpt; } } } while ( SNDC_TRAINING != SndMessage.Prolog.Type || sizeof(SNDTRAINING) - sizeof(SNDPROLOG) < SndMessage.Prolog.BodySize); TRC(ALV, "_VCSndLineVCTraining: response received\n"); pSndTrainingResp = (PSNDTRAINING) (((LPSTR)SndMessage.pBody) - sizeof(SndMessage.Prolog)); // // calculate latency (nonzero) // dwLatency = ((WORD)GetTickCount()) - pSndTrainingResp->wTimeStamp + 1; exitpt: TRC(INF, "_VCSndLineVCTraining: dwLatency = %d\n", dwLatency); if (0 != dwLatency) { // // the latency is in miliseconds, so compute it bytes per seconds // and get nonzero result // dwSuggestedBaudRate = 1 + (1000 * ( pSndTrainingResp->wPackSize + sizeof( *pSndTraining )) / dwLatency); } else dwSuggestedBaudRate = 0; TRC(INF, "_VCSndLineVCTraining: dwSuggestedBaudRate = %d\n", dwSuggestedBaudRate); if (NULL != SndMessage.pBody) TSFREE(SndMessage.pBody); return dwSuggestedBaudRate; } /* * Function: * _VCSndLineDGramTraining * * Description: * Meassures the line speed thru UDP channel * */ DWORD _VCSndLineDGramTraining( HANDLE hDGramEvent ) { PSNDTRAINING pSndTraining; PSNDTRAINING pSndTrainingResp; struct sockaddr_in sin; DWORD dwRetries; DWORD dwSuggestedBaudRate; DWORD dwDGramLatency = 0; INT sendres; DWORD dwPackSize; DWORD dwRespSize; dwDGramLatency = 0; if (NULL == hDGramEvent) { TRC(ERR, "_VCSndLineDGramTraining: hDGramEvent is NULL\n"); goto exitpt; } if (INVALID_SOCKET == g_hDGramSocket || 0 == g_dwDGramPort || g_dwDGramSize < sizeof(*pSndTraining) || 0 == g_ulDGramAddress) { TRC(ERR, "_VCSndLineDGramTraining: no dgram support. Can't train the line\n"); goto exitpt; } dwPackSize = ( g_dwDGramSize < TSSND_TRAINING_BLOCKSIZE )? g_dwDGramSize: TSSND_TRAINING_BLOCKSIZE; __try { pSndTraining = (PSNDTRAINING) alloca( dwPackSize ); } __except((EXCEPTION_STACK_OVERFLOW == GetExceptionCode()) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) { _resetstkoflw(); pSndTraining = NULL; } if (NULL == pSndTraining) { TRC(ERR, "_VCSndLineDGramTraining: can't alloca %d bytes\n", dwPackSize); goto exitpt; } _FillWithGarbage( pSndTraining, dwPackSize ); // // send a block and measure the time when it will arrive // // prepare the to address // sin.sin_family = PF_INET; sin.sin_port = (u_short)g_dwDGramPort; sin.sin_addr.s_addr = g_ulDGramAddress; pSndTraining->Prolog.Type = SNDC_TRAINING; pSndTraining->Prolog.BodySize = (UINT16)( dwPackSize - sizeof (pSndTraining->Prolog)); pSndTraining->wPackSize = (UINT16)TSSND_TRAINING_BLOCKSIZE; dwRetries = 2 * DEFAULT_RESPONSE_TIMEOUT / 1000; do { pSndTraining->wTimeStamp = (WORD)GetTickCount(); // // send the datagram // the type is SNDC_WAVE but the structure is of SNDWAVE // wTimeStamp contains the sending time // sendres = sendto( g_hDGramSocket, (LPSTR)pSndTraining, dwPackSize, 0, // flags (struct sockaddr *)&sin, // to address sizeof(sin) ); if (SOCKET_ERROR == sendres) { TRC(ERR, "_VCSndLineDGramTraining: sendto failed: %d\n", WSAGetLastError()); goto exitpt; } // // wait for a response // do { pSndTrainingResp = NULL; dwRespSize = 0; DGramRead( hDGramEvent, (PVOID*) &pSndTrainingResp, &dwRespSize ); if ( NULL == pSndTrainingResp ) { DWORD dwres; HANDLE ahEvents[2]; ahEvents[0] = hDGramEvent; ahEvents[1] = g_hDisconnectEvent; dwres = WaitForMultipleObjects( sizeof(ahEvents)/sizeof(ahEvents[0]), // count ahEvents, // events FALSE, // wait all 1000); if ( WAIT_OBJECT_0 + 1 == dwres ) { TRC(WRN, "_VCSndLineDGramTraining: disconnected\n"); goto exitpt; } if (WAIT_TIMEOUT == dwres) { TRC(WRN, "_VCSndLineDGramTraining: timeout " "waiting for response\n"); goto try_again; } DGramReadComplete( (PVOID*) &pSndTrainingResp, &dwRespSize ); } } while ( NULL == pSndTrainingResp || sizeof( *pSndTrainingResp ) != dwRespSize || SNDC_TRAINING != pSndTrainingResp->Prolog.Type || sizeof(SNDTRAINING) - sizeof(SNDPROLOG) < pSndTrainingResp->Prolog.BodySize ); TRC(ALV, "_VCSndLineDGramTraining: response received\n"); break; try_again: dwRetries --; } while (0 != dwRetries); if (0 != dwRetries) { // // calculate latency (nonzero) // dwDGramLatency = ((WORD)GetTickCount()) - pSndTrainingResp->wTimeStamp + 1; } exitpt: TRC(INF, "_VCSndLineDGramTraining: dwDGramLatency = %d\n", dwDGramLatency); if (0 != dwDGramLatency) { // // the latency is in miliseconds, so compute it bytes per seconds // and get nonzero result // dwSuggestedBaudRate = 1 + (1000 * ( pSndTrainingResp->wPackSize + sizeof( *pSndTrainingResp )) / dwDGramLatency); } else dwSuggestedBaudRate = 0; TRC(INF, "_VCSndLineDGramTraining: dwSuggestedBaudRate = %d\n", dwSuggestedBaudRate); return dwSuggestedBaudRate; } // // puts code licensing codes into the header // BOOL _VCSndFixHeader( PWAVEFORMATEX pFmt ) { BOOL rv = FALSE; switch (pFmt->wFormatTag) { case WAVE_FORMAT_MSG723: ASSERT(pFmt->cbSize == 10); if ( pFmt->cbSize == 10 ) { ((MSG723WAVEFORMAT *) pFmt)->dwCodeword1 = G723MAGICWORD1; ((MSG723WAVEFORMAT *) pFmt)->dwCodeword2 = G723MAGICWORD2; rv = TRUE; } break; case WAVE_FORMAT_MSRT24: // // assume call control will take care of the other // params ? // ASSERT(pFmt->cbSize == sizeof( VOXACM_WAVEFORMATEX ) - sizeof( WAVEFORMATEX ) ); if ( sizeof( VOXACM_WAVEFORMATEX ) - sizeof( WAVEFORMATEX ) == pFmt->cbSize ) { VOXACM_WAVEFORMATEX *pVOX = (VOXACM_WAVEFORMATEX *)pFmt; ASSERT( strlen( VOXWARE_KEY ) + 1 == sizeof( pVOX->szKey )); strncpy( pVOX->szKey, VOXWARE_KEY, sizeof( pVOX->szKey )); rv = TRUE; } break; // this format eats too much from the CPU // case WAVE_FORMAT_MPEGLAYER3: if ( g_dwEnableMP3Codec ) rv = TRUE; break; case WAVE_FORMAT_WMAUDIO2: if ( g_dwEnableMP3Codec ) { rv = TRUE; } break; default: rv = TRUE; } return rv; } /* * Function: * _VCSndFindSuggestedConverter * * Description: * Searches for intermidiate converter * */ BOOL _VCSndFindSuggestedConverter( HACMDRIVERID hadid, LPWAVEFORMATEX pDestFormat, LPWAVEFORMATEX pInterrimFmt, PFNCONVERTER *ppfnConverter ) { BOOL rv = FALSE; MMRESULT mmres; HACMDRIVER hacmDriver = NULL; PFNCONVERTER pfnConverter = NULL; HACMSTREAM hacmStream = NULL; ASSERT( NULL != pDestFormat ); ASSERT( NULL != hadid ); ASSERT( NULL != pInterrimFmt ); *ppfnConverter = NULL; // // first, open the destination acm driver // mmres = acmDriverOpen(&hacmDriver, hadid, 0); if ( MMSYSERR_NOERROR != mmres ) { TRC(ERR, "_VCSndFindSuggestedConverter: can't " "open the acm driver: %d\n", mmres); goto exitpt; } // // first probe with the native format // if it passes, we don't need intermidiate // format converter // pInterrimFmt->wFormatTag = WAVE_FORMAT_PCM; pInterrimFmt->nChannels = TSSND_NATIVE_CHANNELS; pInterrimFmt->nSamplesPerSec = TSSND_NATIVE_SAMPLERATE; pInterrimFmt->nAvgBytesPerSec = TSSND_NATIVE_AVGBYTESPERSEC; pInterrimFmt->nBlockAlign = TSSND_NATIVE_BLOCKALIGN; pInterrimFmt->wBitsPerSample = TSSND_NATIVE_BITSPERSAMPLE; pInterrimFmt->cbSize = 0; mmres = acmStreamOpen( &hacmStream, hacmDriver, pInterrimFmt, pDestFormat, NULL, // filter 0, // callback 0, // dwinstance ACM_STREAMOPENF_NONREALTIME ); if ( MMSYSERR_NOERROR == mmres ) { // // format is supported // rv = TRUE; goto exitpt; } else { TRC(ALV, "_VCSndFindSuggestedConverter: format is not supported\n"); } // // find a suggested intermidiate PCM format // mmres = acmFormatSuggest( hacmDriver, pDestFormat, pInterrimFmt, sizeof( *pInterrimFmt ), ACM_FORMATSUGGESTF_WFORMATTAG ); if ( MMSYSERR_NOERROR != mmres ) { TRC(ALV, "_VCSndFindSuggestedConverter: can't find " "interrim format: %d\n", mmres); goto exitpt; } if ( 16 != pInterrimFmt->wBitsPerSample || ( 1 != pInterrimFmt->nChannels && 2 != pInterrimFmt->nChannels) || ( 8000 != pInterrimFmt->nSamplesPerSec && 11025 != pInterrimFmt->nSamplesPerSec && 12000 != pInterrimFmt->nSamplesPerSec && 16000 != pInterrimFmt->nSamplesPerSec && 22050 != pInterrimFmt->nSamplesPerSec) ) { TRC(ALV, "_VCSndFindSuggestedConverter: not supported " "interrim format. Details:\n"); TRC(ALV, "Channels - %d\n", pInterrimFmt->nChannels); TRC(ALV, "SamplesPerSec - %d\n", pInterrimFmt->nSamplesPerSec); TRC(ALV, "AvgBytesPerSec - %d\n", pInterrimFmt->nAvgBytesPerSec); TRC(ALV, "BlockAlign - %d\n", pInterrimFmt->nBlockAlign); TRC(ALV, "BitsPerSample - %d\n", pInterrimFmt->wBitsPerSample); goto exitpt; } if ( 1 == pInterrimFmt->nChannels ) { switch ( pInterrimFmt->nSamplesPerSec ) { case 8000: pfnConverter = _Convert8000Mono; break; case 11025: pfnConverter = _Convert11025Mono; break; case 12000: pfnConverter = _Convert12000Mono; break; case 16000: pfnConverter = _Convert16000Mono; break; case 22050: pfnConverter = _Convert22050Mono; break; default: ASSERT( 0 ); } } else { switch ( pInterrimFmt->nSamplesPerSec ) { case 8000: pfnConverter = _Convert8000Stereo; break; case 11025: pfnConverter = _Convert11025Stereo; break; case 12000: pfnConverter = _Convert12000Stereo; break; case 16000: pfnConverter = _Convert16000Stereo; break; case 22050: pfnConverter = NULL; break; default: ASSERT( 0 ); } } // // probe with this format // mmres = acmStreamOpen( &hacmStream, hacmDriver, pInterrimFmt, pDestFormat, NULL, // filter 0, // callback 0, // dwinstance ACM_STREAMOPENF_NONREALTIME ); if ( MMSYSERR_NOERROR != mmres ) { TRC(ALV, "_VCSndFindSuggestedConverter: probing the suggested " "format failed: %d\n", mmres); goto exitpt; } TRC(ALV, "_VCSndFindSuggestedConverter: found intermidiate PCM format\n"); TRC(ALV, "Channels - %d\n", pInterrimFmt->nChannels); TRC(ALV, "SamplesPerSec - %d\n", pInterrimFmt->nSamplesPerSec); TRC(ALV, "AvgBytesPerSec - %d\n", pInterrimFmt->nAvgBytesPerSec); TRC(ALV, "BlockAlign - %d\n", pInterrimFmt->nBlockAlign); TRC(ALV, "BitsPerSample - %d\n", pInterrimFmt->wBitsPerSample); rv = TRUE; exitpt: if ( NULL != hacmStream ) acmStreamClose( hacmStream, 0 ); if ( NULL != hacmDriver ) acmDriverClose( hacmDriver, 0 ); *ppfnConverter = pfnConverter; return rv; } /* * Function: * VCSndEnumAllCodecFormats * * Description: * Creates a list of all codecs/formats * */ BOOL VCSndEnumAllCodecFormats( PVCSNDFORMATLIST *ppFormatList, DWORD *pdwNumberOfFormats ) { BOOL rv = FALSE; PVCSNDFORMATLIST pIter; PVCSNDFORMATLIST pPrev; PVCSNDFORMATLIST pNext; MMRESULT mmres; DWORD dwNum = 0; UINT count, codecsize; ASSERT( ppFormatList ); ASSERT( pdwNumberOfFormats ); *ppFormatList = NULL; // // convert the known format list to a linked list // for ( count = 0, codecsize = 0; count < sizeof( KnownFormats ); count += codecsize ) { PWAVEFORMATEX pSndFmt = (PWAVEFORMATEX)(KnownFormats + count); codecsize = sizeof( WAVEFORMATEX ) + pSndFmt->cbSize; // // skip mp3s if disabled // if (( WAVE_FORMAT_MPEGLAYER3 == pSndFmt->wFormatTag || WAVE_FORMAT_WMAUDIO2 == pSndFmt->wFormatTag ) && !g_dwEnableMP3Codec ) { continue; } UINT entrysize = sizeof( VCSNDFORMATLIST ) + pSndFmt->cbSize; PVCSNDFORMATLIST pNewEntry; pNewEntry = (PVCSNDFORMATLIST) TSMALLOC( entrysize ); if ( NULL != pNewEntry ) { memcpy( &pNewEntry->Format, pSndFmt, codecsize ); pNewEntry->hacmDriverId = NULL; pNewEntry->pNext = *ppFormatList; *ppFormatList = pNewEntry; } } // // additional codecs // these are codecs not included initially, it reads them from the registry // see AllowCodecs initialization // for ( count = 0, codecsize = 0; count < g_AllowCodecsSize ; count += codecsize ) { PWAVEFORMATEX pSndFmt = (PWAVEFORMATEX)(((PBYTE)g_AllowCodecs) + count); codecsize = sizeof( WAVEFORMATEX ) + pSndFmt->cbSize; if ( codecsize + count > g_AllowCodecsSize ) { TRC( ERR, "Invalid size of additional codec\n" ); break; } // // skip mp3s if disabled // if (( WAVE_FORMAT_MPEGLAYER3 == pSndFmt->wFormatTag || WAVE_FORMAT_WMAUDIO2 == pSndFmt->wFormatTag ) && !g_dwEnableMP3Codec ) { continue; } UINT entrysize = sizeof( VCSNDFORMATLIST ) + pSndFmt->cbSize; PVCSNDFORMATLIST pNewEntry; pNewEntry = (PVCSNDFORMATLIST) TSMALLOC( entrysize ); if ( NULL != pNewEntry ) { memcpy( &pNewEntry->Format, pSndFmt, codecsize ); pNewEntry->hacmDriverId = NULL; pNewEntry->pNext = *ppFormatList; *ppFormatList = pNewEntry; } } // // add the native format // pIter = (PVCSNDFORMATLIST) TSMALLOC( sizeof( *pIter ) ); if ( NULL != pIter ) { pIter->Format.wFormatTag = WAVE_FORMAT_PCM; pIter->Format.nChannels = TSSND_NATIVE_CHANNELS; pIter->Format.nSamplesPerSec = TSSND_NATIVE_SAMPLERATE; pIter->Format.nAvgBytesPerSec = TSSND_NATIVE_AVGBYTESPERSEC; pIter->Format.nBlockAlign = TSSND_NATIVE_BLOCKALIGN; pIter->Format.wBitsPerSample = TSSND_NATIVE_BITSPERSAMPLE; pIter->Format.cbSize = 0; pIter->hacmDriverId = NULL; pIter->pNext = *ppFormatList; *ppFormatList = pIter; } if (NULL == *ppFormatList) { TRC(WRN, "VCSndEnumAllCodecFormats: failed to add formats\n"); goto exitpt; } _VCSndOrderFormatList( ppFormatList, &dwNum ); // // number of formats is passed as UINT16, delete all after those // if ( dwNum > 0xffff ) { DWORD dwLimit = 0xfffe; pIter = *ppFormatList; while ( 0 != dwLimit ) { pIter = pIter->pNext; dwLimit --; } pNext = pIter->pNext; pIter->pNext = NULL; pIter = pNext; while( NULL != pIter ) { pNext = pIter->pNext; TSFREE( pNext ); pIter = pNext; } dwNum = 0xffff; } rv = TRUE; exitpt: if (!rv) { // // in case of error free the allocated list of formats // pIter = *ppFormatList; while( NULL != pIter ) { PVCSNDFORMATLIST pNext = pIter->pNext; TSFREE( pIter ); pIter = pNext; } *ppFormatList = NULL; } *pdwNumberOfFormats = dwNum; return rv; } BOOL CALLBACK acmDriverEnumCallbackGetACM( HACMDRIVERID hadid, DWORD_PTR dwInstance, DWORD fdwSupport ) { BOOL rv = TRUE; MMRESULT mmres; ASSERT(dwInstance); ASSERT( NULL != hadid ); if ( (0 != ( fdwSupport & ACMDRIVERDETAILS_SUPPORTF_CODEC ) || 0 != ( fdwSupport & ACMDRIVERDETAILS_SUPPORTF_CONVERTER ))) { // // a codec found // ACMFORMATTAGDETAILS fdwDetails; PVCSNDFORMATLIST pFmt = (PVCSNDFORMATLIST)dwInstance; fdwDetails.cbStruct = sizeof( fdwDetails ); fdwDetails.fdwSupport = 0; fdwDetails.dwFormatTag = pFmt->Format.wFormatTag; mmres = acmFormatTagDetails( (HACMDRIVER)hadid, &fdwDetails, ACM_FORMATTAGDETAILSF_FORMATTAG ); if ( MMSYSERR_NOERROR == mmres ) { WAVEFORMATEX WaveFormat; // dummy parameter PFNCONVERTER pfnConverter; // dummy parameter if ( _VCSndFindSuggestedConverter( (HACMDRIVERID)hadid, &(pFmt->Format), &WaveFormat, &pfnConverter )) { pFmt->hacmDriverId = hadid; rv = FALSE; } } } // // continue to the next driver // return rv; } BOOL _VCSndGetACMDriverId( PSNDFORMATITEM pSndFmt ) { DWORD rv = FALSE; PVCSNDFORMATLIST pIter; // // Find the acm format id // for( pIter = g_pAllCodecsFormatList; NULL != pIter; pIter = pIter->pNext ) { if (pIter->Format.wFormatTag == pSndFmt->wFormatTag && pIter->Format.nChannels == pSndFmt->nChannels && pIter->Format.nSamplesPerSec == pSndFmt->nSamplesPerSec && pIter->Format.nAvgBytesPerSec == pSndFmt->nAvgBytesPerSec && pIter->Format.nBlockAlign == pSndFmt->nBlockAlign && pIter->Format.wBitsPerSample == pSndFmt->wBitsPerSample && pIter->Format.cbSize == pSndFmt->cbSize && 0 == memcmp((&pIter->Format) + 1, pSndFmt + 1, pIter->Format.cbSize)) { // // format is found // DWORD_PTR dwp = (DWORD_PTR)pIter; MMRESULT mmres; if ( NULL != pIter->hacmDriverId ) { // found already rv = TRUE; break; } mmres = acmDriverEnum( acmDriverEnumCallbackGetACM, (DWORD_PTR)dwp, 0 ); if ( MMSYSERR_NOERROR == mmres ) { if ( NULL != pIter->hacmDriverId ) { rv = TRUE; } else { ASSERT( 0 ); } } break; } } return rv; } /* * Function: * _VCSndChooseProperFormat * * Description: * Chooses the closest format to a given bandwidth * */ DWORD _VCSndChooseProperFormat( DWORD dwBandwidth ) { // choose a format from the list // closest to the measured bandwidth // DWORD i; DWORD fmt = (DWORD)-1; DWORD lastgood = (DWORD)-1; if ( NULL == g_ppNegotiatedFormats ) { TRC(ERR, "_VCSndChooseProperFormat: no negotiated formats\n"); goto exitpt; } for( i = 0; i < g_dwNegotiatedFormats; i++ ) { if ( NULL == g_ppNegotiatedFormats[i] ) { continue; } lastgood = i; if ( dwBandwidth != g_dwLineBandwidth ) { // // we are looking for new codec here, make sure we cover at least 90% // of the requested bandwith // if ( g_ppNegotiatedFormats[i]->nAvgBytesPerSec <= dwBandwidth * NEW_CODEC_COVER / 100 ) { fmt = i; break; } } else if ( g_ppNegotiatedFormats[i]->nAvgBytesPerSec <= dwBandwidth ) { fmt = i; break; } } // // get the last format inc case that all format are not // suitable for our bandwidth // if ( (DWORD)-1 == fmt && 0 != g_dwNegotiatedFormats ) { fmt = lastgood; } ASSERT( fmt != (DWORD)-1 ); exitpt: return fmt; } /* * Function: * _VCSndOrderFormatList * * Description: * Order all formats in descendant order * */ VOID _VCSndOrderFormatList( PVCSNDFORMATLIST *ppFormatList, DWORD *pdwNum ) { PVCSNDFORMATLIST pFormatList; PVCSNDFORMATLIST pLessThan; PVCSNDFORMATLIST pPrev; PVCSNDFORMATLIST pNext; PVCSNDFORMATLIST pIter; PVCSNDFORMATLIST pIter2; DWORD dwNum = 0; ASSERT ( NULL != ppFormatList ); pFormatList = *ppFormatList; pLessThan = NULL; // // fill both lists // pIter = pFormatList; while ( NULL != pIter ) { pNext = pIter->pNext; pIter->pNext = NULL; // // descending order // pIter2 = pLessThan; pPrev = NULL; while ( NULL != pIter2 && pIter2->Format.nAvgBytesPerSec > pIter->Format.nAvgBytesPerSec ) { pPrev = pIter2; pIter2 = pIter2->pNext; } pIter->pNext = pIter2; if ( NULL == pPrev ) pLessThan = pIter; else pPrev->pNext = pIter; pIter = pNext; dwNum ++; } *ppFormatList = pLessThan; if ( NULL != pdwNum ) *pdwNum = dwNum; } /* * Function: * _VCSndLineTraining * * Description: * Meassures the line bandwidth * */ BOOL _VCSndLineTraining( HANDLE hReadEvent, HANDLE hDGramEvent ) { BOOL rv = FALSE; DWORD dwLineBandwidth = 0; _DGramOpen(); // // test this line while it's hot // if ( !g_dwDisableDGram ) { dwLineBandwidth = _VCSndLineDGramTraining( hDGramEvent ); } if (0 == dwLineBandwidth || g_dwDisableDGram) { TRC(WRN, "_VCSndLineTraining: no bandwidth trough UDP\n"); g_ulDGramAddress = 0; g_dwDGramPort = 0; g_EncryptionLevel = 0; dwLineBandwidth = _VCSndLineVCTraining( hReadEvent ); if (0 == dwLineBandwidth) { TRC(WRN, "_VCSndLineTraining: no bandwidth " "trough VC either. GIVING up\n"); goto exitpt; } } else { if ( g_wClientVersion == 1 ) g_EncryptionLevel = 0; } // // check for encryption // if ( g_EncryptionLevel >= MIN_ENCRYPT_LEVEL ) { TRC( INF, "Encryption enabled\n" ); if ( TSRNG_GenerateRandomBits( g_EncryptKey, RANDOM_KEY_LENGTH)) { SL_SendKey(); } else { TRC( ERR, "_VCSndLineTraining: failing to generate random numbers. GIVING up\n" ); goto exitpt; } } // // check for limitations // if ((DWORD)-1 != g_dwMaxBandwidth && dwLineBandwidth > g_dwMaxBandwidth ) { dwLineBandwidth = g_dwMaxBandwidth; TRC(INF, "Bandwidth limited up to %d\n", dwLineBandwidth ); } if ( dwLineBandwidth < g_dwMinBandwidth ) { dwLineBandwidth = g_dwMinBandwidth; TRC(INF, "Bandwidth limited to at least %d\n", dwLineBandwidth ); } rv = TRUE; exitpt: g_dwLineBandwidth = dwLineBandwidth; return rv; } /* * Function: * VCSndNegotiateWaveFormat * * Description: * Requests the client for a list of supported formats * */ BOOL VCSndNegotiateWaveFormat( HANDLE hReadEvent, HANDLE hDGramEvent ) { BOOL rv = FALSE; PVCSNDFORMATLIST pIter; BOOL bSuccess; PSNDFORMATMSG pSndFormats; PSNDFORMATMSG pSndResp; PSNDFORMATITEM pSndFmt; SNDMESSAGE SndMessage; DWORD msgsize; DWORD maxsize; DWORD i; DWORD dwNewFmt; DWORD dwSoundCaps = 0; DWORD dwVolume; DWORD dwPitch; DWORD BestChannels; DWORD BestSamplesPerSec; DWORD BestBitsPerSample; DWORD dwPacketSize; BOOL bWMADetected = FALSE; // // clean the previously negotiated format // if (NULL != g_ppNegotiatedFormats) { DWORD i; for ( i = 0; i < g_dwNegotiatedFormats; i++ ) { if ( NULL != g_ppNegotiatedFormats[i] ) TSFREE( g_ppNegotiatedFormats[i] ); } TSFREE( g_ppNegotiatedFormats ); g_ppNegotiatedFormats = NULL; g_dwNegotiatedFormats = 0; g_hacmDriverId = NULL; } memset( &SndMessage, 0, sizeof( SndMessage )); // // get the list of all codec formats // if ( NULL == g_pAllCodecsFormatList ) { bSuccess = VCSndEnumAllCodecFormats( &g_pAllCodecsFormatList, &g_dwAllCodecsNumber ); if (!bSuccess) goto exitpt; } // // create a packet huge enough to hold all formats // msgsize = sizeof( *pSndFormats ) + sizeof( SNDFORMATITEM ) * g_dwAllCodecsNumber; // // calculate the extra data needed by all format // for( maxsize = 0, pIter = g_pAllCodecsFormatList; NULL != pIter; pIter = pIter->pNext ) { msgsize += pIter->Format.cbSize; if (maxsize < pIter->Format.cbSize) maxsize = pIter->Format.cbSize; } __try { pSndFormats = (PSNDFORMATMSG) alloca( msgsize ); } __except((EXCEPTION_STACK_OVERFLOW == GetExceptionCode()) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) { _resetstkoflw(); pSndFormats = NULL; } if ( NULL == pSndFormats ) { TRC(ERR, "VCSndNegotiateWaveFormat: alloca failed for %d bytes\n", msgsize); goto exitpt; } pSndFormats->Prolog.Type = SNDC_FORMATS; pSndFormats->Prolog.BodySize = (UINT16)( msgsize - sizeof( pSndFormats->Prolog )); pSndFormats->wNumberOfFormats = (UINT16)g_dwAllCodecsNumber; pSndFormats->cLastBlockConfirmed = g_Stream->cLastBlockConfirmed; pSndFormats->wVersion = RDPSND_PROTOCOL_VERSION; for ( i = 0, pSndFmt = (PSNDFORMATITEM) (pSndFormats + 1), pIter = g_pAllCodecsFormatList; i < g_dwAllCodecsNumber; i++, pSndFmt = (PSNDFORMATITEM) (((LPSTR)pSndFmt) + sizeof( *pSndFmt ) + pSndFmt->cbSize), pIter = pIter->pNext ) { ASSERT(NULL != pIter); pSndFmt->wFormatTag = pIter->Format.wFormatTag; pSndFmt->nChannels = pIter->Format.nChannels; pSndFmt->nSamplesPerSec = pIter->Format.nSamplesPerSec; pSndFmt->nAvgBytesPerSec = pIter->Format.nAvgBytesPerSec; pSndFmt->nBlockAlign = pIter->Format.nBlockAlign; pSndFmt->wBitsPerSample = pIter->Format.wBitsPerSample; pSndFmt->cbSize = pIter->Format.cbSize; // // copy the rest of the format data // memcpy( pSndFmt + 1, (&pIter->Format) + 1, pSndFmt->cbSize ); } bSuccess = ChannelBlockWrite( pSndFormats, msgsize ); if (!bSuccess) { TRC(ERR, "VCSndNegotiateWaveFormat: ChannelBlockWrite failed: %d\n", GetLastError()); goto exitpt; } do { // // Wait for response with a valid message // SndMessage.uiPrologReceived = 0; SndMessage.uiBodyReceived = 0; while(!ChannelReceiveMessage(&SndMessage, hReadEvent)) { if (ERROR_IO_PENDING == GetLastError()) { DWORD dwres; HANDLE ahEvents[2]; ahEvents[0] = hReadEvent; ahEvents[1] = g_hDisconnectEvent; dwres = WaitForMultipleObjects( sizeof(ahEvents)/sizeof(ahEvents[0]), // count ahEvents, // events FALSE, // wait all DEFAULT_VC_TIMEOUT); if (WAIT_TIMEOUT == dwres || WAIT_OBJECT_0 + 1 == dwres) { TRC(WRN, "VCSndNegotiateWaveFormat: timeout " "waiting for response\n"); ChannelCancelIo(); ResetEvent(hReadEvent); goto exitpt; } ChannelBlockReadComplete(); ResetEvent(hReadEvent); } else if (ERROR_SUCCESS != GetLastError()) { TRC(ERR, "VCSndNegotiateWaveFormat: " "ChannelReceiveMessage failed: %d\n", GetLastError()); goto exitpt; } } } while (SNDC_FORMATS != SndMessage.Prolog.Type); if (SndMessage.Prolog.BodySize < sizeof( SNDFORMATMSG ) - sizeof( SNDPROLOG )) { TRC(ERR, "VCSndNegotiateWaveFormat: SNDC_FORMAT message " "invalid body size: %d\n", SndMessage.Prolog.BodySize ); } pSndResp = (PSNDFORMATMSG) (((LPSTR)SndMessage.pBody) - sizeof( SNDPROLOG )); // save the capabilities // dwSoundCaps = pSndResp->dwFlags; dwVolume = pSndResp->dwVolume; dwPitch = pSndResp->dwPitch; g_dwDGramPort = pSndResp->wDGramPort; g_wClientVersion = pSndResp->wVersion; // // Expect at least one format returned // if (SndMessage.Prolog.BodySize < sizeof( SNDFORMATITEM ) + sizeof( SNDFORMATMSG ) - sizeof( SNDPROLOG )) { TRC(ERR, "VCSndNegotiateWaveFormat: SNDC_FORMAT message " "w/ invalid size (%d). No supported formats\n", SndMessage.Prolog.BodySize ); goto exitpt; } // // train this line // if ( 0 != ( dwSoundCaps & TSSNDCAPS_ALIVE )) { if (!_VCSndLineTraining( hReadEvent, hDGramEvent )) { TRC( WRN, "VCSndIo: can't talk to the client, go silent\n" ); dwSoundCaps = 0; } } // // allocate a new list // g_dwNegotiatedFormats = pSndResp->wNumberOfFormats; g_ppNegotiatedFormats = (PSNDFORMATITEM*) TSMALLOC( sizeof( g_ppNegotiatedFormats[0] ) * g_dwNegotiatedFormats ); memset( g_ppNegotiatedFormats, 0, sizeof( g_ppNegotiatedFormats[0] ) * g_dwNegotiatedFormats ); if ( NULL == g_ppNegotiatedFormats ) { TRC(ERR, "VCSndNegotiateWaveFormat: can't allocate %d bytes\n", sizeof( g_ppNegotiatedFormats[0] ) * g_dwNegotiatedFormats); goto exitpt; } // // allocate space for each entry in the list // for ( i = 0; i < g_dwNegotiatedFormats; i ++ ) { g_ppNegotiatedFormats[i] = (PSNDFORMATITEM) TSMALLOC( sizeof( **g_ppNegotiatedFormats ) + maxsize); if ( NULL == g_ppNegotiatedFormats[i] ) { TRC(ERR, "VCSndNegotiateWaveFormat: can't allocate %d bytes\n", sizeof( **g_ppNegotiatedFormats ) + maxsize ); goto exitpt; } } // // Fill in the global list // pSndFmt = (PSNDFORMATITEM)(pSndResp + 1); dwPacketSize = sizeof( SNDPROLOG ) + SndMessage.Prolog.BodySize - sizeof( *pSndResp ); for( i = 0; i < g_dwNegotiatedFormats; i++) { DWORD adv = sizeof( *pSndFmt ) + pSndFmt->cbSize; if ( adv > dwPacketSize ) { TRC( ERR, "VCSndNegotiateWaveFormat: invalid response packet size\n" ); ASSERT( 0 ); goto exitpt; } if ( pSndFmt->cbSize > maxsize ) { TRC(ERR, "VCSndNegotiateWaveFormat: invalid format size\n" ); ASSERT( 0 ); goto exitpt; } memcpy( g_ppNegotiatedFormats[i], pSndFmt, sizeof( *g_ppNegotiatedFormats[0] ) + pSndFmt->cbSize ); // // advance to the next format // pSndFmt = (PSNDFORMATITEM)(((LPSTR)pSndFmt) + adv); dwPacketSize -= adv; } ASSERT( 0 == dwPacketSize ); // // prune the formats // ie, don't allow 8khZ mono codec to be between // 22kHz and 11kHz stereo // This is the order of imporatnce: frquency, channels, bits, bytes/s // #if DBG TRC( INF, "======== Pruning formats =========. num=%d\n\n", g_dwNegotiatedFormats ); #endif BestSamplesPerSec = 0; BestChannels = 0; BestBitsPerSample = 0; for( i = g_dwNegotiatedFormats; i > 0; i-- ) { PSNDFORMATITEM pDest = g_ppNegotiatedFormats[i - 1]; if ( WAVE_FORMAT_MPEGLAYER3 == pDest->wFormatTag && bWMADetected ) { goto bad_codec; } if ( WAVE_FORMAT_WMAUDIO2 == pDest->wFormatTag ) { bWMADetected = TRUE; } // // a complex check for the order // if ( BestSamplesPerSec > pDest->nSamplesPerSec ) { goto bad_codec; } else if ( BestSamplesPerSec == pDest->nSamplesPerSec ) { if ( BestChannels > pDest->nChannels ) { goto bad_codec; #if 0 } else if ( BestChannels == pDest->nChannels ) { if ( BestBitsPerSample > pDest->wBitsPerSample ) { goto bad_codec; } #endif } } // // good codec, keep it // BestChannels = pDest->nChannels; BestBitsPerSample = pDest->wBitsPerSample; BestSamplesPerSec = pDest->nSamplesPerSec; #if 0 TRC(INF, "GOOD ag=%d chans=%d rate=%d, bps=%d, bpsamp=%d\n", pDest->wFormatTag, pDest->nChannels, pDest->nSamplesPerSec, pDest->nAvgBytesPerSec, pDest->wBitsPerSample ); #endif continue; bad_codec: // // bad codec, delete // #if 0 TRC(INF, "BAD ag=%d chans=%d rate=%d, bps=%d, bpsamp=%d\n", pDest->wFormatTag, pDest->nChannels, pDest->nSamplesPerSec, pDest->nAvgBytesPerSec, pDest->wBitsPerSample ); #endif TSFREE( pDest ); g_ppNegotiatedFormats[i - 1] = NULL; } // // choose the first format as a default // dwNewFmt = _VCSndChooseProperFormat( g_dwLineBandwidth ); if (dwNewFmt != (DWORD) -1) { // // get a valid driver id // _VCSndGetACMDriverId( g_ppNegotiatedFormats[ dwNewFmt ] ); g_dwCurrentFormat = dwNewFmt; // // correct the new bandwidth // g_dwLineBandwidth = g_ppNegotiatedFormats[ dwNewFmt ]->nAvgBytesPerSec; } // // remember the stream settings // if ( 0 != dwSoundCaps ) { // // set the remote sound caps // if (VCSndAcquireStream()) { g_Stream->dwSoundCaps = dwSoundCaps; g_Stream->dwVolume = dwVolume; g_Stream->dwPitch = dwPitch; VCSndReleaseStream(); } } rv = TRUE; exitpt: // don't forget to free the body of the eventually received message // if ( NULL != SndMessage.pBody ) TSFREE( SndMessage.pBody ); // // in case of error cleanup the negotiated format // if ( !rv && NULL != g_ppNegotiatedFormats ) { for ( i = 0; i < g_dwNegotiatedFormats; i++ ) { if ( NULL != g_ppNegotiatedFormats[i] ) TSFREE( g_ppNegotiatedFormats[i] ); } TSFREE( g_ppNegotiatedFormats ); g_ppNegotiatedFormats = NULL; g_dwNegotiatedFormats = 0; g_hacmDriverId = NULL; } return rv; } /* * Function: * _VCSndFindNativeFormat * * Description: * returns the ID of the native format ( no codecs available ) * */ DWORD _VCSndFindNativeFormat( VOID ) { PSNDFORMATITEM pIter; DWORD rv = 0; if ( NULL == g_ppNegotiatedFormats ) { TRC(ERR, "_VCSndFindNativeFormat: no format cache\n"); goto exitpt; } for( rv = 0; rv < g_dwNegotiatedFormats; rv++ ) { pIter = g_ppNegotiatedFormats[ rv ]; if (pIter->wFormatTag == WAVE_FORMAT_PCM && pIter->nChannels == TSSND_NATIVE_CHANNELS && pIter->nSamplesPerSec == TSSND_NATIVE_SAMPLERATE && pIter->nAvgBytesPerSec == TSSND_NATIVE_AVGBYTESPERSEC && pIter->nBlockAlign == TSSND_NATIVE_BLOCKALIGN && pIter->wBitsPerSample == TSSND_NATIVE_BITSPERSAMPLE && pIter->cbSize == 0) // // format is found // break; } ASSERT( rv < g_dwNegotiatedFormats ); exitpt: return rv; } /* * Function: * _VCSndOpenConverter * * Description: * Opens a codec * */ BOOL _VCSndOpenConverter( VOID ) { BOOL rv = FALSE; MMRESULT mmres; WAVEFORMATEX NativeFormat; PSNDFORMATITEM pSndFmt; PVCSNDFORMATLIST pIter; BOOL bSucc; // // assert that these wasn't opened before // ASSERT(NULL == g_hacmDriver); ASSERT(NULL == g_hacmStream); if ( NULL == g_ppNegotiatedFormats ) { TRC(INF, "_VCSndOpenConverter: no acm format specified\n"); goto exitpt; } // // Find the acm format id // pSndFmt = g_ppNegotiatedFormats[ g_dwCurrentFormat ]; for( pIter = g_pAllCodecsFormatList; NULL != pIter; pIter = pIter->pNext ) { if (pIter->Format.wFormatTag == pSndFmt->wFormatTag && pIter->Format.nChannels == pSndFmt->nChannels && pIter->Format.nSamplesPerSec == pSndFmt->nSamplesPerSec && pIter->Format.nAvgBytesPerSec == pSndFmt->nAvgBytesPerSec && pIter->Format.nBlockAlign == pSndFmt->nBlockAlign && pIter->Format.wBitsPerSample == pSndFmt->wBitsPerSample && pIter->Format.cbSize == pSndFmt->cbSize && 0 == memcmp((&pIter->Format) + 1, pSndFmt + 1, pIter->Format.cbSize) ) { // // format is found // g_hacmDriverId = pIter->hacmDriverId; break; } } if (NULL == g_hacmDriverId) { TRC(ERR, "_VCSndOpenConverter: acm driver id was not found\n"); goto exitpt; } TRC(INF, "_VCSndOpenConverter: format received is:\n"); TRC(INF, "FormatTag - %d\n", pSndFmt->wFormatTag); TRC(INF, "Channels - %d\n", pSndFmt->nChannels); TRC(INF, "SamplesPerSec - %d\n", pSndFmt->nSamplesPerSec); TRC(INF, "AvgBytesPerSec - %d\n", pSndFmt->nAvgBytesPerSec); TRC(INF, "BlockAlign - %d\n", pSndFmt->nBlockAlign); TRC(INF, "BitsPerSample - %d\n", pSndFmt->wBitsPerSample); TRC(INF, "cbSize - %d\n", pSndFmt->cbSize); TRC(INF, "acmFormatId - %p\n", g_hacmDriverId); mmres = acmDriverOpen( &g_hacmDriver, g_hacmDriverId, 0 ); if (MMSYSERR_NOERROR != mmres) { TRC(ERR, "_VCSndOpenConverter: unable to open acm driver: %d\n", mmres); goto exitpt; } TRC(ALV, "_VCSndOpenConverter: Driver is open, DriverId = %p\n", g_hacmDriverId); // // first, find a suggested format for this converter // pSndFmt = g_ppNegotiatedFormats[ g_dwCurrentFormat ]; if ( WAVE_FORMAT_PCM == pSndFmt->wFormatTag && TSSND_NATIVE_CHANNELS == pSndFmt->nChannels && TSSND_NATIVE_SAMPLERATE == pSndFmt->nSamplesPerSec && TSSND_NATIVE_AVGBYTESPERSEC == pSndFmt->nAvgBytesPerSec && TSSND_NATIVE_BLOCKALIGN == pSndFmt->nBlockAlign && TSSND_NATIVE_BITSPERSAMPLE == pSndFmt->wBitsPerSample && 0 == pSndFmt->cbSize ) { TRC(INF, "_VCSndOpenConverter: opening native format, no converter\n"); goto exitpt; } bSucc = _VCSndFindSuggestedConverter( g_hacmDriverId, (LPWAVEFORMATEX)pSndFmt, &NativeFormat, &g_pfnConverter); if (!bSucc) { TRC(FATAL, "_VCSndOpenConverter: can't find a suggested format\n"); goto exitpt; } TRC(ALV, "_VCSndOpenConverter: SOURCE format is:\n"); TRC(ALV, "FormatTag - %d\n", NativeFormat.wFormatTag); TRC(ALV, "Channels - %d\n", NativeFormat.nChannels); TRC(ALV, "SamplesPerSec - %d\n", NativeFormat.nSamplesPerSec); TRC(ALV, "AvgBytesPerSec - %d\n", NativeFormat.nAvgBytesPerSec); TRC(ALV, "BlockAlign - %d\n", NativeFormat.nBlockAlign); TRC(ALV, "BitsPerSample - %d\n", NativeFormat.wBitsPerSample); TRC(ALV, "cbSize - %d\n", NativeFormat.cbSize); TRC(ALV, "_VCSndOpenConverter: DESTINATION format is:\n"); TRC(ALV, "FormatTag - %d\n", pSndFmt->wFormatTag); TRC(ALV, "Channels - %d\n", pSndFmt->nChannels); TRC(ALV, "SamplesPerSec - %d\n", pSndFmt->nSamplesPerSec); TRC(ALV, "AvgBytesPerSec - %d\n", pSndFmt->nAvgBytesPerSec); TRC(ALV, "BlockAlign - %d\n", pSndFmt->nBlockAlign); TRC(ALV, "BitsPerSample - %d\n", pSndFmt->wBitsPerSample); TRC(ALV, "cbSize - %d\n", pSndFmt->cbSize); mmres = acmStreamOpen( &g_hacmStream, g_hacmDriver, &NativeFormat, (LPWAVEFORMATEX)pSndFmt, NULL, // no filter 0, // no callback 0, // no callback instance ACM_STREAMOPENF_NONREALTIME ); if (MMSYSERR_NOERROR != mmres) { TRC(ERR, "_VCSndOpenConverter: unable to open acm stream: %d\n", mmres); goto exitpt; } g_dwDataRemain = 0; rv = TRUE; exitpt: if (!rv) { _VCSndCloseConverter(); g_dwCurrentFormat = _VCSndFindNativeFormat(); } return rv; } /* * Function: * _VCSndCloseConverter * * Description: * Closes the codec * */ VOID _VCSndCloseConverter( VOID ) { if (g_hacmStream) { acmStreamClose( g_hacmStream, 0 ); g_hacmStream = NULL; } if (g_hacmDriver) { acmDriverClose( g_hacmDriver, 0 ); g_hacmDriver = NULL; } } /* * Function: * _VCSndConvert * * Description: * Convert a block * */ BOOL _VCSndConvert( PBYTE pSrc, DWORD dwSrcSize, PBYTE pDest, DWORD *pdwDestSize ) { BOOL rv = FALSE; ACMSTREAMHEADER acmStreamHdr; MMRESULT mmres, mmres2; DWORD dwDestSize = 0; DWORD dwSrcBlockAlign = 0; DWORD dwNewDestSize = 0; PBYTE pbRemDst; DWORD dwRemDstLength; ASSERT( NULL != g_hacmStream ); ASSERT( NULL != pdwDestSize ); // // check if we have interrim converter // use it, if so // if ( NULL != g_pfnConverter ) { DWORD dwInterrimSize; g_pfnConverter( (INT16 *)pSrc, dwSrcSize, &dwInterrimSize ); dwSrcSize = dwInterrimSize; } // // compute the destination size // mmres = acmStreamSize( g_hacmStream, dwSrcSize, &dwNewDestSize, ACM_STREAMSIZEF_SOURCE ); if ( MMSYSERR_NOERROR != mmres ) { TRC(ERR, "_VCSndConvert: acmStreamSize failed: %d\n", mmres); g_dwDataRemain = 0; goto go_convert; } // // align the source to a block of the destination // the remainder put in a buffer for consequentive use // mmres = acmStreamSize( g_hacmStream, g_ppNegotiatedFormats[ g_dwCurrentFormat ]->nBlockAlign, &dwSrcBlockAlign, ACM_STREAMSIZEF_DESTINATION ); if ( MMSYSERR_NOERROR != mmres ) { TRC(ALV, "_VCSndConvert: acmStreamSize failed for dst len: %d\n", mmres); g_dwDataRemain = 0; goto go_convert; } dwNewDestSize += g_ppNegotiatedFormats[ g_dwCurrentFormat ]->nBlockAlign; if ( dwNewDestSize > *pdwDestSize ) { TRC( FATAL, "_VCSndConvert: dest size(%d) " "bigger than passed buffer(%d)\n", dwNewDestSize, *pdwDestSize); goto exitpt; } *pdwDestSize = dwNewDestSize; if ( dwSrcBlockAlign <= g_dwDataRemain ) g_dwDataRemain = 0; go_convert: // // prepare the acm stream header // memset( &acmStreamHdr, 0, sizeof( acmStreamHdr )); acmStreamHdr.cbStruct = sizeof( acmStreamHdr ); acmStreamHdr.pbDst = pDest; acmStreamHdr.cbDstLength = *pdwDestSize; // // first convert the remainding data from the previous call // add the data needed to complete one block // if ( 0 != g_dwDataRemain) { DWORD dwDataToCopy = dwSrcBlockAlign - g_dwDataRemain; memcpy( g_pCnvPrevData + g_dwDataRemain, pSrc, dwDataToCopy); pSrc += dwDataToCopy; ASSERT( dwSrcSize > dwDataToCopy ); dwSrcSize -= dwDataToCopy; acmStreamHdr.pbSrc = g_pCnvPrevData; acmStreamHdr.cbSrcLength = dwSrcBlockAlign; mmres = acmStreamPrepareHeader( g_hacmStream, &acmStreamHdr, 0 ); if ( MMSYSERR_NOERROR != mmres ) { TRC(ERR, "_VCSndConvert: acmStreamPrepareHeader failed: %d\n", mmres); goto exitpt; } mmres = acmStreamConvert( g_hacmStream, &acmStreamHdr, ACM_STREAMCONVERTF_BLOCKALIGN ); mmres2 = acmStreamUnprepareHeader( g_hacmStream, &acmStreamHdr, 0 ); ASSERT( mmres == MMSYSERR_NOERROR ); if ( MMSYSERR_NOERROR != mmres ) { TRC(ERR, "_VCSndConvert: acmStreamConvert failed: %d\n", mmres ); } else { dwDestSize += acmStreamHdr.cbDstLengthUsed; acmStreamHdr.cbSrcLengthUsed= 0; acmStreamHdr.pbDst += acmStreamHdr.cbDstLengthUsed; acmStreamHdr.cbDstLength -= acmStreamHdr.cbDstLengthUsed; acmStreamHdr.cbDstLengthUsed= 0; } } // // if we don't have fully aligned block // skip this conversion, but don't forget to save // this block // if (dwSrcSize < dwSrcBlockAlign) { g_dwDataRemain = dwSrcSize; memcpy( g_pCnvPrevData, pSrc, g_dwDataRemain ); rv = TRUE; goto exitpt; } pbRemDst = acmStreamHdr.pbDst; dwRemDstLength = acmStreamHdr.cbDstLength; acmStreamHdr.pbSrc = pSrc; acmStreamHdr.cbSrcLength = dwSrcSize; acmStreamHdr.fdwStatus = 0; mmres = acmStreamPrepareHeader( g_hacmStream, &acmStreamHdr, 0 ); if ( MMSYSERR_NOERROR != mmres ) { TRC(ERR, "_VCSndConvert: can't prepare header: %d\n", mmres); goto exitpt; } while (acmStreamHdr.cbSrcLength > dwSrcBlockAlign) { mmres = acmStreamConvert( g_hacmStream, &acmStreamHdr, ACM_STREAMCONVERTF_BLOCKALIGN ); if ( MMSYSERR_NOERROR != mmres ) { TRC(ERR, "_VCSndConvert: acmStreamConvert failed: %d\n", mmres); goto exitpt; } // // advance the buffer positions // acmStreamHdr.pbSrc += acmStreamHdr.cbSrcLengthUsed; acmStreamHdr.pbDst += acmStreamHdr.cbDstLengthUsed; acmStreamHdr.cbSrcLength -= acmStreamHdr.cbSrcLengthUsed; acmStreamHdr.cbDstLength -= acmStreamHdr.cbDstLengthUsed; dwDestSize += acmStreamHdr.cbDstLengthUsed; acmStreamHdr.cbSrcLengthUsed= 0; acmStreamHdr.cbDstLengthUsed= 0; } rv = TRUE; // // save the unaligned data // if ( 0 != dwSrcBlockAlign ) { g_dwDataRemain = acmStreamHdr.cbSrcLength; memcpy( g_pCnvPrevData, acmStreamHdr.pbSrc, g_dwDataRemain ); } // // restore the header and unprepare // acmStreamHdr.pbSrc = pSrc; acmStreamHdr.pbDst = pbRemDst; acmStreamHdr.cbSrcLength = dwSrcSize; acmStreamHdr.cbSrcLengthUsed= 0; acmStreamHdr.cbDstLength = dwRemDstLength; acmStreamHdr.cbDstLengthUsed= 0; mmres = acmStreamUnprepareHeader( g_hacmStream, &acmStreamHdr, 0 ); ASSERT( mmres == MMSYSERR_NOERROR ); exitpt: *pdwDestSize = dwDestSize; return rv; } /* * Function: * _VCSndCheckDevice * * Description: * Initializes capabilities negotiations with the client * */ VOID _VCSndCheckDevice( HANDLE hReadEvent, HANDLE hDGramEvent ) { SNDPROLOG Prolog; BOOL bSuccess; // // no sound caps yet // g_Stream->dwSoundCaps = 0; // // find the best compression for this audio line // if ( !VCSndNegotiateWaveFormat( hReadEvent, hDGramEvent )) { ChannelClose(); } } /* * Function: * _VCSndCloseDevice * * Description: * Closes the remote device * */ VOID _VCSndCloseDevice( VOID ) { if (!VCSndAcquireStream()) { TRC(FATAL, "_VCSndCloseDevice: Can't acquire stream mutex. exit\n"); goto exitpt; } // disable the local audio // g_Stream->dwSoundCaps = 0; g_bDeviceOpened = FALSE; g_dwLineBandwidth = 0; VCSndReleaseStream(); exitpt: ; } /* * Function: * _VCSndSendWave * * Description: * Sends a wave data to the client using UDP * */ BOOL _VCSndSendWaveDGram( BYTE cBlockNo, PVOID pData, DWORD dwDataSize ) { BOOL bSucc = FALSE; struct sockaddr_in sin; INT sendres; PSNDWAVE pSndWave; // // encrypt the packet if necessary // if ( g_EncryptionLevel >= MIN_ENCRYPT_LEVEL ) if ( !SL_Encrypt( (PBYTE)pData, ( g_HiBlockNo << 8 ) + cBlockNo, dwDataSize )) goto exitpt; __try { pSndWave = (PSNDWAVE) alloca(g_dwDGramSize); } __except((EXCEPTION_STACK_OVERFLOW == GetExceptionCode()) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) { _resetstkoflw(); pSndWave = NULL; TRC(ERR, "_VCSndSendWaveDGram: alloca generate exception: %d\n", GetExceptionCode()); } if (NULL == pSndWave) goto exitpt; pSndWave->Prolog.Type = (g_EncryptionLevel >= MIN_ENCRYPT_LEVEL)?SNDC_WAVEENCRYPT:SNDC_WAVE; pSndWave->wFormatNo = (UINT16)g_dwCurrentFormat; pSndWave->wTimeStamp = (UINT16)GetTickCount(); pSndWave->dwBlockNo = (g_HiBlockNo << 8) + cBlockNo; // prepare the to address // sin.sin_family = PF_INET; sin.sin_port = (u_short)g_dwDGramPort; sin.sin_addr.s_addr = g_ulDGramAddress; // Send the block in chunks of dwDGramSize // while (dwDataSize) { DWORD dwWaveDataLen; dwWaveDataLen = (dwDataSize + sizeof(*pSndWave) < g_dwDGramSize) ? dwDataSize: g_dwDGramSize - sizeof(*pSndWave); pSndWave->Prolog.BodySize = (UINT16) ( sizeof(*pSndWave) - sizeof(pSndWave->Prolog) + dwWaveDataLen ); memcpy(pSndWave + 1, pData, dwWaveDataLen); sendres = sendto(g_hDGramSocket, (LPSTR)pSndWave, sizeof(*pSndWave) + dwWaveDataLen, 0, // flags (struct sockaddr *)&sin, // to address sizeof(sin)); if (SOCKET_ERROR == sendres) { TRC(ERR, "_VCSndSendWaveDGram: sendto failed: %d\n", WSAGetLastError()); goto exitpt; } g_dwPacketSize = sizeof(*pSndWave) + dwWaveDataLen; dwDataSize -= dwWaveDataLen; pData = ((LPSTR)(pData)) + dwWaveDataLen; } bSucc = TRUE; exitpt: return bSucc; } BOOL _VCSndSendWaveDGramInFrags( BYTE cBlockNo, PVOID pData, DWORD dwDataSize ) { BOOL bSucc = FALSE; struct sockaddr_in sin; INT sendres; PSNDUDPWAVE pWave; PSNDUDPWAVELAST pLast; PBYTE pSource; PBYTE pEnd; DWORD dwFragSize; DWORD dwNumFrags; DWORD count; DWORD dwSize; PVOID pBuffer; UINT16 wStartTime; ASSERT( CanUDPFragment( g_wClientVersion ) && dwDataSize <= 0x8000 + RDPSND_SIGNATURE_SIZE ); __try { pBuffer = _alloca( g_dwDGramSize ); } __except((EXCEPTION_STACK_OVERFLOW == GetExceptionCode()) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) { _resetstkoflw(); pBuffer = 0; } if ( NULL == pBuffer ) { goto exitpt; } // // calculate number of frags etc // dwFragSize = g_dwDGramSize - sizeof( *pLast ); dwNumFrags = dwDataSize / dwFragSize; pSource = (PBYTE)pData; pEnd = pSource + dwDataSize; wStartTime = (UINT16)GetTickCount(); // prepare the to address // sin.sin_family = PF_INET; sin.sin_port = (u_short)g_dwDGramPort; sin.sin_addr.s_addr = g_ulDGramAddress; // // make sure we can fit all the frag counters // ASSERT( dwNumFrags < 0x7fff ); if ( 0 != dwNumFrags ) { pWave = (PSNDUDPWAVE)pBuffer; pWave->Type = SNDC_UDPWAVE; pWave->cBlockNo = cBlockNo; for( count = 0; count < dwNumFrags; count++ ) { PBYTE pDest = (PBYTE)(&pWave->cFragNo); dwSize = sizeof( *pWave ) + dwFragSize; if ( count >= RDPSND_FRAGNO_EXT ) { *pDest = (BYTE)(((count >> 8) & (~RDPSND_FRAGNO_EXT)) | RDPSND_FRAGNO_EXT); pDest++; *pDest = (BYTE)(count & 0xff); dwSize++; } else { *pDest = (BYTE)count; } pDest++; memcpy( pDest, pSource, dwFragSize ); sendres = sendto( g_hDGramSocket, (LPSTR)pWave, dwSize, 0, // flags (struct sockaddr *)&sin, // to address sizeof(sin)); if (SOCKET_ERROR == sendres) { TRC(ERR, "_VCSndSendWaveDGramInFrags: sendto failed: %d\n", WSAGetLastError()); goto exitpt; } pSource += dwFragSize; } } ASSERT( pSource <= pEnd ); // // send the last fragment with all the extra info // pLast = (PSNDUDPWAVELAST)pBuffer; pLast->Type = SNDC_UDPWAVELAST; pLast->wTotalSize = (UINT16)dwDataSize; pLast->wTimeStamp = wStartTime; pLast->wFormatNo = (UINT16)g_dwCurrentFormat; pLast->dwBlockNo = (g_HiBlockNo << 8) + cBlockNo; dwSize = PtrToLong( (PVOID)( pEnd - pSource )); memcpy( pLast + 1, pSource, dwSize ); sendres = sendto( g_hDGramSocket, (LPSTR)pLast, dwSize + sizeof( *pLast ), 0, (struct sockaddr *)&sin, sizeof(sin) ); if (SOCKET_ERROR == sendres) { TRC(ERR, "_VCSndSendWaveDGramInFrags: sendto failed: %d\n", WSAGetLastError()); goto exitpt; } g_dwPacketSize = dwNumFrags * sizeof( *pWave ) + sizeof( *pLast ) + dwDataSize; bSucc = TRUE; exitpt: return bSucc; } BOOL _VCSndSendWaveVC( BYTE cBlockNo, PVOID pData, DWORD dwDataSize ) { BOOL bSucc = FALSE; SNDWAVE Wave; // // send this block through the virtual channel // TRC(ALV, "_VCSndSendWave: sending through VC\n"); Wave.Prolog.Type = SNDC_WAVE; Wave.cBlockNo = cBlockNo; Wave.wFormatNo = (UINT16)g_dwCurrentFormat; Wave.wTimeStamp = (UINT16)GetTickCount(); Wave.Prolog.BodySize = (UINT16)( sizeof(Wave) - sizeof(Wave.Prolog) + dwDataSize ); bSucc = ChannelMessageWrite( &Wave, sizeof(Wave), pData, dwDataSize ); g_dwPacketSize = sizeof(Wave) + Wave.Prolog.BodySize; if (!bSucc) { TRC(ERR, "_VCSndSendWave: failed to send wave: %d\n", GetLastError()); } return bSucc; } /* * Function: * _VCSndSendWave * * Description: * Sends a wave data to the client * */ BOOL _VCSndSendWave( BYTE cBlockNo, PVOID pData, DWORD dwDataSize ) { BOOL bSucc = FALSE; static BYTE s_pDest[ TSSND_BLOCKSIZE + RDPSND_SIGNATURE_SIZE ]; PBYTE pDest = s_pDest + RDPSND_SIGNATURE_SIZE; #if _SIM_RESAMPLE // // resample randomly // if ( NULL != g_ppNegotiatedFormats && 0 == (cBlockNo % 32) ) { _VCSndCloseConverter(); g_dwCurrentFormat = rand() % g_dwNegotiatedFormats; g_dwDataRemain = 0; _VCSndOpenConverter(); } #endif _StatsCheckResample(); if ( NULL != g_hacmStream ) { DWORD dwDestSize = TSSND_BLOCKSIZE; if (!_VCSndConvert( (PBYTE) pData, dwDataSize, pDest, &dwDestSize )) { TRC(ERR, "_VCSndSendWave: conversion failed\n"); goto exitpt; } else { pData = pDest; dwDataSize = dwDestSize; } } else { // // no conversion // use the data as it is // copy it in the s_pDest buffer // ASSERT( dwDataSize <= TSSND_BLOCKSIZE ); memcpy( pDest, pData, dwDataSize ); } if ( INVALID_SOCKET != g_hDGramSocket && 0 != g_dwDGramPort && 0 != g_dwDGramSize && 0 != g_ulDGramAddress && 0 != g_dwLineBandwidth // if this is 0, we don't have UDP ) { // Send a datagram // if ( !CanUDPFragment( g_wClientVersion ) && dwDataSize + sizeof( SNDWAVE ) + RDPSND_SIGNATURE_SIZE > g_dwDGramSize ) { // // if the wave doesn't fit in the UDP packet use VCs // bSucc = _VCSndSendWaveVC( cBlockNo, pData, dwDataSize ); } else { // // add signature if necessary // if ( IsDGramWaveSigned( g_wClientVersion )) { if ( !IsDGramWaveAudioSigned( g_wClientVersion )) { SL_Signature( s_pDest, (g_HiBlockNo << 8) + cBlockNo ); } else { SL_AudioSignature( s_pDest, (g_HiBlockNo << 8) + cBlockNo, (PBYTE)pData, dwDataSize ); } pData = s_pDest; dwDataSize += RDPSND_SIGNATURE_SIZE; } if ( CanUDPFragment( g_wClientVersion ) && dwDataSize + sizeof( SNDWAVE ) > g_dwDGramSize ) { bSucc = _VCSndSendWaveDGramInFrags( cBlockNo, pData, dwDataSize ); } else { bSucc = _VCSndSendWaveDGram( cBlockNo, pData, dwDataSize ); } } } else { bSucc = _VCSndSendWaveVC( cBlockNo, pData, dwDataSize ); } TRC(ALV, "_VCSndSendWave: BlockNo: %d sent\n", cBlockNo); exitpt: return bSucc; } INT WSInit( VOID ) { WORD versionRequested; WSADATA wsaData; int intRC; versionRequested = MAKEWORD(1, 1); intRC = WSAStartup(versionRequested, &wsaData); if (intRC != 0) { TRC(ERR, "Failed to initialize WinSock rc:%d\n", intRC); } return intRC; } /* * Function: * DGramRead * * Description: * Reads an UDP message (datagram) * */ VOID DGramRead( HANDLE hDGramEvent, PVOID *ppBuff, DWORD *pdwSize ) { DWORD dwRecvd; DWORD dwFlags; INT rc; if ( INVALID_SOCKET == g_hDGramSocket ) goto exitpt; ASSERT( NULL != hDGramEvent ); do { memset(&g_WSAOverlapped, 0, sizeof(g_WSAOverlapped)); g_WSAOverlapped.hEvent = hDGramEvent; dwRecvd = 0; dwFlags = 0; g_wsabuf.len = sizeof( g_pDGramRecvData ); g_wsabuf.buf = (char *) g_pDGramRecvData; rc = WSARecvFrom( g_hDGramSocket, &g_wsabuf, 1, &dwRecvd, &dwFlags, NULL, // no from address NULL, &g_WSAOverlapped, NULL); // no completion routine if ( 0 == rc ) { // // data received // SNDMESSAGE SndMsg; if ( NULL != ppBuff && NULL != pdwSize ) { // // pass the data to the caller // *ppBuff = g_pDGramRecvData; *pdwSize = dwRecvd; goto exitpt; } if ( dwRecvd < sizeof( SNDPROLOG )) { TRC(WRN, "DGramRead: invalid message received: len=%d\n", dwRecvd ); continue; } memcpy( &SndMsg.Prolog, g_pDGramRecvData, sizeof( SNDPROLOG )); SndMsg.pBody = g_pDGramRecvData + sizeof( SNDPROLOG ); TRC(ALV, "DGramRead: data received\n"); // parse this packet // VCSndDataArrived( &SndMsg ); } } while ( SOCKET_ERROR != rc ); exitpt: ; } /* * Function: * DGramReadCompletion * * Description: * Datagram read completion * */ VOID DGramReadComplete( PVOID *ppBuff, DWORD *pdwSize ) { BOOL rc; SNDMESSAGE SndMsg; DWORD dwFlags = 0; DWORD dwRecvd = 0; ASSERT( INVALID_SOCKET != g_hDGramSocket ); rc = WSAGetOverlappedResult( g_hDGramSocket, &g_WSAOverlapped, &dwRecvd, FALSE, &dwFlags ); if ( !rc ) { TRC(ERR, "DGramReadComplete: WSAGetOverlappedResult failed=%d\n", WSAGetLastError()); goto exitpt; } // // data received // if ( dwRecvd < sizeof( SNDPROLOG )) { TRC(WRN, "DGramReadComplete: invalid message received: len=%d\n", dwRecvd ); goto exitpt; } if ( NULL != ppBuff && NULL != pdwSize ) { // // pass the data to the caller // *ppBuff = g_pDGramRecvData; *pdwSize = dwRecvd; goto exitpt; } memcpy( &SndMsg.Prolog, g_pDGramRecvData, sizeof( SNDPROLOG )); SndMsg.pBody = g_pDGramRecvData + sizeof( SNDPROLOG ); TRC(ALV, "DGramReadComplete: data received\n"); // parse this packet // VCSndDataArrived( &SndMsg ); exitpt: ; } /* * Add an ACE to object security descriptor */ DWORD AddAceToObjectsSecurityDescriptor ( HANDLE hObject, // handle to object SE_OBJECT_TYPE ObjectType, // type of object LPTSTR pszTrustee, // trustee for new ACE TRUSTEE_FORM TrusteeForm, // format of TRUSTEE structure DWORD dwAccessRights, // access mask for new ACE ACCESS_MODE AccessMode, // type of ACE DWORD dwInheritance // inheritance flags for new ACE ) { DWORD dwRes; PACL pOldDACL = NULL, pNewDACL = NULL; PSECURITY_DESCRIPTOR pSD = NULL; EXPLICIT_ACCESS ea; if (NULL == hObject) return ERROR_INVALID_PARAMETER; // Get a pointer to the existing DACL. dwRes = GetSecurityInfo(hObject, ObjectType, DACL_SECURITY_INFORMATION, NULL, NULL, &pOldDACL, NULL, &pSD); if (ERROR_SUCCESS != dwRes) { TRC( ERR, "GetSecurityInfo Error %u\n", dwRes ); goto exitpt; } // Initialize an EXPLICIT_ACCESS structure for the new ACE. ZeroMemory(&ea, sizeof(EXPLICIT_ACCESS)); ea.grfAccessPermissions = dwAccessRights; ea.grfAccessMode = AccessMode; ea.grfInheritance= dwInheritance; ea.Trustee.TrusteeForm = TrusteeForm; ea.Trustee.ptstrName = pszTrustee; // Create a new ACL that merges the new ACE // into the existing DACL. dwRes = SetEntriesInAcl(1, &ea, pOldDACL, &pNewDACL); if (ERROR_SUCCESS != dwRes) { TRC( ERR, "SetEntriesInAcl Error %u\n", dwRes ); goto exitpt; } // Attach the new ACL as the object's DACL. dwRes = SetSecurityInfo(hObject, ObjectType, DACL_SECURITY_INFORMATION, NULL, NULL, pNewDACL, NULL); if (ERROR_SUCCESS != dwRes) { TRC( ERR, "SetSecurityInfo Error %u\n", dwRes ); goto exitpt; } exitpt: if(pSD != NULL) LocalFree((HLOCAL) pSD); if(pNewDACL != NULL) LocalFree((HLOCAL) pNewDACL); return dwRes; } /* * Add "system" account with full control over this handle * */ BOOL _ObjectAllowSystem( HANDLE h ) { BOOL rv = FALSE; PSID pSidSystem; SID_IDENTIFIER_AUTHORITY AuthorityNt = SECURITY_NT_AUTHORITY; DWORD dw; if (!AllocateAndInitializeSid(&AuthorityNt, 1, SECURITY_LOCAL_SYSTEM_RID, 0, 0, 0, 0, 0, 0, 0, &pSidSystem)) { TRC( ERR, "AllocateAndInitializeSid failed: %d\n", GetLastError() ); goto exitpt; } ASSERT(IsValidSid(pSidSystem)); dw = AddAceToObjectsSecurityDescriptor ( h, // handle to object SE_KERNEL_OBJECT, // type of object (LPTSTR)pSidSystem, // trustee for new ACE TRUSTEE_IS_SID, // format of TRUSTEE structure GENERIC_ALL, // access mask for new ACE GRANT_ACCESS, // type of ACE 0 // inheritance flags for new ACE ); if ( ERROR_SUCCESS != dw ) { TRC( ERR, "AddAceToObjectsSecurityDescriptor failed=%d\n", dw ); goto exitpt; } rv = TRUE; exitpt: return rv; } VOID _SignalInitializeDone( VOID ) { HANDLE hInitEvent = OpenEvent( EVENT_MODIFY_STATE, FALSE, TSSND_WAITTOINIT ); g_Stream->dwSoundCaps |= TSSNDCAPS_INITIALIZED; if ( NULL != hInitEvent ) { PulseEvent( hInitEvent ); CloseHandle( hInitEvent ); } TRC( INF, "Audio host is ready!\n" ); } /* * Function: * VCSndIoThread * * Description: * Main entry pint for this thread * */ INT WINAPI VCSndIoThread( PVOID pParam ) { HANDLE ahEvents[TOTAL_EVENTS]; HANDLE hReadEvent = NULL; HANDLE hDGramEvent = NULL; SNDMESSAGE SndMessage; DWORD dwres; ULONG logonId; HANDLE hReconnectEvent = NULL; WCHAR szEvName[64]; BYTE i; _VCSndReadRegistry(); memset (&SndMessage, 0, sizeof(SndMessage)); WSInit(); // create the global/local events // g_hDataReadyEvent = CreateEvent(NULL, FALSE, FALSE, TSSND_DATAREADYEVENT); g_hStreamIsEmptyEvent = CreateEvent(NULL, FALSE, TRUE, TSSND_STREAMISEMPTYEVENT); g_hStreamMutex = CreateMutex(NULL, FALSE, TSSND_STREAMMUTEX); hReadEvent = CreateEvent(NULL, TRUE, FALSE, NULL); hDGramEvent = WSACreateEvent(); if (NULL == g_hDataReadyEvent || NULL == g_hStreamIsEmptyEvent || NULL == g_hStreamMutex || NULL == hReadEvent || NULL == hDGramEvent) { TRC(FATAL, "VCSndIoThread: no events\n"); goto exitpt; } // adjust privileges on the events // if (!_ObjectAllowSystem( g_hDataReadyEvent )) goto exitpt; if (!_ObjectAllowSystem( g_hStreamIsEmptyEvent )) goto exitpt; if (!_ObjectAllowSystem( g_hStreamMutex )) goto exitpt; // create the stream // g_hStream = CreateFileMapping( INVALID_HANDLE_VALUE, //PG.SYS NULL, // security PAGE_READWRITE, 0, // Size high sizeof(*g_Stream), // Size low TSSND_STREAMNAME // mapping name ); if (NULL == g_hStream) { TRC(FATAL, "DllInstanceInit: failed to create mapping: %d\n", GetLastError()); goto exitpt; } if (!_ObjectAllowSystem( g_hStream )) goto exitpt; g_Stream = (PSNDSTREAM) MapViewOfFile( g_hStream, FILE_MAP_ALL_ACCESS, 0, 0, // offset sizeof(*g_Stream) ); if (NULL == g_Stream) { TRC(ERR, "VCSndIoThread: " "can't map the stream view: %d\n", GetLastError()); goto exitpt; } // Initialize the stream // if (VCSndAcquireStream()) { memset(g_Stream, 0, sizeof(*g_Stream) - sizeof(g_Stream->pSndData)); memset(g_Stream->pSndData, 0x00000000, sizeof(g_Stream->pSndData)); g_Stream->cLastBlockConfirmed = g_Stream->cLastBlockSent - 1; // // no socket created, so far // g_hDGramSocket = INVALID_SOCKET; VCSndReleaseStream(); } else { TRC(FATAL, "VCSndIoThread, can't map the stream: %d, aborting\n", GetLastError()); goto exitpt; } if (!ProcessIdToSessionId(GetCurrentProcessId(), &logonId)) { TRC(FATAL, "VCSndIoThread: failed to het session Id. %d\n", GetLastError()); goto exitpt; } // create disconnect/reconnect events // wcsncpy(szEvName, L"RDPSound-Disconnect", sizeof(szEvName)/sizeof(szEvName[0])); g_hDisconnectEvent = CreateEvent(NULL, FALSE, FALSE, szEvName); if (NULL == g_hDisconnectEvent) { TRC(FATAL, "VCSndIoThread: can't create disconnect event. %d\n", GetLastError()); goto exitpt; } wcsncpy(szEvName, L"RDPSound-Reconnect", sizeof(szEvName)/sizeof(szEvName[0])); hReconnectEvent = CreateEvent(NULL, FALSE, FALSE, szEvName); if (NULL == hReconnectEvent) { TRC(FATAL, "VCSndIoThread: can't create reconnect event. %d\n", GetLastError()); goto exitpt; } if (!ChannelOpen()) { TRC(FATAL, "VCSndIoThread: unable to open virtual channel\n"); goto exitpt; } ahEvents[READ_EVENT] = hReadEvent; ahEvents[DISCONNECT_EVENT] = g_hDisconnectEvent; ahEvents[RECONNECT_EVENT] = hReconnectEvent; ahEvents[DATAREADY_EVENT] = g_hDataReadyEvent; ahEvents[DGRAM_EVENT] = hDGramEvent; ahEvents[POWERWAKEUP_EVENT] = g_hPowerWakeUpEvent; ahEvents[POWERSUSPEND_EVENT] = g_hPowerSuspendEvent; _VCSndCheckDevice( hReadEvent, hDGramEvent ); // Check the channel for data // while (ChannelReceiveMessage(&SndMessage, hReadEvent)) { VCSndDataArrived(&SndMessage); SndMessage.uiPrologReceived = 0; SndMessage.uiBodyReceived = 0; } DGramRead( hDGramEvent, NULL, NULL ); // // signal all workers waiting for initialize // _SignalInitializeDone(); // main loop // while (g_bRunning) { DWORD dwNumEvents = sizeof(ahEvents)/sizeof(ahEvents[0]); dwres = WaitForMultipleObjectsEx( dwNumEvents, // count ahEvents, // events FALSE, // wait all DEFAULT_RESPONSE_TIMEOUT, FALSE // non alertable ); if (!g_bRunning) TRC(ALV, "VCSndIoThread: time to exit\n"); if (WAIT_TIMEOUT != dwres) TRC(ALV, "VCSndIoThread: an event was fired\n"); if (READ_EVENT == dwres) // // data is ready to read // { ChannelBlockReadComplete(); ResetEvent(ahEvents[0]); // Check the channel for data // while (ChannelReceiveMessage(&SndMessage, hReadEvent)) { VCSndDataArrived(&SndMessage); SndMessage.uiPrologReceived = 0; SndMessage.uiBodyReceived = 0; } } else if (( DISCONNECT_EVENT == dwres ) || // disconnect event ( POWERSUSPEND_EVENT == dwres )) // suspend event { // disconnect event // TRC(INF, "VCSndIoThread: DISCONNECTED\n"); _VCSndCloseDevice(); _VCSndCloseConverter(); ChannelClose(); _StatReset(); if ( DISCONNECT_EVENT == dwres ) { g_bDisconnected = TRUE; } else { g_bSuspended = TRUE; } continue; } else if (( RECONNECT_EVENT == dwres ) || // reconnect event ( POWERWAKEUP_EVENT == dwres )) { // reconnect event // if ( POWERWAKEUP_EVENT == dwres ) { // power wakeup event // here, we may have not received suspend event, but in that case we // had failed to send, so check the g_bDeviceFailed flag and act only if it is on if ( g_bDisconnected ) { // no reason to process power wake up if we are not remote // g_bSuspended = FALSE; continue; } if ( !g_bSuspended && !g_bDeviceFailed ) { // if neither of these happend // then we don't care continue; } } TRC(INF, "VCSndIoThread: RECONNECTED\n"); if (!ChannelOpen()) { TRC(FATAL, "VCSndIoThread: unable to open virtual channel\n"); } else { _VCSndCheckDevice( hReadEvent, hDGramEvent ); // // start the receive loop again // if (ChannelReceiveMessage(&SndMessage, hReadEvent)) { VCSndDataArrived(&SndMessage); SndMessage.uiPrologReceived = 0; SndMessage.uiBodyReceived = 0; } if ( RECONNECT_EVENT == dwres ) { g_bDisconnected = FALSE; } else { g_bSuspended = FALSE; } g_bDeviceFailed = FALSE; // kick the player // PulseEvent( g_hStreamIsEmptyEvent ); _SignalInitializeDone(); } } else if ( DGRAM_EVENT == dwres ) { // // DGram ready // DGramReadComplete( NULL, NULL ); // // atttempt more read // DGramRead( hDGramEvent, NULL, NULL ); } // Check for data available from the apps // if (!VCSndAcquireStream()) { TRC(FATAL, "VCSndIoThread: somebody is holding the " "Stream mutext for too long\n"); continue; } // if this was a reconnect // roll back to the last block sent // if ( RECONNECT_EVENT == dwres) { // // clean this chunk for the next mix // memset( g_Stream->pSndData, 0, TSSND_MAX_BLOCKS * TSSND_BLOCKSIZE ); g_Stream->cLastBlockConfirmed = g_Stream->cLastBlockSent; } // // if we have not received confirmation // for the packets sent, just give up and continue // if (WAIT_TIMEOUT == dwres && g_bDeviceOpened && g_Stream->cLastBlockSent != g_Stream->cLastBlockConfirmed) { BYTE cCounter; TRC(WRN, "VCSndIoThread: not received confirmation for blocks " "between %d and %d\n", g_Stream->cLastBlockConfirmed, g_Stream->cLastBlockSent); for ( cCounter = g_Stream->cLastBlockConfirmed; cCounter != (BYTE)(g_Stream->cLastBlockSent + 1); cCounter++) { _StatsCollect(( GetTickCount() - DEFAULT_RESPONSE_TIMEOUT ) & 0xffff ); } // // end of the loop // g_Stream->cLastBlockConfirmed = g_Stream->cLastBlockSent; // // kick the player // PulseEvent(g_hStreamIsEmptyEvent); } // Check for control commands // // Volume // if ( g_bDeviceOpened && g_Stream->bNewVolume && 0 != (g_Stream->dwSoundCaps & TSSNDCAPS_VOLUME)) { SNDSETVOLUME SetVolume; TRC(ALV, "VCSndIoThread: new volume\n"); SetVolume.Prolog.Type = SNDC_SETVOLUME; SetVolume.Prolog.BodySize = sizeof(SetVolume) - sizeof(SetVolume.Prolog); SetVolume.dwVolume = g_Stream->dwVolume; ChannelBlockWrite( &SetVolume, sizeof(SetVolume) ); g_Stream->bNewVolume = FALSE; } // Pitch // if ( g_bDeviceOpened && g_Stream->bNewPitch && 0 != (g_Stream->dwSoundCaps & TSSNDCAPS_PITCH)) { SNDSETPITCH SetPitch; TRC(ALV, "VCSndIoThread: new pitch\n"); SetPitch.Prolog.Type = SNDC_SETPITCH; SetPitch.Prolog.BodySize = sizeof(SetPitch) - sizeof(SetPitch.Prolog); SetPitch.dwPitch = g_Stream->dwPitch; ChannelBlockWrite( &SetPitch, sizeof(SetPitch) ); g_Stream->bNewPitch = FALSE; } // Check for data available from the apps // if (g_Stream->cLastBlockSent != g_Stream->cLastBlockQueued && (BYTE)(g_Stream->cLastBlockSent - g_Stream->cLastBlockConfirmed) < g_dwBlocksOnTheNet ) { // Aha, here's some data to send // TRC(ALV, "VCSndIoThread: will send some data\n"); if (g_bDisconnected || g_bSuspended || g_bDeviceFailed) { TRC(ALV, "Device is disconnected. ignore the packets\n"); g_Stream->cLastBlockSent = g_Stream->cLastBlockQueued; g_Stream->cLastBlockConfirmed = g_Stream->cLastBlockSent - 1; PulseEvent( g_hStreamIsEmptyEvent ); } else if (!g_bDeviceOpened) { // send an "open device" command // SNDPROLOG Prolog; // // first, try to open the acm converter // _VCSndOpenConverter(); // // if we failed width the converter will // send in native format // g_bDeviceOpened = TRUE; } for (i = g_Stream->cLastBlockSent; i != g_Stream->cLastBlockQueued && (BYTE)(g_Stream->cLastBlockSent - g_Stream->cLastBlockConfirmed) < g_dwBlocksOnTheNet; i++) { BOOL bSucc; // TRC( INF, "Sending block # %d, last conf=%d, last queued=%d\n", i, g_Stream->cLastBlockConfirmed, g_Stream->cLastBlockSent ); bSucc = _VCSndSendWave( i, // block no ((LPSTR)g_Stream->pSndData) + ((i % TSSND_MAX_BLOCKS) * TSSND_BLOCKSIZE), TSSND_BLOCKSIZE ); // // clean this chunk for the next mix // memset(g_Stream->pSndData + (i % TSSND_MAX_BLOCKS) * TSSND_BLOCKSIZE, 0x00000000, TSSND_BLOCKSIZE); if ( 0xff == i ) g_HiBlockNo++; if (bSucc) { g_Stream->cLastBlockSent = i + 1; } else { TRC(WRN, "VCSndIoThread: failed to send, " "disabling the device\n"); // // act the same way as DISCONNECT // _VCSndCloseDevice(); _VCSndCloseConverter(); ChannelClose(); g_Stream->cLastBlockConfirmed = g_Stream->cLastBlockSent = g_Stream->cLastBlockQueued; _StatReset(); g_bDeviceFailed = TRUE; // // Break this loop // break; } } } // Check if there's no more data // if so, close the remote device // if (g_bDeviceOpened && g_Stream->cLastBlockQueued == g_Stream->cLastBlockSent && g_Stream->cLastBlockSent == g_Stream->cLastBlockConfirmed) { SNDPROLOG Prolog; TRC(ALV, "VCSndIoThread: no more data, closing the device\n"); _VCSndCloseConverter(); Prolog.Type = SNDC_CLOSE; Prolog.BodySize = 0; ChannelBlockWrite(&Prolog, sizeof(Prolog)); g_bDeviceOpened = FALSE; } VCSndReleaseStream(); } exitpt: ChannelClose(); if (NULL != hReadEvent) CloseHandle(hReadEvent); if (NULL != hDGramEvent) WSACloseEvent( hDGramEvent ); if (SndMessage.pBody) TSFREE(SndMessage.pBody); if (NULL != hReconnectEvent) CloseHandle(hReconnectEvent); if (NULL != g_hDisconnectEvent) CloseHandle(g_hDisconnectEvent); if (NULL != g_hStreamIsEmptyEvent) CloseHandle(g_hStreamIsEmptyEvent); if (VCSndAcquireStream()) { // // mark the device dead // g_Stream->dwSoundCaps = TSSNDCAPS_TERMINATED; VCSndReleaseStream(); _SignalInitializeDone(); } if (NULL != g_Stream) { if (INVALID_SOCKET != g_hDGramSocket) closesocket(g_hDGramSocket); UnmapViewOfFile(g_Stream); } if (NULL != g_hStream) CloseHandle(g_hStream); if (NULL != g_hStreamMutex) CloseHandle(g_hStreamMutex); // clean the previously negotiated format // if (NULL != g_ppNegotiatedFormats) { DWORD i; for ( i = 0; i < g_dwNegotiatedFormats; i++ ) { if ( NULL != g_ppNegotiatedFormats[i] ) TSFREE( g_ppNegotiatedFormats[i] ); } TSFREE( g_ppNegotiatedFormats ); } // // cleanup the format list // if ( NULL != g_pAllCodecsFormatList ) { PVCSNDFORMATLIST pIter; pIter = g_pAllCodecsFormatList; while( NULL != pIter ) { PVCSNDFORMATLIST pNext = pIter->pNext; TSFREE( pIter ); pIter = pNext; } } if ( NULL != g_AllowCodecs ) { TSFREE( g_AllowCodecs ); g_AllowCodecs = NULL; g_AllowCodecsSize = 0; } WSACleanup(); TRC(INF, "VCSndIoThread: EXIT !\n"); return 0; } ///////////////////////////////////////////////////////////////////// // // Startup code // ///////////////////////////////////////////////////////////////////// VOID TSSNDD_Term( VOID ) { if ( NULL == g_hThread ) return; g_bRunning = FALSE; // // kick the io thread // if (NULL != g_hDataReadyEvent) SetEvent(g_hDataReadyEvent); if ( NULL != g_hThread ) { WaitForSingleObject(g_hThread, DEFAULT_VC_TIMEOUT); CloseHandle(g_hThread); g_hThread = NULL; } if (NULL != g_hDataReadyEvent) { CloseHandle(g_hDataReadyEvent); g_hDataReadyEvent = NULL; } if ( NULL != g_hPowerWakeUpEvent ) { CloseHandle( g_hPowerWakeUpEvent ); g_hPowerWakeUpEvent = NULL; } if ( NULL != g_hPowerSuspendEvent ) { CloseHandle( g_hPowerSuspendEvent ); g_hPowerSuspendEvent = NULL; } } LRESULT TSSNDD_PowerMessage( WPARAM wParam, LPARAM lParam ) { switch( wParam ) { case PBT_APMSUSPEND: // // signal only if connected // if ( NULL != g_hPowerSuspendEvent ) { SetEvent( g_hPowerSuspendEvent ); } break; case PBT_APMRESUMEAUTOMATIC: case PBT_APMRESUMECRITICAL: case PBT_APMRESUMESUSPEND: // // signal only if not connected // if ( NULL != g_hPowerWakeUpEvent ) { SetEvent( g_hPowerWakeUpEvent ); } break; } return TRUE; } LRESULT CALLBACK _VCSndWndProc( HWND hwnd, UINT uiMessage, WPARAM wParam, LPARAM lParam ) { LRESULT rv = 0; switch( uiMessage ) { case WM_CREATE: break; case WM_CLOSE: DestroyWindow(hwnd); break; case WM_DESTROY: PostQuitMessage(0); break; case WM_ENDSESSION: TSSNDD_Term(); break; case WM_POWERBROADCAST: rv = TSSNDD_PowerMessage( wParam, lParam ); break; default: rv = DefWindowProc(hwnd, uiMessage, wParam, lParam); } return rv; } BOOL TSSNDD_Loop( HINSTANCE hInstance ) { BOOL rv = FALSE; WNDCLASS wc; DWORD dwLastErr; HWND hWnd = NULL; MSG msg; memset(&wc, 0, sizeof(wc)); wc.lpfnWndProc = _VCSndWndProc; wc.hInstance = hInstance; wc.hbrBackground = (HBRUSH)GetStockObject(HOLLOW_BRUSH); wc.lpszClassName = _RDPSNDWNDCLASS; if (!RegisterClass (&wc) && (dwLastErr = GetLastError()) && dwLastErr != ERROR_CLASS_ALREADY_EXISTS) { TRC(ERR, "TSSNDD_Loop: Can't register class. GetLastError=%d\n", GetLastError()); goto exitpt; } hWnd = CreateWindow( _RDPSNDWNDCLASS, _RDPSNDWNDCLASS, // Window name WS_OVERLAPPEDWINDOW, // dwStyle 0, // x 0, // y 100, // nWidth 100, // nHeight NULL, // hWndParent NULL, // hMenu hInstance, NULL); // lpParam if (!hWnd) { TRC(ERR, "TSSNDD_Loop: Failed to create message window: %d\n", GetLastError()); goto exitpt; } while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } rv = TRUE; exitpt: return rv; } BOOL TSSNDD_Init( ) { BOOL rv = FALSE; DWORD dwThreadId; g_bRunning = TRUE; if ( NULL == g_hPowerWakeUpEvent ) { g_hPowerWakeUpEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); if ( NULL == g_hPowerWakeUpEvent ) { TRC( FATAL, "TSSNDD_Init: failed to create power wakeup notification message: %d\n", GetLastError() ); goto exitpt; } } if ( NULL == g_hPowerSuspendEvent ) { g_hPowerSuspendEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); if ( NULL == g_hPowerSuspendEvent ) { TRC( FATAL, "TSSNDD_Init: failed to create power suspend notification message: %d\n", GetLastError() ); goto exitpt; } } g_hThread = CreateThread( NULL, 0, (LPTHREAD_START_ROUTINE)VCSndIoThread, NULL, 0, &dwThreadId ); if (NULL == g_hThread) { TRC(FATAL, "WinMain: can't create thread: %d. Aborting\n", GetLastError()); goto exitpt; } rv = TRUE; exitpt: return rv; } ///////////////////////////////////////////////////////////////////// // // Tracing // ///////////////////////////////////////////////////////////////////// VOID _cdecl _DebugMessage( LPCSTR szLevel, LPCSTR szFormat, ... ) { CHAR szBuffer[256]; va_list arglist; if (szLevel == ALV) return; va_start (arglist, szFormat); _vsnprintf (szBuffer, RTL_NUMBER_OF(szBuffer), szFormat, arglist); va_end (arglist); szBuffer[ RTL_NUMBER_OF( szBuffer ) - 1 ] = 0; OutputDebugStringA(szLevel); OutputDebugStringA(szBuffer); }