/*==========================================================================; * * Copyright (C) 1999 Microsoft Corporation. All Rights Reserved. * * File: dvcsplay.cpp * Content: Implementation of CDVCSPlayer class * History: * Date By Reason * ============ * 07/22/99 rodtoll created * 10/05/99 rodtoll Added comments, dpf's. * 10/29/99 rodtoll Bug #113726 - Integrate Voxware Codecs, updating to use new * pluggable codec architecture. * 01/14/2000 rodtoll Updated to support multiple targets * 03/28/2000 rodtoll Updated to use new player class as base * rodtoll Moved a bunch of logic out of server into this class * 11/16/2000 rodtoll Bug #40587 - DPVOICE: Mixing server needs to use multi-processors * 09/05/2001 simonpow Bug #463972. Added constuct/destruct methods to enable * allocations and de-allocations via CFixedPool objects * 02/28/2002 rodtoll WINBUG #549959 - SECURITY: DPVOICE: Voice server trusts client's target list * - When server controlled targetting is enabled, use server's copy of client * target list instead of list specified in incoming packet. * - Fixed crashed caused by updated bilink code. ***************************************************************************/ #include "dxvoicepch.h" #undef DPF_MODNAME #define DPF_MODNAME "CDVCSPlayer::Construct" BOOL CDVCSPlayer::PoolAllocFunction(void * pvItem, void * pvContext) { ((CDVCSPlayer * ) pvItem)->CDVCSPlayer::CDVCSPlayer(); return TRUE; } #undef DPF_MODNAME #define DPF_MODNAME "CDVCSPlayer::Destruct" void CDVCSPlayer::PoolDeallocFunction(void * pvItem) { ((CDVCSPlayer * ) pvItem)->CDVCSPlayer::~CDVCSPlayer(); } CDVCSPlayer::CDVCSPlayer() :CVoicePlayer() { m_lpOutBoundAudioConverter=NULL; m_bLastSent=NULL; m_bMsgNum=(BYTE)-1; m_bSeqNum=0; m_targetSize=0; m_pblMixingActivePlayers=NULL; m_pblMixingSpeakingPlayers=NULL; m_pblMixingHearingPlayers=NULL; m_pdwHearCount=NULL; m_pfDecompressed=NULL; m_pfSilence=NULL; m_pfNeedsDecompression=NULL; m_pppCanHear=NULL; m_pdwMaxCanHear=NULL; m_pSourceFrame=NULL; m_sourceUnCompressed=NULL; m_targetCompressed=NULL; m_pfMixed=NULL; m_dwNumMixingThreads=0; m_pbMsgNumToSend=NULL; m_pbSeqNumToSend=NULL; m_pdwResultLength=NULL; m_pfMixToBeReused=NULL; } CDVCSPlayer::~CDVCSPlayer() { //this space intentionally left blank } #undef DPF_MODNAME #define DPF_MODNAME "CDVCSPlayer::ComparePlayerMix" // // ComparePlayerMix // // Compares the mix of one player to the mix of another. // BOOL CDVCSPlayer::ComparePlayerMix( DWORD dwThreadIndex, const CDVCSPlayer *lpdvPlayer ) { DNASSERT( lpdvPlayer != NULL ); if( lpdvPlayer->m_pdwHearCount[dwThreadIndex] != m_pdwHearCount[dwThreadIndex] ) { return FALSE; } for( int index = 0; index < m_pdwHearCount[dwThreadIndex]; index++ ) { if( lpdvPlayer->m_pppCanHear[dwThreadIndex][index] != m_pppCanHear[dwThreadIndex][index] ) { return FALSE; } } return TRUE; } #undef DPF_MODNAME #define DPF_MODNAME "CDVCSPlayer::CompleteRun" void CDVCSPlayer::CompleteRun( DWORD dwThreadIndex ) { if( m_pSourceFrame[dwThreadIndex] ) { m_pSourceFrame[dwThreadIndex]->Return(); m_pSourceFrame[dwThreadIndex] = NULL; } } #undef DPF_MODNAME #define DPF_MODNAME "CDVCSPlayer::ResetForNextRun" void CDVCSPlayer::ResetForNextRun( DWORD dwThreadIndex, BOOL fDequeue ) { BOOL fLostFrame; m_pdwHearCount[dwThreadIndex] = 0; m_pfSilence[dwThreadIndex] = FALSE; m_pReuseMixFromThisPlayer[dwThreadIndex] = NULL; if( !fDequeue ) { m_pSourceFrame[dwThreadIndex] = NULL; } else { DNASSERT( !m_pSourceFrame[dwThreadIndex] ); m_pSourceFrame[dwThreadIndex] = Dequeue(&fLostFrame, &m_pfSilence[dwThreadIndex]); } m_pfMixed[dwThreadIndex] = FALSE; m_pfNeedsDecompression[dwThreadIndex] = FALSE; m_pfDecompressed[dwThreadIndex] = FALSE; m_pfMixToBeReused[dwThreadIndex] = FALSE; m_pdwResultLength[dwThreadIndex] = NULL; RemoveFromHearingList(dwThreadIndex); RemoveFromSpeakingList(dwThreadIndex); } #undef DPF_MODNAME #define DPF_MODNAME "CDVCSPlayer::Initialize" HRESULT CDVCSPlayer::Initialize( const DVID dvidPlayer, const DWORD dwHostOrder, DWORD dwFlags, PVOID pvContext, DWORD dwCompressedSize, DWORD dwUnCompressedSize, CFixedPool *pCSOwner, DWORD dwNumMixingThreads ) { HRESULT hr; DWORD dwIndex; m_pCSOwner = pCSOwner; hr = CVoicePlayer::Initialize( dvidPlayer, dwHostOrder, dwFlags, pvContext, NULL ); if( FAILED( hr ) ) { DPFX(DPFPREP, 0, "Initialize failed on player hr=0x%x", hr ); return hr; } m_pdwHearCount = new DWORD[dwNumMixingThreads]; m_pfSilence = new BOOL[dwNumMixingThreads]; m_pdwMaxCanHear = new DWORD[dwNumMixingThreads]; m_pSourceFrame = new CFrame*[dwNumMixingThreads]; m_pfMixed = new BOOL[dwNumMixingThreads]; m_pblMixingActivePlayers = new CBilinkPlusObject[dwNumMixingThreads]; m_pblMixingHearingPlayers = new CBilinkPlusObject[dwNumMixingThreads]; m_pblMixingSpeakingPlayers = new CBilinkPlusObject[dwNumMixingThreads]; m_sourceUnCompressed = new BYTE[dwNumMixingThreads*dwUnCompressedSize]; m_targetCompressed = new BYTE[dwNumMixingThreads*dwCompressedSize]; m_pfNeedsDecompression = new BOOL[dwNumMixingThreads]; m_pfDecompressed = new BOOL[dwNumMixingThreads]; m_dwNumMixingThreads = dwNumMixingThreads; m_pbMsgNumToSend = new BYTE[dwNumMixingThreads]; m_pbSeqNumToSend = new BYTE[dwNumMixingThreads]; m_pdwUnCompressedBufferOffset = new DWORD[dwNumMixingThreads]; m_pdwCompressedBufferOffset = new DWORD[dwNumMixingThreads]; m_pReuseMixFromThisPlayer = new CDVCSPlayer*[dwNumMixingThreads]; m_pdwResultLength = new DWORD[dwNumMixingThreads]; m_pfMixToBeReused = new BOOL[dwNumMixingThreads]; if( !m_pblMixingActivePlayers || !m_pdwHearCount || !m_pfSilence || !m_pdwMaxCanHear || !m_pSourceFrame || !m_sourceUnCompressed || !m_targetCompressed || !m_pfMixed || !m_pfNeedsDecompression || !m_pfDecompressed || !m_pbMsgNumToSend || !m_pbSeqNumToSend || !m_pdwUnCompressedBufferOffset || !m_pdwCompressedBufferOffset || !m_pReuseMixFromThisPlayer || !m_pdwResultLength || !m_pfMixToBeReused || !m_pblMixingHearingPlayers || !m_pblMixingSpeakingPlayers ) { DPFX(DPFPREP, 0, "Memory alloc failure" ); hr = DVERR_OUTOFMEMORY; goto INITIALIZE_FAILURE; } // Create the Can Hear arrays m_pppCanHear = new CDVCSPlayer**[dwNumMixingThreads]; ZeroMemory( m_pppCanHear, sizeof(CDVCSPlayer*)*dwNumMixingThreads ); // Resize canhear arrays and setup compressed/uncompressed offsets for( dwIndex = 0; dwIndex < dwNumMixingThreads; dwIndex++ ) { m_pdwUnCompressedBufferOffset[dwIndex] = dwIndex * dwUnCompressedSize; m_pdwCompressedBufferOffset[dwIndex] = dwIndex * dwCompressedSize; m_pSourceFrame[dwIndex] = 0; m_pdwMaxCanHear[dwIndex] = 0; m_pblMixingActivePlayers[dwIndex].m_pPlayer = this; m_pblMixingActivePlayers[dwIndex].m_bl.Initialize(); m_pblMixingSpeakingPlayers[dwIndex].m_pPlayer = this; m_pblMixingSpeakingPlayers[dwIndex].m_bl.Initialize(); m_pblMixingHearingPlayers[dwIndex].m_pPlayer = this; m_pblMixingHearingPlayers[dwIndex].m_bl.Initialize(); ResetForNextRun(dwIndex,FALSE); hr = ResizeIfRequired(dwIndex,1); if( FAILED( hr ) ) { DPFX(DPFPREP, 0, "Error resizing target array hr=0x%x", hr ); goto INITIALIZE_FAILURE; } } return hr; INITIALIZE_FAILURE: DeInitialize(); return hr; } #undef DPF_MODNAME #define DPF_MODNAME "CDVCSPlayer::DeInitialize" HRESULT CDVCSPlayer::DeInitialize() { DWORD dwIndex; if( m_pblMixingActivePlayers ) { #ifdef DEBUG for( dwIndex = 0; dwIndex < m_dwNumMixingThreads; dwIndex++ ) { DNASSERT( m_pblMixingActivePlayers[dwIndex].m_bl.IsEmpty() ); DNASSERT( !m_pSourceFrame[dwIndex] ); } #endif delete [] m_pblMixingActivePlayers; m_pblMixingActivePlayers = NULL; } if( m_pblMixingSpeakingPlayers ) { for( dwIndex = 0; dwIndex < m_dwNumMixingThreads; dwIndex++ ) { this->RemoveFromSpeakingList( dwIndex ); } delete [] m_pblMixingSpeakingPlayers; m_pblMixingSpeakingPlayers = NULL; } if( m_pblMixingHearingPlayers ) { for( dwIndex = 0; dwIndex < m_dwNumMixingThreads; dwIndex++ ) { this->RemoveFromHearingList( dwIndex ); } delete [] m_pblMixingHearingPlayers; m_pblMixingHearingPlayers = NULL; } if( m_lpOutBoundAudioConverter ) { m_lpOutBoundAudioConverter->Release(); m_lpOutBoundAudioConverter = NULL; } if( m_pppCanHear ) { for( dwIndex = 0; dwIndex < m_dwNumMixingThreads; dwIndex++ ) { if( m_pppCanHear[dwIndex] ) { delete [] m_pppCanHear[dwIndex]; m_pppCanHear[dwIndex] = NULL; } } delete [] m_pppCanHear; m_pppCanHear = NULL; } if( m_pdwHearCount ) { delete [] m_pdwHearCount; m_pdwHearCount = NULL; } if( m_pdwResultLength ) { delete [] m_pdwResultLength; m_pdwResultLength = NULL; } if( m_pfSilence ) { delete [] m_pfSilence; m_pfSilence = NULL; } if( m_pdwUnCompressedBufferOffset ) { delete [] m_pdwUnCompressedBufferOffset; m_pdwUnCompressedBufferOffset = NULL; } if( m_pReuseMixFromThisPlayer ) { delete [] m_pReuseMixFromThisPlayer; m_pReuseMixFromThisPlayer = NULL; } if( m_pdwCompressedBufferOffset ) { delete [] m_pdwCompressedBufferOffset; m_pdwCompressedBufferOffset = NULL; } if( m_pdwMaxCanHear ) { delete [] m_pdwMaxCanHear; m_pdwMaxCanHear = NULL; } if( m_sourceUnCompressed ) { delete [] m_sourceUnCompressed; m_sourceUnCompressed = NULL; } if( m_pfMixToBeReused ) { delete [] m_pfMixToBeReused; m_pfMixToBeReused = NULL; } if( m_pbMsgNumToSend ) { delete [] m_pbMsgNumToSend; m_pbMsgNumToSend = NULL; } if( m_pbSeqNumToSend ) { delete [] m_pbSeqNumToSend; m_pbSeqNumToSend = NULL; } if( m_targetCompressed ) { delete [] m_targetCompressed; m_targetCompressed = NULL; } if( m_pfMixed ) { delete [] m_pfMixed; m_pfMixed = NULL; } if( m_pfNeedsDecompression ) { delete [] m_pfNeedsDecompression; m_pfNeedsDecompression = NULL; } if( m_pfDecompressed ) { delete [] m_pfDecompressed; m_pfDecompressed = NULL; } if( m_pSourceFrame ) { delete [] m_pSourceFrame; m_pSourceFrame = NULL; } FreeResources(); m_pCSOwner->Release( this ); return DV_OK; } #undef DPF_MODNAME #define DPF_MODNAME "CDVCSPlayer::HandleMixingReceive" HRESULT CDVCSPlayer::HandleMixingReceive( PDVPROTOCOLMSG_SPEECHHEADER pdvSpeechHeader, PBYTE pbData, DWORD dwSize, PDVID pdvidTargets, DWORD dwNumTargets, BOOL fServerTargetting ) { CFrame tmpFrame; tmpFrame.SetSeqNum( pdvSpeechHeader->bSeqNum ); tmpFrame.SetMsgNum( pdvSpeechHeader->bMsgNum ); tmpFrame.SetIsSilence( FALSE ); tmpFrame.SetFrameLength( dwSize ); tmpFrame.UserOwn_SetData( pbData, dwSize ); Lock(); // If the server is controlling the targets, take targets for the speech // packet from the local user record instead of the packet. Prevents // clients from being hacked to get around client-side targetting. // if( fServerTargetting ) tmpFrame.UserOwn_SetTargets( GetTargetList(), GetNumTargets() ); else tmpFrame.UserOwn_SetTargets( pdvidTargets, dwNumTargets ); // STATSBLOCK: Begin //m_pStatsBlob->m_dwPRESpeech++; // STATSBLOCK: End m_lpInputQueue->Enqueue( tmpFrame ); m_dwLastData = GetTickCount(); DPFX(DPFPREP, DVF_MIXER_DEBUG_LEVEL, "Received speech is buffered!" ); m_dwNumReceivedFrames++; UnLock(); return DV_OK; } #undef DPF_MODNAME #define DPF_MODNAME "CDVCSPlayer::CompressOutBound" HRESULT CDVCSPlayer::CompressOutBound( PVOID pvInputBuffer, DWORD dwInputBufferSize, PVOID pvOutputBuffer, DWORD *pdwOutputSize ) { HRESULT hr; hr = m_lpOutBoundAudioConverter->Convert( pvInputBuffer, dwInputBufferSize, pvOutputBuffer, pdwOutputSize, FALSE ); if( FAILED( hr ) ) { DPFX(DPFPREP, 0, "Failed converting audio hr=0x%x", hr ); return hr; } return hr; }