/*++ Copyright (c) 1997 Microsoft Corporation Module Name: H323call.cpp Abstract: This module contains implementation of CH323MSPCall. Author: Mu Han (muhan) 5-September-1998 --*/ #include "stdafx.h" #include "common.h" #include STDMETHODIMP CH323MSPCall::CreateStream( IN long lMediaType, IN TERMINAL_DIRECTION Direction, IN OUT ITStream ** ppStream ) /*++ Routine Description: This method is called by the app to create a new stream. If this happens before the TSP's new call notification, we just create the object. If the call is already connected, we will send the TSP an open channel request when the first terminal is selected. Arguments: lMediaType - The media type of this stream. Direction - The direction of this stream. ppStream - The memory address to store the returned pointer. Return Value: HRESULT. --*/ { LOG((MSP_TRACE, "CH323MSPCall.CreateStream--dwMediaType:%x, Direction:%x, ppStream %x", lMediaType, Direction, ppStream )); // first call the base class's implementation to create the stream object. HRESULT hr = CMSPCallBase::CreateStream( lMediaType, Direction, ppStream ); return hr; } STDMETHODIMP CH323MSPCall::RemoveStream( IN ITStream * pStream ) /*++ Routine Description: This method is called by the app to remove a stream. If the channel is already connected, we need to send the TSP an close channel request. Arguments: pStream - The stream to be removed. Return Value: HRESULT. --*/ { LOG((MSP_TRACE, "CH323MSPCall::RemoveStream - pStream %x", pStream)); // acquire the lock before accessing the stream object list. CLock lock(m_lock); if (m_Streams.Find(pStream) < 0) { LOG((MSP_ERROR, "CH323MSPCall::RemoveStream - Stream %x is not found.", pStream)); return E_INVALIDARG; } // Close the TSP channel if it is active. HANDLE htChannel; if ((htChannel = ((CH323MSPStream *)pStream)->TSPChannel()) != NULL) { SendTSPMessage( H323MSP_CLOSE_CHANNEL_COMMAND, NULL, htChannel ); } return CMSPCallMultiGraph::RemoveStream(pStream); } HRESULT CH323MSPCall::Init( IN CMSPAddress * pMSPAddress, IN MSP_HANDLE htCall, IN DWORD dwReserved, IN DWORD dwMediaType ) /*++ Routine Description: This method is called when the call is first created. It sets up the streams based on the mediatype specified. Arguments: pMSPAddress - The pointer to the address object. htCall - The handle to the Call in TAPI's space. Used in sending events. dwReserved - Reserved. dwMediaType - The media type of this call. Return Value: HRESULT. --*/ { LOG((MSP_TRACE, "H323MSP call %x initialize entered," " pMSPAddress:%x, htCall %x, dwMediaType %x", this, pMSPAddress, htCall, dwMediaType )); #ifdef DEBUG_REFCOUNT LOG((MSP_TRACE, "Number of Streams alive: %d", g_lStreamObjects)); if (g_lStreamObjects != 0) { DebugBreak(); } #endif // Call the base class's init. HRESULT hr= CMSPCallMultiGraph::Init( pMSPAddress, htCall, dwReserved, dwMediaType ); if (FAILED(hr)) { LOG((MSP_ERROR, "MSPCallMultiGraph init failed:%x", hr)); return hr; } // create streams based on the media types. if (dwMediaType & TAPIMEDIATYPE_AUDIO) { ITStream * pStream; // create a stream object. hr = InternalCreateStream(TAPIMEDIATYPE_AUDIO, TD_RENDER, &pStream); if (FAILED(hr)) { LOG((MSP_ERROR, "create audio render stream failed:%x", hr)); return hr; } // The stream is already in our array, we don't need this pointer. pStream->Release(); // create a stream object. hr = InternalCreateStream(TAPIMEDIATYPE_AUDIO, TD_CAPTURE, &pStream); if (FAILED(hr)) { LOG((MSP_ERROR, "create audio capture stream failed:%x", hr)); return hr; } // The stream is already in our array, we don't need this pointer. pStream->Release(); } if (dwMediaType & TAPIMEDIATYPE_VIDEO) { ITStream * pStream; // create a stream object. hr = InternalCreateStream(TAPIMEDIATYPE_VIDEO, TD_RENDER, &pStream); if (FAILED(hr)) { LOG((MSP_ERROR, "create video render stream failed:%x", hr)); return hr; } // The stream is already in our array, we don't need this pointer. pStream->Release(); // create a stream object. hr = InternalCreateStream(TAPIMEDIATYPE_VIDEO, TD_CAPTURE, &pStream); if (FAILED(hr)) { LOG((MSP_ERROR, "create video capture stream failed:%x", hr)); return hr; } // The stream is already in our array, we don't need this pointer. pStream->Release(); } m_fCallConnected = FALSE; m_fShutDown = FALSE; return S_OK; } HRESULT CH323MSPCall::ShutDown() /*++ Routine Description: Just call the internal shut down method. Arguments: Return Value: HRESULT. --*/ { InternalShutDown(); // acquire the lock on the call. CLock lock(m_lock); // release all the streams for (int i = m_Streams.GetSize() - 1; i >= 0; i --) { m_Streams[i]->Release(); } m_Streams.RemoveAll(); return S_OK; } HRESULT CH323MSPCall::InternalShutDown() /*++ Routine Description: First call the base class's shutdown and then release all the participant objects. Arguments: Return Value: HRESULT. --*/ { // acquire the lock on the call. CLock lock(m_lock); if (m_fShutDown) { return S_OK; } // Shutdown all the streams for (int i = m_Streams.GetSize() - 1; i >= 0; i --) { UnregisterWaitEvent(i); ((CMSPStream*)m_Streams[i])->ShutDown(); } m_ThreadPoolWaitBlocks.RemoveAll(); m_fShutDown = TRUE; return S_OK; } template HRESULT CreateStreamHelper( IN T * pT, IN HANDLE hAddress, IN CH323MSPCall* pMSPCall, IN IMediaEvent * pGraph, IN DWORD dwMediaType, IN TERMINAL_DIRECTION Direction, OUT ITStream ** ppITStream ) /*++ Routine Description: Create a stream object and initialize it. This method is called internally to create a stream object of different class. Arguments: hAddress - the handle to the address object. pCall - the call object. pGraph - the filter graph for this stream. dwMediaType - the media type of the stream. Direction - the direction of the steam. ppITStream - the interface on this stream object. Return Value: HRESULT. --*/ { CComObject * pCOMMSPStream; HRESULT hr = CComObject::CreateInstance(&pCOMMSPStream); if (NULL == pCOMMSPStream) { LOG((MSP_ERROR, "CreateMSPStream:could not create stream:%x", hr)); return hr; } // get the interface pointer. hr = pCOMMSPStream->_InternalQueryInterface( IID_ITStream, (void **)ppITStream ); if (FAILED(hr)) { LOG((MSP_ERROR, "CreateMSPStream:QueryInterface failed: %x", hr)); delete pCOMMSPStream; return hr; } // Initialize the object. hr = pCOMMSPStream->Init( hAddress, pMSPCall, pGraph, dwMediaType, Direction ); if (FAILED(hr)) { LOG((MSP_ERROR, "CreateMSPStream:call init failed: %x", hr)); (*ppITStream)->Release(); return hr; } return S_OK; } HRESULT CH323MSPCall::CreateStreamObject( IN DWORD dwMediaType, IN TERMINAL_DIRECTION Direction, IN IMediaEvent * pGraph, IN ITStream ** ppStream ) /*++ Routine Description: Create a media stream object based on the mediatype and direction. Arguments: pMediaType - TAPI3 media type. Direction - direction of this stream. IMediaEvent - The filter graph used in this stream. ppStream - the return pointer of the stream interface Return Value: HRESULT. --*/ { LOG((MSP_TRACE, "CreateStreamObject, entered")); HRESULT hr; ITStream * pIMSPStream = NULL; // Create a stream object based on the media type. if (dwMediaType == TAPIMEDIATYPE_AUDIO) { if (Direction == TD_RENDER) { CStreamAudioRecv *pAudioRecv = NULL; hr = ::CreateStreamHelper( pAudioRecv, m_pMSPAddress, this, pGraph, TAPIMEDIATYPE_AUDIO, TD_RENDER, &pIMSPStream ); LOG((MSP_TRACE, "create audio receive:%x, hr:%x", pIMSPStream,hr)); } else if (Direction == TD_CAPTURE) { CStreamAudioSend *pAudioSend = NULL; hr = ::CreateStreamHelper( pAudioSend, m_pMSPAddress, this, pGraph, TAPIMEDIATYPE_AUDIO, TD_CAPTURE, &pIMSPStream ); LOG((MSP_TRACE, "create audio send:%x, hr:%x", pIMSPStream,hr)); } } else if (dwMediaType == TAPIMEDIATYPE_VIDEO) { if (Direction == TD_RENDER) { CStreamVideoRecv *pVideoRecv = NULL; hr = ::CreateStreamHelper( pVideoRecv, m_pMSPAddress, this, pGraph, TAPIMEDIATYPE_VIDEO, TD_RENDER, &pIMSPStream ); LOG((MSP_TRACE, "create video Recv:%x, hr:%x", pIMSPStream,hr)); } else if (Direction == TD_CAPTURE) { CStreamVideoSend *pVideoSend = NULL; hr = ::CreateStreamHelper( pVideoSend, m_pMSPAddress, this, pGraph, TAPIMEDIATYPE_VIDEO, TD_CAPTURE, &pIMSPStream ); LOG((MSP_TRACE, "create video send:%x, hr:%x", pIMSPStream,hr)); } } if (FAILED(hr)) { LOG((MSP_ERROR, "create stream failed. %x", hr)); return hr; } *ppStream = pIMSPStream; return S_OK; } HRESULT CH323MSPCall::SendTSPMessage( IN H323MSP_MESSAGE_TYPE Type, IN ITStream * hmChannel, IN HANDLE htChannel, IN MEDIATYPE MediaType, IN DWORD dwReason, IN DWORD dwBitRate ) const /*++ Routine Description: Send the TSP a message from the MSP. Arguments: command - the command to be sent. hmChannel - the handle of the stream. htChannel - the handle of the channel in the TSP. Mediatype - the media type of the channel. dwReason - the reason for the command. Return Value: HRESULT. --*/ { LOG((MSP_TRACE, "SendTSPMessage, type %d, hmChannel %p, htChannel %d", Type, hmChannel, htChannel)); // first allocate the memory. DWORD dwSize = sizeof(H323MSP_MESSAGE); MSPEVENTITEM* pEventItem = AllocateEventItem(dwSize); if (pEventItem == NULL) { LOG((MSP_ERROR, "No memory for the TSPMSP data, size: %d", dwSize)); return E_OUTOFMEMORY; } // Fill in the necessary fields for the event structure. pEventItem->MSPEventInfo.dwSize = sizeof(MSP_EVENT_INFO) + sizeof(H323MSP_MESSAGE); pEventItem->MSPEventInfo.Event = ME_TSP_DATA; pEventItem->MSPEventInfo.hCall = m_htCall; pEventItem->MSPEventInfo.MSP_TSP_DATA.dwBufferSize = sizeof(H323MSP_MESSAGE); H323MSP_MESSAGE *pData = (H323MSP_MESSAGE *) pEventItem->MSPEventInfo.MSP_TSP_DATA.pBuffer; // Fill in the data for the TSP. pData->Type = Type; switch (Type) { case H323MSP_OPEN_CHANNEL_REQUEST: pData->OpenChannelRequest.hmChannel = hmChannel; pData->OpenChannelRequest.Settings.MediaType = MediaType; break; case H323MSP_ACCEPT_CHANNEL_RESPONSE: pData->AcceptChannelResponse.hmChannel = hmChannel; pData->AcceptChannelResponse.htChannel = htChannel; break; case H323MSP_CLOSE_CHANNEL_COMMAND: pData->CloseChannelCommand.hChannel = htChannel; pData->CloseChannelCommand.dwReason = dwReason; break; case H323MSP_QOS_Evnet: pData->QOSEvent.dwEvent = dwReason; pData->QOSEvent.htChannel = htChannel; break; case H323MSP_VIDEO_FAST_UPDATE_PICTURE_COMMAND: pData->VideoFastUpdatePictureCommand.hChannel = htChannel; break; case H323MSP_FLOW_CONTROL_COMMAND: pData->FlowControlCommand.hChannel = htChannel; pData->FlowControlCommand.dwBitRate = dwBitRate; break; } HRESULT hr = m_pMSPAddress->PostEvent(pEventItem); if (FAILED(hr)) { LOG((MSP_ERROR, "Post event failed %x", hr)); FreeEventItem(pEventItem); return hr; } return S_OK; } void CH323MSPCall::ProcessNewCallIndication(void) /*++ Routine Description: This function handles the new call indication pdu. It walk through the list of streams and send open channel requests to the TSP for each outgoing stream. Arguments: none. Return Value: none. --*/ { LOG((MSP_TRACE, "NewCallIndication entered.")); CLock lock(m_lock); if (m_fCallConnected) { LOG((MSP_ERROR, "New Call indication Twice!!!")); return; } for (int i = 0; i < m_Streams.GetSize(); i ++) { CH323MSPStream* pH323MSPStream = (CH323MSPStream*)m_Streams[i]; // Notify the stream that the call is connected. pH323MSPStream->CallConnect(); if ((pH323MSPStream->Direction() == TD_CAPTURE) && (pH323MSPStream->IsTerminalSelected())) { // Send an open Channel request for outgoing channels. SendTSPMessage( H323MSP_OPEN_CHANNEL_REQUEST, m_Streams[i], NULL, (pH323MSPStream->MediaType() == TAPIMEDIATYPE_AUDIO) ? MEDIA_AUDIO : MEDIA_VIDEO ); } } m_fCallConnected = TRUE; return; } void CH323MSPCall::ProcessOpenChannelResponse( IN H323MSG_OPEN_CHANNEL_RESPONSE * pOpenChannelResponse ) /*++ Routine Description: This function handles the open channel response pdu. It finds the stream based on the handle and configures the stream. Arguments: pOpenChannelResponse - the PDU that has the detailed info. Return Value: none. --*/ { LOG((MSP_TRACE, "OpenChannelResponse entered, hmChannel %x, htChannel %x, media %d", pOpenChannelResponse->hmChannel, pOpenChannelResponse->htChannel, pOpenChannelResponse->Settings.MediaType )); ITStream *pITStream = (ITStream *)(pOpenChannelResponse->hmChannel); m_lock.Lock(); if (m_Streams.Find(pITStream) < 0) { LOG((MSP_ERROR, "Stream %x does not exit", pITStream)); m_lock.Unlock(); return; } // it is a valid stream, addref it so that it won't go away. pITStream->AddRef(); // release the lock on the call. m_lock.Unlock(); HRESULT hr = ((CH323MSPStream *)pITStream)->Configure( pOpenChannelResponse->htChannel, pOpenChannelResponse->Settings ); if (FAILED(hr)) { SendTSPMessage( H323MSP_CLOSE_CHANNEL_COMMAND, NULL, pOpenChannelResponse->htChannel ); } // release the refcount we just added. pITStream->Release(); return; } HRESULT CH323MSPCall::SendTAPIStreamEvent( IN ITStream * pITStream, IN MSP_CALL_EVENT Type, IN MSP_CALL_EVENT_CAUSE Cause ) { MSPEVENTITEM* pEventItem = AllocateEventItem(); if (pEventItem == NULL) { LOG((MSP_ERROR, "No memory for the TSPMSP data, size: %d", sizeof(MSPEVENTITEM))); 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; pEventItem->MSPEventInfo.MSP_CALL_EVENT_INFO.Type = Type; pEventItem->MSPEventInfo.MSP_CALL_EVENT_INFO.Cause = Cause; pEventItem->MSPEventInfo.MSP_CALL_EVENT_INFO.pStream = pITStream; // Addref to prevent it from going away. pITStream->AddRef(); pEventItem->MSPEventInfo.MSP_CALL_EVENT_INFO.pTerminal = NULL; pEventItem->MSPEventInfo.MSP_CALL_EVENT_INFO.hrError= 0; // send the event to tapi. HRESULT hr = HandleStreamEvent(pEventItem); if (FAILED(hr)) { LOG((MSP_ERROR, "Post event failed %x", hr)); FreeEventItem(pEventItem); return hr; } return S_OK; } void CH323MSPCall::ProcessAcceptChannelRequest( IN H323MSG_ACCEPT_CHANNEL_REQUEST * pAcceptChannelRequest ) /*++ Routine Description: This function handles the accept channel request pdu. It finds a free stream that can be used for this channel. If no one can be found, it will create a new stream and notify the app. The channel is always accepted automatically. If the app choose to remove the stream, the channel will be closed. Arguments: pAcceptChannelRequest - the PDU that has the detailed info. Return Value: none. --*/ { LOG((MSP_TRACE, "AcceptChannelRequest entered, htChannel %x, media %d", pAcceptChannelRequest->htChannel, pAcceptChannelRequest->Settings.MediaType )); DWORD dwMediaType; if (pAcceptChannelRequest->Settings.MediaType == MEDIA_AUDIO) { dwMediaType = TAPIMEDIATYPE_AUDIO; } else { dwMediaType = TAPIMEDIATYPE_VIDEO; } // if the media type of the channel is not one of the media types of the // call, reject the channel. if (!(dwMediaType & m_dwMediaType)) { LOG((MSP_WARN, "Unwanted media type %x, call media type %x", dwMediaType, m_dwMediaType )); SendTSPMessage( H323MSP_CLOSE_CHANNEL_COMMAND, NULL, pAcceptChannelRequest->htChannel ); return; } CH323MSPStream* pStream = NULL; BOOL fFoundUnused = FALSE; BOOL fExistButInUse = FALSE; CLock lock(m_lock); // find an unused incoming stream of that media type. for (int i = 0; i < m_Streams.GetSize(); i++) { pStream = (CH323MSPStream*)m_Streams[i]; if ((pStream->Direction() == TD_RENDER) && (pStream->MediaType() == dwMediaType)) { if (pStream->IsConfigured()) { fExistButInUse = TRUE; } else { fFoundUnused = TRUE; break; } } } HRESULT hr; if (!fFoundUnused) { if (!fExistButInUse) { // This means the app has deleted the streams of this type and // direction. We should reject this channel. SendTSPMessage( H323MSP_CLOSE_CHANNEL_COMMAND, NULL, pAcceptChannelRequest->htChannel ); return; } // // This channel might be wanted by the APP, create a stream for it. // ITStream * pITStream; // create a stream object. hr = InternalCreateStream(dwMediaType, TD_RENDER, &pITStream); if (FAILED(hr)) { LOG((MSP_ERROR, "create stream failed:%x", hr)); SendTSPMessage( H323MSP_CLOSE_CHANNEL_COMMAND, NULL, pAcceptChannelRequest->htChannel ); return; } // notify the app about the new stream. hr = SendTAPIStreamEvent(pITStream, CALL_NEW_STREAM, CALL_CAUSE_REMOTE_REQUEST); if (FAILED(hr)) { LOG((MSP_ERROR, "Send new stream event failed:%x", hr)); RemoveStream(pITStream); SendTSPMessage( H323MSP_CLOSE_CHANNEL_COMMAND, NULL, pAcceptChannelRequest->htChannel ); return; } // The stream is already in our array, we don't need this pointer. pITStream->Release(); pStream = (CH323MSPStream*)pITStream; } if (SUCCEEDED(pStream->Configure( pAcceptChannelRequest->htChannel, pAcceptChannelRequest->Settings ))) { SendTSPMessage( H323MSP_ACCEPT_CHANNEL_RESPONSE, (ITStream*)pStream, pAcceptChannelRequest->htChannel ); } return; } void CH323MSPCall::ProcessCloseChannelCommand( IN H323MSG_CLOSE_CHANNEL_COMMAND * pCloseChannelCommand ) /*++ Routine Description: This function handles the close channel pdu. It finds the stream based on the handle and remove the stream and also notify the app. Arguments: pCloseChannelCommand - the PDU that has the detailed info. Return Value: none. --*/ { LOG((MSP_TRACE, "CloseChannelCommand entered, hChannel %x, reason: %x", pCloseChannelCommand->hChannel, pCloseChannelCommand->dwReason )); ITStream *pITStream = (ITStream *)(pCloseChannelCommand->hChannel); m_lock.Lock(); if (m_Streams.Find(pITStream) < 0) { LOG((MSP_ERROR, "Stream %x does not exit", pITStream)); m_lock.Unlock(); return; } // it is a valid stream, addref it so that it won't go away. pITStream->AddRef(); // release the lock on the call. m_lock.Unlock(); ((CH323MSPStream *)pITStream)->ShutDown(); // notify the app about the stream being closed. SendTAPIStreamEvent(pITStream, CALL_STREAM_NOT_USED, CALL_CAUSE_REMOTE_REQUEST); // release the refcount we just added. pITStream->Release(); return; } void CH323MSPCall::ProcessIFrameRequest( IN H323MSG_VIDEO_FAST_UPDATE_PICTURE_COMMAND * pIFrameRequest ) /*++ Routine Description: Arguments: Return Value: none. --*/ { LOG((MSP_TRACE, "ProcessIFrameRequest entered, hChannel %x", pIFrameRequest->hChannel )); ITStream *pITStream = (ITStream *)(pIFrameRequest->hChannel); m_lock.Lock(); if (m_Streams.Find(pITStream) < 0) { LOG((MSP_ERROR, "Stream %x does not exit", pITStream)); m_lock.Unlock(); return; } // it is a valid stream, addref it so that it won't go away. pITStream->AddRef(); // release the lock on the call. m_lock.Unlock(); ((CH323MSPStream *)pITStream)->SendIFrame(); // release the refcount we just added. pITStream->Release(); return; } void CH323MSPCall::ProcessFlowControlCommand( IN H323MSG_FLOW_CONTROL_COMMAND * pFlowControlCommand ) /*++ Routine Description: Arguments: Return Value: none. --*/ { LOG((MSP_TRACE, "FlowControlCommand entered, hChannel %x, dataRate: %d", pFlowControlCommand->hChannel, pFlowControlCommand->dwBitRate )); ITStream *pITStream = (ITStream *)(pFlowControlCommand->hChannel); m_lock.Lock(); if (m_Streams.Find(pITStream) < 0) { LOG((MSP_ERROR, "Stream %x does not exit", pITStream)); m_lock.Unlock(); return; } // it is a valid stream, addref it so that it won't go away. pITStream->AddRef(); // release the lock on the call. m_lock.Unlock(); ((CH323MSPStream *)pITStream)->ChangeMaxBitRate( pFlowControlCommand->dwBitRate ); // release the refcount we just added. pITStream->Release(); return; } DWORD CH323MSPCall::ProcessWorkerCallBack( IN PBYTE pBuffer, IN DWORD dwSize ) /*++ Routine Description: This function handles the work item given by the TSP. Arguments: pBuffer - a buffer that contains a TSP_MSP command block. dwSize - the size of the buffer. Return Value: NOERROR. --*/ { LOG((MSP_TRACE, "ProcessWorkerCallBAck")); _ASSERTE(!IsBadReadPtr(pBuffer, dwSize)); H323TSP_MESSAGE * pData = (H323TSP_MESSAGE *)pBuffer; switch (pData->Type) { // // H323TSP_NEW_CALL_INDICATION - sent only from the TSP // to the MSP in order to initiate communication once // a call has been created. // case H323TSP_NEW_CALL_INDICATION: ProcessNewCallIndication(); break; // // H323TSP_OPEN_CHANNEL_RESPONSE - sent only from the TSP // to the MSP in response to H323MSP_OPEN_CHANNEL_REQUEST. // case H323TSP_OPEN_CHANNEL_RESPONSE: ProcessOpenChannelResponse(&(pData->OpenChannelResponse)); break; // // H323TSP_ACCEPT_CHANNEL_REQUEST - sent only from the TSP // to the MSP in order to request the acceptance of an // incoming logical channel. // case H323TSP_ACCEPT_CHANNEL_REQUEST: ProcessAcceptChannelRequest(&(pData->AcceptChannelRequest)); break; // // H323TSP_CLOSE_CHANNEL_COMMAND - sent only from the TSP // to the MSP in order to demand the immediate closure of // an incoming or outgoing logical channel. // case H323TSP_CLOSE_CHANNEL_COMMAND: ProcessCloseChannelCommand(&(pData->CloseChannelCommand)); break; // // H323TSP_CLOSE_CALL_CAMMAND - sent only from the TSP // to the MSP in order to stop all the streaming for a call. // case H323TSP_CLOSE_CALL_COMMAND: InternalShutDown(); break; // I Frame Request. // case H323TSP_VIDEO_FAST_UPDATE_PICTURE_COMMAND: ProcessIFrameRequest(&(pData->VideoFastUpdatePictureCommand)); break; // Flow control command Request. // case H323TSP_FLOW_CONTROL_COMMAND: ProcessFlowControlCommand(&(pData->FlowControlCommand)); break; } return NOERROR; } DWORD WINAPI CH323MSPCall::WorkerCallbackDispatcher(VOID *pContext) /*++ Routine Description: Because configure the streams uses a lot of COM stuff, we can't rely on the RPC thread the calls into the MSP to receive the TSP data. So, we let our own working thread do the work. This method is the callback function for the queued work item. It just gets the call object from the context structure and calls a method on the call object to handle the work item. Arguments: pContext - A pointer to a CALLWORKITEM structure. Return Value: HRESULT. --*/ { CALLWORKITEM *pItem = (CALLWORKITEM *)pContext; pItem->pCall->ProcessWorkerCallBack(pItem->Buffer, pItem->dwLen); pItem->pCall->MSPCallRelease(); free(pItem); return NOERROR; } HRESULT CH323MSPCall::ReceiveTSPCallData( IN PBYTE pBuffer, IN DWORD dwSize ) /*++ Routine Description: This function handles the work item given by the TSP. Arguments: pBuffer - a buffer that contains a TSP_MSP command block. dwSize - the size of the buffer. Return Value: E_POINTER. S_OK. --*/ { LOG((MSP_TRACE, "ReceiveTSPCallData, pBuffer %x, dwSize %d", pBuffer, dwSize)); // make sure the data is valid. if (IsBadReadPtr(pBuffer, dwSize)) { LOG((MSP_ERROR, "the TSP data is invalid.")); return E_POINTER; } // allocate a work item structure for our worker thread. CALLWORKITEM *pItem = (CALLWORKITEM *)malloc(sizeof(CALLWORKITEM) + dwSize); if (pItem == NULL) { LOG((MSP_ERROR, "out of memory for work item.")); return E_OUTOFMEMORY; } this->MSPCallAddRef(); pItem->pCall = this; pItem->dwLen = dwSize; CopyMemory(pItem->Buffer, pBuffer, dwSize); // post a work item to our worker thread. HRESULT hr = g_Thread.QueueWorkItem( WorkerCallbackDispatcher, // the callback pItem, // the context. FALSE // sync (FALSE means asyn) ); if (FAILED(hr)) { this->MSPCallRelease(); free(pItem); LOG((MSP_ERROR, "queue work item failed.")); return E_UNEXPECTED; } return S_OK; }