/*++ Copyright (c) 1998 Microsoft Corporation Module Name: wavestrm.cpp Abstract: This module contains implementation of CWaveMSPStream. Author: Zoltan Szilagyi (zoltans) September 7, 1998 --*/ #include "stdafx.h" #include #include HRESULT TryCreateCSAFilter( IN GUID *PermanentGuid, OUT IBaseFilter **ppCSAFilter ); /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// // CWaveMSPStream::CWaveMSPStream() : CMSPStream() { LOG((MSP_TRACE, "CWaveMSPStream::CWaveMSPStream entered.")); m_fTerminalConnected = FALSE; m_fHaveWaveID = FALSE; m_DesiredGraphState = State_Stopped; m_pFilter = NULL; m_pG711Filter = NULL; LOG((MSP_TRACE, "CWaveMSPStream::CWaveMSPStream exited.")); } /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// // CWaveMSPStream::~CWaveMSPStream() { LOG((MSP_TRACE, "CWaveMSPStream::~CWaveMSPStream entered.")); LOG((MSP_TRACE, "CWaveMSPStream::~CWaveMSPStream exited.")); } /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// // void CWaveMSPStream::FinalRelease() { LOG((MSP_TRACE, "CWaveMSPStream::FinalRelease entered.")); // // At this point we should have no terminals selected, since // Shutdown is supposed to be called before we are destructed. // _ASSERTE( 0 == m_Terminals.GetSize() ); // // Remove out filter from the graph and release it. // if ( m_fHaveWaveID ) { _ASSERTE( m_pFilter ); m_pIGraphBuilder->RemoveFilter( m_pFilter ); m_pFilter->Release(); } if ( m_pG711Filter ) { m_pIGraphBuilder->RemoveFilter( m_pG711Filter ); m_pG711Filter->Release(); } // // Call the base class method to clean up everything else. // CMSPStream::FinalRelease(); LOG((MSP_TRACE, "CWaveMSPStream::FinalRelease exited.")); } /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// // STDMETHODIMP CWaveMSPStream::get_Name ( OUT BSTR * ppName ) { LOG((MSP_TRACE, "CWaveMSPStream::get_Name - enter")); // // Check argument. // if ( IsBadWritePtr(ppName, sizeof(BSTR) ) ) { LOG((MSP_TRACE, "CWaveMSPStream::get_Name - " "bad return pointer - returning E_POINTER")); return E_POINTER; } // // Decide what string to return based on which stream this is. // ULONG ulID; if ( m_Direction == TD_CAPTURE ) { ulID = IDS_CAPTURE_STREAM; } else { ulID = IDS_RENDER_STREAM; } // // Get the string from the string table. // const int ciAllocSize = 2048; WCHAR wszName[ciAllocSize]; int iReturn = LoadString( _Module.GetModuleInstance(), ulID, wszName, ciAllocSize - 1 ); if ( iReturn == 0 ) { _ASSERTE( FALSE ); *ppName = NULL; LOG((MSP_ERROR, "CWaveMSPStream::get_Name - " "LoadString failed - returning E_UNEXPECTED")); return E_UNEXPECTED; } // // Convert to a BSTR and return the BSTR. // *ppName = SysAllocString(wszName); if ( *ppName == NULL ) { LOG((MSP_ERROR, "CWaveMSPStream::get_Name - " "SysAllocString failed - returning E_OUTOFMEMORY")); return E_OUTOFMEMORY; } LOG((MSP_TRACE, "CWaveMSPStream::get_Name - exit S_OK")); return S_OK; } /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// // STDMETHODIMP CWaveMSPStream::SelectTerminal( IN ITTerminal * pTerminal ) { LOG((MSP_TRACE, "CWaveMSPStream::SelectTerminal - enter")); // // We are going to access the terminal list -- grab the lock // CLock lock(m_lock); // // Reject if we already have a terminal selected. // if ( 0 != m_Terminals.GetSize() ) { LOG((MSP_ERROR, "CWaveMSPStream::SelectTerminal - " "exit TAPI_E_MAXTERMINALS")); return TAPI_E_MAXTERMINALS; } // // Use base class method to add it to our list of terminals. // HRESULT hr = CMSPStream::SelectTerminal(pTerminal); if ( FAILED(hr) ) { LOG((MSP_ERROR, "CWaveMSPStream::SelectTerminal - " "base class method failed - exit 0x%08x", hr)); return hr; } // // Re-pause or re-start the stream if needed. // if ( m_DesiredGraphState == State_Paused ) { hr = PauseStream(); } else if ( m_DesiredGraphState == State_Running ) { hr = StartStream(); } else { _ASSERTE( m_DesiredGraphState == State_Stopped ); hr = S_OK; } if ( FAILED(hr) ) { LOG((MSP_TRACE, "CWaveMSPStream::SelectTerminal - " "can't regain old graph state - unselecting terminal - " "exit 0x%08x", hr)); // // Unselect it to undo all of the above. // UnselectTerminal(pTerminal); return hr; } LOG((MSP_TRACE, "CWaveMSPStream::SelectTerminal - exit S_OK")); return S_OK; } /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// // STDMETHODIMP CWaveMSPStream::UnselectTerminal ( IN ITTerminal * pTerminal ) { LOG((MSP_TRACE, "CWaveMSPStream::UnselectTerminal - enter")); CLock lock(m_lock); // // Add an extra reference to the terminal so it doesn't go away // after we call CMSPStream::UnselectTerminal. We need it later // in the function. // pTerminal->AddRef(); // // Use base class method to remove terminal from our list of terminals. // HRESULT hr = CMSPStream::UnselectTerminal(pTerminal); if (FAILED(hr)) { LOG((MSP_ERROR, "CWaveMSPStream::UnselectTerminal - " "base class method failed - exit 0x%08x", hr)); pTerminal->Release(); return hr; } // // If we've been given a waveid then we may not be stopped. // This does nothing if we are already stopped. // CMSPStream::StopStream(); // // Disconnect the terminal if this call had it connected. // if ( m_fTerminalConnected ) { // // Get the ITTerminalControl interface. // ITTerminalControl * pTerminalControl; hr = pTerminal->QueryInterface(IID_ITTerminalControl, (void **) &pTerminalControl); if ( FAILED(hr) ) { LOG((MSP_ERROR, "CWaveMSPStream::UnselectTerminal - " "QI for ITTerminalControl failed - exit 0x%08x", hr)); pTerminal->Release(); return hr; } // // Disconnect the terminal. // hr = pTerminalControl->DisconnectTerminal(m_pIGraphBuilder, 0); pTerminalControl->Release(); m_fTerminalConnected = FALSE; if ( FAILED(hr) ) { LOG((MSP_ERROR, "CWaveMSPStream::UnselectTerminal - " "DisconnectTerminal failed - exit 0x%08x", hr)); pTerminal->Release(); return hr; } } LOG((MSP_TRACE, "CWaveMSPStream::UnselectTerminal - exit S_OK")); pTerminal->Release(); return S_OK; } /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// // STDMETHODIMP CWaveMSPStream::StartStream (void) { LOG((MSP_TRACE, "CWaveMSPStream::StartStream - enter")); CLock lock(m_lock); m_DesiredGraphState = State_Running; // // Can't start the stream if we don't know the waveid. // (We create our filters on discovery of the waveid.) // if ( ! m_fHaveWaveID ) { LOG((MSP_WARN, "CWaveMSPStream::StartStream - " "no waveid so nothing to do yet - exit S_OK")); return S_OK; } // // Can't start the stream if no terminal has been selected. // if ( 0 == m_Terminals.GetSize() ) { LOG((MSP_WARN, "CWaveMSPStream::StartStream - " "no Terminal so nothing to do yet - exit S_OK")); return S_OK; } // // Connect the terminal. This does nothing if this call already // connected the terminal and fails if another call has the // terminal connected. // HRESULT hr; hr = ConnectTerminal(m_Terminals[0]); if ( FAILED(hr) ) { FireEvent(CALL_TERMINAL_FAIL, hr, CALL_CAUSE_CONNECT_FAIL); FireEvent(CALL_STREAM_FAIL, hr, CALL_CAUSE_CONNECT_FAIL); LOG((MSP_ERROR, "CWaveMSPStream::StartStream - " "our ConnectTerminal failed - exit 0x%08x", hr)); return hr; } // // Run the stream via the base class method. // hr = CMSPStream::StartStream(); if ( FAILED(hr) ) { FireEvent(CALL_STREAM_FAIL, hr, CALL_CAUSE_UNKNOWN); LOG((MSP_ERROR, "CWaveMSPStream::StartStream - " "Run failed - exit 0x%08x", hr)); return hr; } HRESULT hr2 = FireEvent(CALL_STREAM_ACTIVE, hr, CALL_CAUSE_LOCAL_REQUEST); if ( FAILED(hr2) ) { LOG((MSP_ERROR, "CWaveMSPStream::StartStream - " "FireEvent failed - exit 0x%08x", hr2)); return hr2; } LOG((MSP_TRACE, "CWaveMSPStream::StartStream - exit S_OK")); return S_OK; } /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// // STDMETHODIMP CWaveMSPStream::PauseStream (void) { LOG((MSP_TRACE, "CWaveMSPStream::PauseStream - enter")); CLock lock(m_lock); m_DesiredGraphState = State_Paused; // // Can't pause the stream if we don't know the waveid. // (We create our filters on discovery of the waveid.) // if ( ! m_fHaveWaveID ) { LOG((MSP_WARN, "CWaveMSPStream::PauseStream - " "no waveid so nothing to do yet - exit S_OK")); return S_OK; } // // Can't pause the stream if no terminal has been selected. // if ( 0 == m_Terminals.GetSize() ) { LOG((MSP_WARN, "CWaveMSPStream::PauseStream - " "no Terminal so nothing to do yet - exit S_OK")); return S_OK; } // // Connect the terminal. This does nothing if this call already // connected the terminal and fails if another call has the // terminal connected. // HRESULT hr; hr = ConnectTerminal(m_Terminals[0]); if ( FAILED(hr) ) { FireEvent(CALL_TERMINAL_FAIL, hr, CALL_CAUSE_CONNECT_FAIL); FireEvent(CALL_STREAM_FAIL, hr, CALL_CAUSE_CONNECT_FAIL); LOG((MSP_ERROR, "CWaveMSPStream::StartStream - " "our ConnectTerminal failed - exit 0x%08x", hr)); return hr; } // // Pause the stream via the base class method. // hr = CMSPStream::PauseStream(); if ( FAILED(hr) ) { FireEvent(CALL_STREAM_FAIL, hr, CALL_CAUSE_UNKNOWN); LOG((MSP_ERROR, "CWaveMSPStream::PauseStream - " "Pause failed - exit 0x%08x", hr)); return hr; } HRESULT hr2 = FireEvent(CALL_STREAM_INACTIVE, hr, CALL_CAUSE_LOCAL_REQUEST); if ( FAILED(hr2) ) { LOG((MSP_ERROR, "CWaveMSPStream::PauseStream - " "FireEvent failed - exit 0x%08x", hr2)); return hr2; } LOG((MSP_TRACE, "CWaveMSPStream::PauseStream - exit S_OK")); return S_OK; } /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// // STDMETHODIMP CWaveMSPStream::StopStream (void) { LOG((MSP_TRACE, "CWaveMSPStream::StopStream - enter")); CLock lock(m_lock); m_DesiredGraphState = State_Stopped; // // Nothing to do if we don't know our waveid. // if ( ! m_fHaveWaveID ) { LOG((MSP_WARN, "CWaveMSPStream::StopStream - " "no waveid - exit S_OK")); return S_OK; } // // Nothing to do if no terminal has been selected. // if ( 0 == m_Terminals.GetSize() ) { LOG((MSP_WARN, "CWaveMSPStream::StopStream - " "no Terminal - exit S_OK")); return S_OK; } // // Stop the stream via the base class method. // HRESULT hr; hr = CMSPStream::StopStream(); if ( FAILED(hr) ) { FireEvent(CALL_STREAM_FAIL, hr, CALL_CAUSE_UNKNOWN); LOG((MSP_ERROR, "CWaveMSPStream::StopStream - " "Stop failed - exit 0x%08x", hr)); return hr; } HRESULT hr2 = FireEvent(CALL_STREAM_INACTIVE, hr, CALL_CAUSE_LOCAL_REQUEST); if ( FAILED(hr2) ) { LOG((MSP_ERROR, "CWaveMSPStream::StopStream - " "FireEvent failed - exit 0x%08x", hr2)); return hr2; } LOG((MSP_TRACE, "CWaveMSPStream::StopStream - exit S_OK")); return S_OK; } /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// // HRESULT CWaveMSPStream::SetWaveID(GUID * PermanentGuid) { LOG((MSP_TRACE, "CWaveMSPStream::SetWaveID - enter")); CLock lock(m_lock); // // create the correct wave filter // HRESULT hr; hr= TryCreateCSAFilter( PermanentGuid, &m_pFilter ); if (!(SUCCEEDED(hr))) { LOG((MSP_ERROR, "CWaveMSPStream::SetWaveID - " "Filter creation failed - exit 0x%08x", hr)); return hr; } // // Add the filter. Supply a name to make debugging easier. // WCHAR * pName = (m_Direction == TD_RENDER) ? (L"The Stream's WaveIn (on line device)") : (L"The Stream's WaveOut (on line device)"); hr = m_pIGraphBuilder->AddFilter(m_pFilter, pName); if (!(SUCCEEDED(hr))) { LOG((MSP_ERROR, "CWaveMSPStream::SetWaveID - " "AddFilter failed - exit 0x%08x", hr)); m_pFilter->Release(); return hr; } // // We now have the wave ID. // m_fHaveWaveID = TRUE; LOG((MSP_TRACE, "CWaveMSPStream::SetWaveID - exit S_OK")); return S_OK; } #if 0 ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// // // Create the G711 filter, which we will try to connect if direct // connection fails. // void CWaveMSPStream::CreateAndAddG711(void) { // // Create the G711 filter. // HRESULT hr; hr = CoCreateInstance( CLSID_G711Codec, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void **) &m_pG711Filter ); if (!(SUCCEEDED(hr))) { LOG((MSP_ERROR, "CWaveMSPStream - Failed to create G711 codec: %lx", hr)); // // Method #2 for connection will not be available. // m_pG711Filter = NULL; return; } // // add the G711 filter // hr = m_pIGraphBuilder->AddFilter( m_pG711Filter, NULL ); if (!(SUCCEEDED(hr))) { LOG((MSP_ERROR, "CWaveMSPStream - Failed to add G711 filter: %lx", hr)); // // If we couldn't add it to the graph, then it's useless to us. // Method #2 for connection will not be available. // m_pG711Filter->Release(); m_pG711Filter = NULL; } } //++++++++++++++++++++++++++++++++++++++++++++++++++++++++= // This function suggests a reasonable buffer size // on the wave in filter's output pin. It is called right before // connection. //++++++++++++++++++++++++++++++++++++++++++++++++++++++++= // Dialogic said something about small buffers causing problems for their wave // driver. 20 ms samples were ok on a dual-proc Pentium Pro but caused choppy // sound followed by silence on a single-proc 166 Pentium. I hate to do this // but we had better try raising this for compatibility... :( static const long DESIRED_BUFFER_SIZE_MS = 20; // milliseconds HRESULT CWaveMSPStream::DecideDesiredCaptureBufferSize(IUnknown * pUnknown, long * plDesiredSize) { LOG((MSP_TRACE, "CWaveMSPStream::DecideDesiredCaptureBufferSize - " "enter")); _ASSERTE( ! IsBadReadPtr(pUnknown, sizeof(IUnknown)) ); _ASSERTE( ! IsBadWritePtr(plDesiredSize, sizeof(long)) ); HRESULT hr; IAMStreamConfig * pConfig = NULL; hr = pUnknown->QueryInterface(IID_IAMStreamConfig, (void **) &pConfig ); if ( FAILED(hr) ) { LOG((MSP_ERROR, "CWaveMSPStream::DecideDesiredCaptureBufferSize" " - IAMStreamConfig QI failed on IUnknown 0x%08x; hr = 0x%08x", pUnknown, hr)); return hr; } AM_MEDIA_TYPE * pMediaType; hr = pConfig->GetFormat(&pMediaType); pConfig->Release(); if ( FAILED(hr) ) { LOG((MSP_ERROR, "CWaveMSPStream::DecideDesiredCaptureBufferSize" " - GetFormat failed; hr = 0x%08x", hr)); return hr; } _ASSERTE( pMediaType->cbFormat >= sizeof(WAVEFORMATEX) ); *plDesiredSize = DESIRED_BUFFER_SIZE_MS * ((WAVEFORMATEX *) (pMediaType->pbFormat) )->nChannels * ( ((WAVEFORMATEX *) (pMediaType->pbFormat) )->nSamplesPerSec / 1000) * ( ((WAVEFORMATEX *) (pMediaType->pbFormat) )->wBitsPerSample / 8); DeleteMediaType(pMediaType); LOG((MSP_TRACE, "CWaveMSPStream::DecideDesiredCaptureBufferSize - " "exit S_OK")); return S_OK; } //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // ManipulateAllocatorProperties // // This is a helper function that sets up the allocator properties on the // capture filter, given the interface pointer required for doing so and // an interface pointer that is used to discover downstream allocator // requirements. // we are already in a lock; no need to do locking here. // //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ HRESULT CWaveMSPStream::ManipulateAllocatorProperties (IAMBufferNegotiation * pNegotiation, IMemInputPin * pMemInputPin) { LOG((MSP_TRACE, "CWaveMSPStream::ManipulateAllocatorProperties - enter")); HRESULT hr; ALLOCATOR_PROPERTIES props; hr = pMemInputPin->GetAllocatorRequirements(&props); if ( SUCCEEDED(hr) ) { LOG((MSP_TRACE, "CWaveMSPStream::ManipulateAllocatorProperties - " "using downstream allocator requirements")); } else { LOG((MSP_TRACE, "CWaveMSPStream::ManipulateAllocatorProperties - " "using our default allocator properties")); long lDesiredSize = 0; hr = DecideDesiredCaptureBufferSize(pNegotiation, &lDesiredSize); if ( FAILED(hr) ) { LOG((MSP_ERROR, "CWaveMSPStream::ManipulateAllocatorProperties - " "DecideDesiredCaptureBufferSize failed - exit 0x%08x", hr)); return hr; } props.cBuffers = 32; // we use 32 to avoid starvation, just as we do in the terminal manager. props.cbBuffer = lDesiredSize; props.cbAlign = -1; // means "default" props.cbPrefix = -1; // means "default" } hr = pNegotiation->SuggestAllocatorProperties(&props); if ( FAILED(hr) ) { LOG((MSP_ERROR, "CWaveMSPStream::ManipulateAllocatorProperties - " "SuggestAllocatorProperties failed - exit 0x%08x", hr)); return hr; } LOG((MSP_TRACE, "CWaveMSPStream::ManipulateAllocatorProperties - " "exit S_OK")); return S_OK; } //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // SetupWaveIn // // This is a helper function that sets up the allocator properties on the // capture filter, given the terminal's pin and our filter's pin. This // involves deciding where the capture interfaces should be found, checkin // if the downstream filters have allocator requirements, and then applying // either these requirements or our default requirements to the capture // filter. // we are already in a lock; no need to do locking here. // //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ HRESULT CWaveMSPStream::SetupWaveIn( IPin * pOutputPin, IPin * pInputPin ) { LOG((MSP_TRACE, "CWaveMSPStream::SetupWaveIn - enter")); // // Ask the output pin for its buffer negotiation interface. // HRESULT hr; IAMBufferNegotiation * pNegotiation; hr = pOutputPin->QueryInterface(IID_IAMBufferNegotiation, (void **) &pNegotiation); if ( FAILED(hr) ) { LOG((MSP_ERROR, "IAMBufferNegotiation QI failed - hr = 0x%08x", hr)); return hr; } // // Ask the input pin for its meminputpin interface. // IMemInputPin * pMemInputPin; hr = pInputPin->QueryInterface(IID_IMemInputPin, (void **) &pMemInputPin); if ( FAILED(hr) ) { LOG((MSP_ERROR, "IMemInputPin QI failed - hr = 0x%08x", hr)); pNegotiation->Release(); return hr; } // // now set the properties on the negotiation interface, depending // on the properties that are set on the meminputpin interface // hr = ManipulateAllocatorProperties(pNegotiation, pMemInputPin); pNegotiation->Release(); pMemInputPin->Release(); if ( FAILED(hr) ) { LOG((MSP_ERROR, "ManipulateAllocatorProperties - hr = 0x%08x", hr)); return hr; } LOG((MSP_TRACE, "CWaveMSPStream::SetupWaveIn - exit S_OK")); return S_OK; } #endif //++++++++++++++++++++++++++++++++++++++++++++++++++++++++= // // This function is for debugging purposes only. It pops up a // couple of message boxes telling you various information about // media formats and allocator properties. It's called after // connection has taken place. pPin is the output pin of the // wavein filter. // //++++++++++++++++++++++++++++++++++++++++++++++++++++++++= HRESULT CWaveMSPStream::ExamineWaveInProperties(IPin *pPin) { LOG((MSP_TRACE, "CWaveMSPStream::ExamineWaveInProperties - enter")); HRESULT hr; IAMBufferNegotiation * pNegotiation = NULL; hr = pPin->QueryInterface(IID_IAMBufferNegotiation, (void **) &pNegotiation ); if ( FAILED(hr) ) { LOG((MSP_ERROR, "CWaveMSPStream::ExamineWaveInProperties - " "IAMBufferNegotiation QI failed on pin 0x%08x; hr = 0x%08x", pPin, hr)); return hr; } ALLOCATOR_PROPERTIES prop; hr = pNegotiation->GetAllocatorProperties(&prop); pNegotiation->Release(); if ( FAILED(hr) ) { LOG((MSP_ERROR, "CWaveMSPStream::ExamineWaveInProperties - " "GetAllocatorProperties failed; hr = 0x%08x", hr)); return hr; } LOG((MSP_TRACE, "GetAllocatorProperties info:\n" "buffer count: %d\n" "size of each buffer: %d bytes\n" "alignment multiple: %d\n" "each buffer has a prefix: %d bytes", prop.cBuffers, prop.cbBuffer, prop.cbAlign, prop.cbPrefix )); IAMStreamConfig * pConfig = NULL; hr = pPin->QueryInterface(IID_IAMStreamConfig, (void **) &pConfig ); if ( FAILED(hr) ) { LOG((MSP_ERROR, "CWaveMSPStream::ExamineWaveInProperties - " "IAMStreamConfig QI failed on pin 0x%08x; hr = 0x%08x", pPin, hr)); return hr; } AM_MEDIA_TYPE * pMediaType; hr = pConfig->GetFormat(&pMediaType); pConfig->Release(); if ( FAILED(hr) ) { LOG((MSP_ERROR, "CWaveMSPStream::ExamineWaveInProperties - " "GetFormat failed; hr = 0x%08x", hr)); return hr; } _ASSERTE( pMediaType->cbFormat >= sizeof(WAVEFORMATEX) ); LOG((MSP_TRACE, "GetFormat info:\n" "sample size: %d bytes\n" "channels: %d\n" "samples per second: %d\n" "bits per sample: %d\n", pMediaType->lSampleSize, ((WAVEFORMATEX *) (pMediaType->pbFormat) )->nChannels, ((WAVEFORMATEX *) (pMediaType->pbFormat) )->nSamplesPerSec, ((WAVEFORMATEX *) (pMediaType->pbFormat) )->wBitsPerSample )); DeleteMediaType(pMediaType); LOG((MSP_TRACE, "CWaveMSPStream::ExamineWaveInProperties - " "exit S_OK")); return S_OK; } /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// // // Add the terminal to the graph and connect it to our // filters, if it is not already in use. // HRESULT CWaveMSPStream::ConnectTerminal(ITTerminal * pTerminal) { LOG((MSP_TRACE, "CWaveMSPStream::ConnectTerminal - enter")); // // Find out the terminal's internal state. // TERMINAL_STATE state; HRESULT hr; hr = pTerminal->get_State( &state ); if ( FAILED(hr) ) { LOG((MSP_ERROR, "CWaveMSPStream::ConnectTerminal - " "get_State on terminal failed - exit 0x%08x", hr)); return hr; } // // If we've already connected the terminal on this stream, then // there is nothing for us to do. Just assert that the terminal // also thinks it's connected. // if ( m_fTerminalConnected ) { _ASSERTE( state == TS_INUSE ); LOG((MSP_ERROR, "CWaveMSPStream::ConnectTerminal - " "terminal already connected on this stream - exit S_OK")); return S_OK; } // // Otherwise we need to connect the terminal on this call. If the // terminal is already connected on another call, we must fail. Note // that since we are making several calls on the terminal here, the // terminal could become connected on another call while we are // in the process of doing this. If this happens, the we will just fail // later. // if ( state == TS_INUSE ) { LOG((MSP_ERROR, "CWaveMSPStream::ConnectTerminal - " "terminal in use - exit TAPI_E_TERMINALINUSE")); return TAPI_E_TERMINALINUSE; } // // Get the ITTerminalControl interface. // ITTerminalControl * pTerminalControl; hr = m_Terminals[0]->QueryInterface(IID_ITTerminalControl, (void **) &pTerminalControl); if ( FAILED(hr) ) { LOG((MSP_ERROR, "CWaveMSPStream::ConnectTerminal - " "QI for ITTerminalControl failed - exit 0x%08x", hr)); return hr; } // // Find out how many pins the terminal has. If not one then bail as // we have no idea what to do with multiple-pin terminals at this point. // DWORD dwNumPinsAvailable; hr = pTerminalControl->ConnectTerminal(m_pIGraphBuilder, m_Direction, &dwNumPinsAvailable, NULL); if ( FAILED(hr) ) { LOG((MSP_ERROR, "CWaveMSPStream::ConnectTerminal - " "query for number of terminal pins failed - exit 0x%08x", hr)); pTerminalControl->Release(); return hr; } if ( 1 != dwNumPinsAvailable ) { LOG((MSP_ERROR, "CWaveMSPStream::ConnectTerminal - " "unsupported number of terminal pins - exit E_FAIL")); pTerminalControl->Release(); return E_FAIL; } IPin * pTerminalPin; // // Actually connect the terminal. // hr = pTerminalControl->ConnectTerminal(m_pIGraphBuilder, m_Direction, &dwNumPinsAvailable, &pTerminalPin); if ( FAILED(hr) ) { pTerminalControl->Release(); LOG((MSP_ERROR, "CWaveMSPStream::ConnectTerminal - " "ConnectTerminal on terminal failed - exit 0x%08x", hr)); return hr; } if (IsBadReadPtr(pTerminalPin,sizeof(IPin))) { // // bad pin // pTerminalControl->Release(); LOG((MSP_ERROR, "CWaveMSPStream::ConnectTerminal - " "bad IPin returned from ConnectTerminal")); return E_POINTER; } // // Now make the connection between our filters and the terminal's pin. // hr = ConnectToTerminalPin(pTerminalPin); pTerminalPin->Release(); if ( FAILED(hr) ) { pTerminalControl->DisconnectTerminal(m_pIGraphBuilder, 0); pTerminalControl->Release(); LOG((MSP_ERROR, "CWaveMSPStream::ConnectTerminal - " "ConnectToTerminalPin failed - exit 0x%08x", hr)); return hr; } // // Now we are actually connected. Update our state and perform postconnection // (ignore postconnection error code). // m_fTerminalConnected = TRUE; pTerminalControl->CompleteConnectTerminal(); pTerminalControl->Release(); LOG((MSP_TRACE, "CWaveMSPStream::ConnectTerminal - exit S_OK")); return S_OK; } //++++++++++++++++++++++++++++++++++++++++++++++++++++++++= // // Tries to connect the waveOut filter. First it tries a // direct connection, then with an intermediate G711 // codec, then an intelligent connect which may draw in // more filters. //++++++++++++++++++++++++++++++++++++++++++++++++++++++++= void ShowMediaTypes(IEnumMediaTypes * pEnum) { AM_MEDIA_TYPE * pMediaType; while (pEnum->Next(1, &pMediaType, NULL) == S_OK) { if ( pMediaType->cbFormat < sizeof(WAVEFORMATEX) ) { LOG((MSP_TRACE, "*** Media Type: *** non-wave")); } else { LOG((MSP_TRACE,"*** Media Type: *** " "sample size: %d bytes *** " "channels: %d *** " "samples per second: %d *** " "bits per sample: %d", pMediaType->lSampleSize, ((WAVEFORMATEX *) (pMediaType->pbFormat) )->nChannels, ((WAVEFORMATEX *) (pMediaType->pbFormat) )->nSamplesPerSec, ((WAVEFORMATEX *) (pMediaType->pbFormat) )->wBitsPerSample )); } DeleteMediaType(pMediaType); } } HRESULT CWaveMSPStream::TryToConnect( IPin * pOutputPin, // on the capture filter or terminal IPin * pInputPin // on the render filter or terminal ) { LOG((MSP_TRACE, "TryToConnect - enter")); HRESULT hr; IEnumMediaTypes * pEnum; hr = pOutputPin->EnumMediaTypes(&pEnum); if (SUCCEEDED(hr)) { LOG((MSP_TRACE, "Output pin media types:")); ShowMediaTypes(pEnum); pEnum->Release(); } hr = pInputPin->EnumMediaTypes(&pEnum); if (SUCCEEDED(hr)) { LOG((MSP_TRACE, "Input pin media types:")); ShowMediaTypes(pEnum); pEnum->Release(); } // // Method 1: direct connection // hr = m_pIGraphBuilder->ConnectDirect( pOutputPin, pInputPin, NULL ); if ( SUCCEEDED(hr) ) { LOG((MSP_TRACE, "TryToConnect: direct connection worked - exit S_OK")); return S_OK; } LOG((MSP_ERROR, "TryToConnect - direct connection failed - %lx", hr)); // // Method 1.5: work around DirectShow bug for Unimodem. // Try 8 KHz 16-bit mono explicitly // AM_MEDIA_TYPE MediaType; WAVEFORMATEX WaveFormatEx; MediaType.majortype = MEDIATYPE_Audio; MediaType.subtype = MEDIASUBTYPE_PCM; MediaType.bFixedSizeSamples = TRUE; MediaType.bTemporalCompression = FALSE; MediaType.lSampleSize = 2; MediaType.formattype = FORMAT_WaveFormatEx; MediaType.pUnk = NULL; MediaType.cbFormat = sizeof( WAVEFORMATEX ); MediaType.pbFormat = (LPBYTE) & WaveFormatEx; WaveFormatEx.wFormatTag = WAVE_FORMAT_PCM; WaveFormatEx.nChannels = 1; WaveFormatEx.nSamplesPerSec = 8000; WaveFormatEx.nAvgBytesPerSec = 16000; WaveFormatEx.nBlockAlign = 2; WaveFormatEx.wBitsPerSample = 16; WaveFormatEx.cbSize = 0; IAMStreamConfig * pConfig; hr = pOutputPin->QueryInterface(IID_IAMStreamConfig, (void **) &pConfig ); if ( FAILED(hr) ) { LOG((MSP_ERROR, "CWaveMSPStream::TryToConnect" " - IAMStreamConfig QI failed on output pin 0x%08x; hr = 0x%08x", pOutputPin, hr)); } else { AM_MEDIA_TYPE * pOldMediaType; hr = pConfig->GetFormat(&pOldMediaType); if ( FAILED(hr) ) { LOG((MSP_ERROR, "CWaveMSPStream::TryToConnect - " "GetFormat failed - 0x%08x", hr)); } else { // Suggest the new format. If it fails, we want to know about it // as something is wrong. hr = pConfig->SetFormat(&MediaType); if ( FAILED(hr) ) { LOG((MSP_ERROR, "CWaveMSPStream::TryToConnect - " "SetFormat failed - 0x%08x", hr)); } else { hr = m_pIGraphBuilder->ConnectDirect( pOutputPin, pInputPin, &MediaType ); if ( SUCCEEDED(hr) ) { LOG((MSP_TRACE, "TryToConnect: direct connection with explicit " "WaveIn 8KHz 16-bit setting worked - exit S_OK")); DeleteMediaType(pOldMediaType); pConfig->Release(); return S_OK; } else { // restore old type, best effort hr = pConfig->SetFormat(pOldMediaType); if ( FAILED(hr) ) { LOG((MSP_ERROR, "CWaveMSPStream::TryToConnect - " "SetFormat failed to restore old type - 0x%08x", hr)); } } } DeleteMediaType(pOldMediaType); } pConfig->Release(); } #if 0 LOG((MSP_ERROR, "TryToConnect - direct connection with explicit " "WaveIn 8KHz 16-bit setting failed - %lx", hr)); // // Method 2: direct connection with G711 filter in between. // If we haven't created and added the G711 filter to the graph yet, // do so now. // if ( ! m_pG711Filter ) { CreateAndAddG711(); } // // If the CreateAndAddG711 method worked, now or previously, then try to // use the G711. // if (m_pG711Filter) { IPin * pG711InputPin = NULL; hr = FindPinInFilter( false, // want input pin m_pG711Filter, &pG711InputPin ); if ( SUCCEEDED(hr) ) { hr = m_pIGraphBuilder->ConnectDirect( pOutputPin, pG711InputPin, NULL ); // We don't release the G711's input pin here because we must // hang onto it in order to break the connection if any of the // subsequent steps fail. if ( SUCCEEDED(hr) ) { IPin * pG711OutputPin = NULL; hr = FindPinInFilter( true, // want output pin m_pG711Filter, &pG711OutputPin ); if ( SUCCEEDED(hr) ) { hr = m_pIGraphBuilder->ConnectDirect( pG711OutputPin, pInputPin, NULL ); pG711OutputPin->Release(); if ( SUCCEEDED(hr) ) { LOG((MSP_TRACE, "TryToConnect - G711 connection succeeded - exit S_OK")); // Held onto this in case of failure... see above pG711InputPin->Release(); return S_OK; } else { LOG((MSP_ERROR, "TryToConnect - could not connect " "G711 codec's output pin - %lx", hr)); } } else { LOG((MSP_ERROR, "TryToConnect - could not find " "G711 codec's input pin - %lx", hr)); } if ( FAILED(hr) ) { // // The first G711 connection succeeded but something else // subsequently failed. This means we must disconnect the left // end of the G711 filter. Luckily, we held onto the G711 filter's // input pin above. We must disconnect the them here, otherwise // method #3 won't work. // hr = m_pIGraphBuilder->Disconnect(pOutputPin); LOG((MSP_ERROR, "TryToConnect - error undoing what we did - could not " "disconnect the wave filter's output pin! hr = 0x%08x", hr)); hr = m_pIGraphBuilder->Disconnect(pG711InputPin); LOG((MSP_ERROR, "TryToConnect - error undoing what we did - could not " "disconnect the wave filter's output pin! hr = 0x%08x", hr)); // // Now we no longer need to talk to the pin... // pG711InputPin->Release(); // // And the G711 filter itself sticks around in the graph for next time. // } } else { LOG((MSP_ERROR, "TryToConnect - could not connect " "G711 codec's input pin - %lx", hr)); } } else { LOG((MSP_ERROR, "TryToConnect - could not find " "G711 codec's input pin - %lx", hr)); } } else { hr = E_FAIL; LOG((MSP_ERROR, "TryToConnect - G711 codec does not exist")); } LOG((MSP_TRACE, "TryToConnect - G711 connection failed - %lx", hr)); // // Method 3: intelligent connection, which may pull in who knows what other filters // #ifdef ALLOW_INTELLIGENT_CONNECTION hr = m_pIGraphBuilder->Connect( pOutputPin, pInputPin ); #else // ALLOW_INTELLIGENT_CONNECTION LOG((MSP_ERROR, "TryToConnect - NOTE: we never allow intelligent connection")); hr = E_FAIL; #endif // ALLOW_INTELLIGENT_CONNECTION #endif if ( FAILED(hr) ) { LOG((MSP_ERROR, "TryToConnect - intelligent connection failed - %lx", hr)); return hr; } LOG((MSP_TRACE, "TryToConnect: intelligent connection worked - exit S_OK")); return S_OK; } ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// HRESULT CWaveMSPStream::ConnectToTerminalPin(IPin * pTerminalPin) { LOG((MSP_TRACE, "CWaveMSPStream::ConnectToTerminalPin - enter")); HRESULT hr = S_OK; IPin * pMyPin; hr = FindPin( &pMyPin ); if (!SUCCEEDED(hr)) { LOG((MSP_ERROR, "CWaveMSPStream::ConnectToTerminalPin - " "could not find pin - exit 0x%08x", hr)); return hr; // we can't continue without this pin } // The OUTPUT pin from WAVEIN; the INPUT pin from WAVEOUT IPin * pOutputPin = ( m_Direction == TD_RENDER ) ? pMyPin : pTerminalPin; IPin * pInputPin = ( m_Direction == TD_CAPTURE ) ? pMyPin : pTerminalPin; #if 0 // don't care if this fails SetupWaveIn(pOutputPin, pInputPin); #endif hr = TryToConnect(pOutputPin, pInputPin); if ( SUCCEEDED(hr) ) { // don't care if this fails... ExamineWaveInProperties(pOutputPin); } pMyPin->Release(); if ( FAILED(hr) ) { LOG((MSP_ERROR, "CWaveMSPStream::ConnectToTerminalPin - " "could not connect to pin - exit 0x%08x", hr)); return hr; } LOG((MSP_TRACE, "CWaveMSPStream::ConnectToTerminalPin - exit S_OK")); return S_OK; } //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ HRESULT CWaveMSPStream::FindPinInFilter( BOOL bWantOutputPin, // IN: if false, we want the input pin IBaseFilter * pFilter, // IN: the filter to examine IPin ** ppPin // OUT: the pin we found ) { HRESULT hr; IEnumPins * pEnumPins; *ppPin = NULL; // enumerate the pins on the filter hr = pFilter->EnumPins( &pEnumPins ); if (!(SUCCEEDED(hr))) { return hr; } // go through the pins while (TRUE) { PIN_DIRECTION pd; hr = pEnumPins->Next( 1, ppPin, NULL ); if (S_OK != hr) { // didn't find a pin! break; } // get the pin info hr = (*ppPin)->QueryDirection( &pd ); // does it meet the criteria? if (bWantOutputPin && (pd == PINDIR_OUTPUT)) { // yes break; } if ( ! bWantOutputPin && (pd == PINDIR_INPUT)) { // yes break; } (*ppPin)->Release(); *ppPin = NULL; } pEnumPins->Release(); if (NULL == *ppPin) { // error return E_FAIL; } return S_OK; } //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // FindPin // // Finds the first pin in the filter that meets criteria. // For bWaveIn == TRUE, the pin must be direction PINDIR_OUTPUT // For bWaveIn == FALSE, the pin must be direction PINDIR_INPUT // //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ HRESULT CWaveMSPStream::FindPin( IPin ** ppPin ) { return FindPinInFilter(m_Direction == TD_RENDER, m_pFilter, ppPin); } //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // // ProcessGraphEvent // // Sends an event to the app when we get an event from the filter graph. // //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ HRESULT CWaveMSPStream::ProcessGraphEvent( IN long lEventCode, IN LONG_PTR lParam1, IN LONG_PTR lParam2 ) { LOG((MSP_EVENT, "CWaveMSPStream::ProcessGraphEvent - enter")); HRESULT hr = S_OK; switch (lEventCode) { case EC_COMPLETE: hr = FireEvent(CALL_STREAM_INACTIVE, (HRESULT) lParam1, CALL_CAUSE_UNKNOWN); break; case EC_USERABORT: hr = FireEvent(CALL_STREAM_INACTIVE, S_OK, CALL_CAUSE_UNKNOWN); break; case EC_ERRORABORT: case EC_STREAM_ERROR_STOPPED: case EC_STREAM_ERROR_STILLPLAYING: case EC_ERROR_STILLPLAYING: hr = FireEvent(CALL_STREAM_FAIL, (HRESULT) lParam1, CALL_CAUSE_UNKNOWN); break; default: LOG((MSP_EVENT, "CWaveMSPStream::ProcessGraphEvent - " "ignoring event code %d", lEventCode)); break; } if ( FAILED(hr) ) { LOG((MSP_ERROR, "CWaveMSPStream::ProcessGraphEvent - " "FireEvent failed - exit 0x%08x", hr)); return hr; } LOG((MSP_EVENT, "CWaveMSPStream::ProcessGraphEvent - exit S_OK")); return S_OK; } HRESULT CWaveMSPStream::FireEvent( IN MSP_CALL_EVENT type, IN HRESULT hrError, IN MSP_CALL_EVENT_CAUSE cause ) { LOG((MSP_EVENT, "CWaveMSPStream::FireEvent - enter")); // // First, need to check if the call is shutting down. This is important // because UnselectTerminal can fire an event, and UnselectTerminal can // be called within ITStream::Shutdown. We can safely discard such // events because there is nothing the app can do with them anyway. // // Note on locking: It is convenient to check the m_pMSPCall here // and we don't use it until the end of the method, so we simply lock // during the entire method. This could be optimized at the expense of // some code complexity; note that we also need to lock while accessing // m_Terminals. // CLock lock(m_lock); if ( m_pMSPCall == NULL ) { LOG((MSP_EVENT, "FireEvent - call is shutting down; dropping event - exit S_OK")); return S_OK; } // // Create the event structure. Must use "new" as it will be // "delete"d later. // MSPEVENTITEM * pEventItem = AllocateEventItem(); if (pEventItem == NULL) { LOG((MSP_ERROR, "CWaveMSPStream::FireEvent - " "can't create MSPEVENTITEM structure - exit E_OUTOFMEMORY")); return E_OUTOFMEMORY; } // // Fill in the necessary fields for the event structure. // pEventItem->MSPEventInfo.dwSize = sizeof(MSP_EVENT_INFO); pEventItem->MSPEventInfo.Event = ME_CALL_EVENT; ITTerminal * pTerminal = NULL; if ( 0 != m_Terminals.GetSize() ) { _ASSERTE( 1 == m_Terminals.GetSize() ); pTerminal = m_Terminals[0]; pTerminal->AddRef(); } ITStream * pStream = (ITStream *) this; pStream->AddRef(); pEventItem->MSPEventInfo.MSP_CALL_EVENT_INFO.Type = type; pEventItem->MSPEventInfo.MSP_CALL_EVENT_INFO.Cause = cause; pEventItem->MSPEventInfo.MSP_CALL_EVENT_INFO.pStream = pStream; pEventItem->MSPEventInfo.MSP_CALL_EVENT_INFO.pTerminal = pTerminal; pEventItem->MSPEventInfo.MSP_CALL_EVENT_INFO.hrError = hrError; // // Send the event to the app. // HRESULT hr = m_pMSPCall->HandleStreamEvent(pEventItem); if (FAILED(hr)) { LOG((MSP_ERROR, "CWaveMSPStream::FireEvent - " "HandleStreamEvent failed - returning 0x%08x", hr)); pStream->Release(); pTerminal->Release(); FreeEventItem(pEventItem); return hr; } LOG((MSP_EVENT, "CWaveMSPStream::FireEvent - exit S_OK")); return S_OK; } DEFINE_GUID(CLSID_Proxy, 0x17CCA71BL, 0xECD7, 0x11D0, 0xB9, 0x08, 0x00, 0xA0, 0xC9, 0x22, 0x31, 0x96); DEFINE_GUID(CLSID_WDM_RENDER, 0x65E8773EL, 0x8F56, 0x11D0, 0xA3, 0xB9, 0x00, 0xA0, 0xC9, 0x22, 0x31, 0x96); // {F420CB9C-B19D-11d2-A286-00C04F8EC951} DEFINE_GUID(KSPROPSETID_MODEMCSA, 0xf420cb9c, 0xb19d, 0x11d2, 0xa2, 0x86, 0x0, 0xc0, 0x4f, 0x8e, 0xc9, 0x51); HRESULT CheckFilterPropery( IBaseFilter *CsaFilter, const GUID *GuidToMatch ) { IKsPropertySet *pKsPropSet = NULL; HRESULT hr = S_OK; GUID PermanentGuid; hr = CsaFilter->QueryInterface(IID_IKsPropertySet, (void **)&pKsPropSet); if (SUCCEEDED(hr)) { DWORD BytesReturned; hr = pKsPropSet->Get(KSPROPSETID_MODEMCSA, 0, NULL, 0, (LPVOID)&PermanentGuid, sizeof(PermanentGuid), &BytesReturned ); pKsPropSet->Release(); if (IsEqualGUID((PermanentGuid), *GuidToMatch)) { hr=S_OK; } else { hr=E_FAIL; } } return hr; } HRESULT FindModemCSA( IN GUID *PermanentGuid, IBaseFilter ** ppFilter ) { ICreateDevEnum *pCreateDevEnum; HRESULT hr; // // create system device enumerator // hr = CoCreateInstance( CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, IID_ICreateDevEnum, (void**)&pCreateDevEnum ); if (SUCCEEDED(hr)) { IEnumMoniker *pEnumMoniker = NULL; hr = pCreateDevEnum->CreateClassEnumerator( CLSID_WDM_RENDER, &pEnumMoniker, 0 ); pCreateDevEnum->Release(); if (hr == S_OK) { pEnumMoniker->Reset(); while( NULL == *ppFilter ) { IMoniker *pMon; VARIANT var; hr = pEnumMoniker->Next(1, &pMon, NULL); if ( S_OK != hr ) { break; } // Bind to selected device hr = pMon->BindToObject( 0, 0, IID_IBaseFilter, (void**)ppFilter ); pMon->Release(); if (SUCCEEDED(hr)) { hr=CheckFilterPropery( *ppFilter, PermanentGuid ); if (SUCCEEDED(hr)) { break; } else { (*ppFilter)->Release(); *ppFilter=NULL; } } } } } return hr; } HRESULT TryCreateCSAFilter( IN GUID *PermanentGuid, OUT IBaseFilter **ppCSAFilter ) { HRESULT hr = E_UNEXPECTED; if (ppCSAFilter != NULL) { *ppCSAFilter=NULL; hr = FindModemCSA(PermanentGuid,ppCSAFilter); } return hr; } // eof