windows-nt/Source/XPSP1/NT/enduser/netmeeting/av/h323/icomchan.cpp
2020-09-26 16:20:57 +08:00

1375 lines
35 KiB
C++

#include "precomp.h"
#ifndef OLDSTUFF
extern IRTP *g_pIRTP;
#endif
STDMETHODIMP ImpICommChan::StandbyInit(LPGUID lpMID, LPIH323PubCap pCapObject,
IMediaChannel* pMediaStreamSend)
{
if((!lpMID) || (!pCapObject))
return CHAN_E_INVALID_PARAM;
m_MediaID = *lpMID;
bIsSendDirection = TRUE;
m_pMediaStream = pMediaStreamSend;
m_pMediaStream->AddRef();
// keeps a cap object ref
pCapObject->AddRef();
m_pCapObject = pCapObject;
return hrSuccess;
}
STDMETHODIMP ImpICommChan::QueryInterface( REFIID iid, void ** ppvObject)
{
// this breaks the rules for the official COM QueryInterface because
// the interfaces that are queried for are not necessarily real COM
// interfaces. The reflexive property of QueryInterface would be broken in
// that case.
HRESULT hr = E_NOINTERFACE;
if(!ppvObject)
return hr;
*ppvObject = 0;
if(iid == IID_IUnknown)
{
*ppvObject = this;
hr = hrSuccess;
AddRef();
}
else if((iid == IID_ICommChannel))
{
*ppvObject = (ICommChannel *)this;
hr = hrSuccess;
AddRef();
}
else if((iid == IID_ICtrlCommChannel))
{
*ppvObject = (ICtrlCommChan *)this;
hr = hrSuccess;
AddRef();
}
else if((iid == IID_IStreamSignal))
{
*ppvObject = (IStreamSignal *)this;
hr = hrSuccess;
AddRef();
}
else if((iid == IID_IAppAudioCap ) && m_pCapObject)
{
hr = m_pCapObject->QueryInterface(iid, ppvObject);
}
else if((iid == IID_IAppVidCap ) && m_pCapObject)
{
hr = m_pCapObject->QueryInterface(iid, ppvObject);
}
else if((iid == IID_IDualPubCap) && m_pCapObject)
{
hr = m_pCapObject->QueryInterface(iid, ppvObject);
}
else if(iid == IID_IVideoRender)
{
hr=hrSuccess;
if(!m_pMediaStream && m_pH323ConfAdvise)
{
hr = m_pH323ConfAdvise->GetMediaChannel(&m_MediaID,
bIsSendDirection, &m_pMediaStream);
}
if(HR_SUCCEEDED(hr))
{
hr = m_pMediaStream->QueryInterface(iid, ppvObject);
}
}
else if(iid == IID_IVideoChannel)
{
hr=hrSuccess;
if(!m_pMediaStream && m_pH323ConfAdvise)
{
hr = m_pH323ConfAdvise->GetMediaChannel(&m_MediaID,
bIsSendDirection, &m_pMediaStream);
}
if(HR_SUCCEEDED(hr))
{
hr = m_pMediaStream->QueryInterface(iid, ppvObject);
}
}
return (hr);
}
ULONG ImpICommChan::AddRef()
{
m_uRef++;
DEBUGMSG(ZONE_REFCOUNT,("ImpICommChan::AddRef:(0x%08lX)->AddRef() m_uRef = 0x%08lX\r\n",this, m_uRef ));
return m_uRef;
}
ULONG ImpICommChan::Release()
{
m_uRef--;
if(m_uRef == 0)
{
DEBUGMSG(ZONE_REFCOUNT,("ImpICommChan::Release:(0x%08lX)->Releasing\r\n", this));
delete this;
return 0;
}
else
{
DEBUGMSG(ZONE_REFCOUNT,("ImpICommChan::Release:(0x%08lX)->Release() m_uRef = 0x%08lX\r\n",this, m_uRef ));
return m_uRef;
}
}
HRESULT ImpICommChan::GetMediaType(LPGUID pGuid)
{
if(!pGuid)
return CHAN_E_INVALID_PARAM;
*pGuid = m_MediaID;
return hrSuccess;
}
HRESULT ImpICommChan::IsChannelOpen(BOOL *pbOpen)
{
if(!pbOpen)
return CHAN_E_INVALID_PARAM;
*pbOpen = (IsComchOpen()) ? TRUE:FALSE;
return hrSuccess;
}
STDMETHODIMP ImpICommChan::GetProperty(DWORD prop, PVOID pBuf, LPUINT pcbBuf)
{
#define CHECKSIZE(type) if(*pcbBuf != sizeof(type)) return CHAN_E_INVALID_PARAM;
#define OUTPROP(type) *(type *)pBuf
if(!pBuf || !pcbBuf)
return CHAN_E_INVALID_PARAM;
switch (prop)
{
case PROP_TS_TRADEOFF:
CHECKSIZE(DWORD);
OUTPROP(DWORD) = m_TemporalSpatialTradeoff;
break;
case PROP_REMOTE_TS_CAPABLE:
CHECKSIZE(BOOL);
OUTPROP(BOOL) = m_bPublicizeTSTradeoff;
break;
case PROP_CHANNEL_ENABLED:
CHECKSIZE(BOOL);
OUTPROP(BOOL) = (m_dwFlags && COMCH_ENABLED )? TRUE:FALSE;
break;
case PROP_LOCAL_FORMAT_ID:
CHECKSIZE(MEDIA_FORMAT_ID);
OUTPROP(MEDIA_FORMAT_ID) = m_LocalFmt;
break;
case PROP_REMOTE_FORMAT_ID:
CHECKSIZE(MEDIA_FORMAT_ID);
OUTPROP(MEDIA_FORMAT_ID) = m_RemoteFmt;
break;
case PROP_REMOTE_PAUSED:
CHECKSIZE(BOOL);
OUTPROP(BOOL) = (IsStreamingRemote())? FALSE:TRUE;
break;
case PROP_LOCAL_PAUSE_RECV:
case PROP_LOCAL_PAUSE_SEND:
CHECKSIZE(BOOL);
OUTPROP(BOOL) = IsPausedLocal();
break;
case PROP_VIDEO_PREVIEW_ON:
CHECKSIZE(BOOL);
OUTPROP(BOOL) = IsStreamingStandby();
break;
case PROP_VIDEO_PREVIEW_STANDBY:
CHECKSIZE(BOOL);
OUTPROP(BOOL) = IsConfigStandby();
break;
default:
if(m_pMediaStream)
{
// we don't recognize this property, pass to media control
return m_pMediaStream->GetProperty(prop, pBuf, (LPUINT)pcbBuf);
}
else
return CHAN_E_INVALID_PARAM;
break;
}
return hrSuccess;
}
// Some properties are not writeable by client code. CtrlChanSetProperty allows setting of
// those properties. This method is *not* exposed in ICommChannel
STDMETHODIMP ImpICommChan::CtrlChanSetProperty(DWORD prop, PVOID pBuf, DWORD cbBuf)
{
FX_ENTRY("ImpICommChan::CtrlChanSetProperty");
BOOL bTemp;
HRESULT hr = hrSuccess;
if(!pBuf || !pBuf || !cbBuf)
return CHAN_E_INVALID_PARAM;
#define CHECKSIZEIN(type) if(cbBuf != sizeof(type)) return CHAN_E_INVALID_PARAM;
#define INPROP(type) *(type *)pBuf
switch (prop)
{
case PROP_TS_TRADEOFF_IND: // remote sender changed T/S tradeoff of what it is
if(bIsSendDirection) // sending (valid for receive channels only)
return CHAN_E_INVALID_PARAM;
m_TemporalSpatialTradeoff = INPROP(DWORD);
if(m_pH323ConfAdvise && m_pCtlChan)
{
DEBUGMSG(ZONE_COMMCHAN,("%s:issuing notification 0x%08lX\r\n",_fx_, CHANNEL_VIDEO_TS_TRADEOFF));
m_pH323ConfAdvise->ChannelEvent(this, m_pCtlChan->GetIConnIF(), CHANNEL_VIDEO_TS_TRADEOFF);
}
break;
case PROP_REMOTE_FORMAT_ID:
CHECKSIZEIN(DWORD);
m_RemoteFmt = INPROP(DWORD);
break;
case PROP_REMOTE_TS_CAPABLE: // only valid for receive channels
if(bIsSendDirection)
return CHAN_E_INVALID_PARAM;
else
{
CHECKSIZEIN(BOOL);
m_bPublicizeTSTradeoff = INPROP(BOOL);
DEBUGMSG (ZONE_COMMCHAN,("%s:remote TS tradeoff cap %d\r\n", _fx_, m_bPublicizeTSTradeoff));
}
break;
default:
return SetProperty(prop, pBuf, cbBuf);
break;
}
return hr;
}
STDMETHODIMP ImpICommChan::Preview(MEDIA_FORMAT_ID idLocalFormat, IMediaChannel * pMediaStream)
{
HRESULT hr = hrSuccess;
FX_ENTRY("ImpICommChan::Preview");
SHOW_OBJ_ETIME("ImpICommChan::Preview");
LPVOID lpvFormatDetails;
UINT uFormatSize;
if(!bIsSendDirection)
{
hr = CHAN_E_INVALID_PARAM;
goto EXIT;
}
if(NULL == pMediaStream)
{
// preview off
if(IsStreamingStandby())
{
DEBUGMSG(ZONE_COMMCHAN,("%s:(%s)transition to preview OFF\r\n",_fx_,
(bIsSendDirection)?"send":"recv"));
//turn preview off.
// if network side is paused or closed, stop all streaming
if(!IsComchOpen() || !IsStreamingNet())
{
DEBUGMSG(ZONE_COMMCHAN,("%s:(%s)stopping local stream\r\n",_fx_,
(bIsSendDirection)?"send":"recv"));
// Stop the stream, but DO NOT UNCONFIGURE becase we want to
// be able to start later
hr = m_pMediaStream->Stop();
if(!HR_SUCCEEDED(hr))
{
DEBUGMSG(ZONE_COMMCHAN,("%s:(%s)Stop() returned 0x%08lx\r\n",_fx_,
(bIsSendDirection)?"send":"recv", hr));
}
SHOW_OBJ_ETIME("ImpICommChan::Preview - stopped");
LocalStreamFlagOff();
}
// else just need to turn off flag
StandbyFlagOff();
}
else
DEBUGMSG(ZONE_COMMCHAN,("%s:(%s) no change (%s)\r\n",_fx_,
(bIsSendDirection)?"send":"recv", "OFF"));
}
else
{
// preview on
ASSERT(m_pCapObject);
if(idLocalFormat == INVALID_MEDIA_FORMAT)
{
hr = CHAN_E_INVALID_PARAM;
goto EXIT;
}
ASSERT(!(m_pMediaStream && (m_pMediaStream != pMediaStream)));
if (m_pMediaStream == NULL)
{
m_pMediaStream = pMediaStream;
m_pMediaStream->AddRef();
}
if(!IsStreamingStandby())
{
DEBUGMSG(ZONE_COMMCHAN,("%s:(%s)transition to preview ON\r\n",_fx_,
(bIsSendDirection)?"send":"recv"));
// turn preview on.
if(!IsStreamingLocal())
{
ASSERT(!IsStreamingNet());
if(IsComchOpen())
{
// if the channel is open, local streaming should only be off
// if the network side of the channel is paused.
//ASSERT(!IsStreamingNet());
}
else
{
// ensure that the stream does not come up with network send enabled
// (!!!!! override default stream behavior !!!!!)
BOOL bPause = TRUE;
hr = m_pMediaStream->SetProperty(
(bIsSendDirection)? PROP_PAUSE_SEND:PROP_PAUSE_RECV,
&bPause, sizeof(bPause));
// get format info for the specified format
m_pCapObject->GetEncodeFormatDetails(idLocalFormat, &lpvFormatDetails, &uFormatSize);
// fire up the local stream
// this is now a two step process
hr = m_pMediaStream->Configure((BYTE*)lpvFormatDetails, uFormatSize,
NULL, 0, (IUnknown*)(ImpICommChan *)this);
if(!HR_SUCCEEDED(hr))
{
ERRORMESSAGE(("%s: m_pMediaStream->Configure returned 0x%08lX\r\n", _fx_, hr));
goto EXIT;
}
m_pMediaStream->SetNetworkInterface(NULL);
if(!HR_SUCCEEDED(hr))
{
ERRORMESSAGE(("%s: m_pMediaStream->SetNetworkInterface returned 0x%08lX\r\n", _fx_, hr));
goto EXIT;
}
SHOW_OBJ_ETIME("ImpICommChan::Preview - config'd for preview");
}
// Start the stream
hr = m_pMediaStream->Start();
if(!HR_SUCCEEDED(hr))
{
ERRORMESSAGE(("%s: m_pMediaStream->Start returned 0x%08lX\r\n", _fx_, hr));
goto EXIT;
}
SHOW_OBJ_ETIME("ImpICommChan::Preview - started preview");
LocalStreamFlagOn();
}
// else // just need to set flag to make preview sticky
StandbyFlagOn();
}
else
DEBUGMSG(ZONE_COMMCHAN,("%s:(%s) no change (%s)\r\n",_fx_,
(bIsSendDirection)?"send":"recv", "ON"));
}
EXIT:
return hr;
}
STDMETHODIMP ImpICommChan::PauseNetworkStream(BOOL fPause)
{
if(fPause)
LocalPauseFlagOn();
else
LocalPauseFlagOff();
return PauseNet(fPause, FALSE);
}
BOOL ImpICommChan::IsNetworkStreamPaused(VOID)
{
return IsPausedLocal();
}
BOOL ImpICommChan::IsRemotePaused(VOID)
{
return (IsStreamingRemote())? FALSE:TRUE;
}
STDMETHODIMP ImpICommChan::PauseNet(BOOL bPause, BOOL bRemoteInitiated)
{
HRESULT hr = hrSuccess;
FX_ENTRY("ImpICommChan::PauseNet");
// issue notification
if(bRemoteInitiated)
{
// keep track of remote state
if(bPause)
RemoteStreamFlagOff();
else
RemoteStreamFlagOn();
if(!IsNotificationSupressed())
{
if(m_pH323ConfAdvise && m_pCtlChan)
{
DEBUGMSG(ZONE_COMMCHAN,("%s:issuing %s notification \r\n",_fx_,
(bPause)?"pause":"un-pause"));
m_pH323ConfAdvise->ChannelEvent(this, m_pCtlChan->GetIConnIF(),
(bPause)? CHANNEL_REMOTE_PAUSE_ON: CHANNEL_REMOTE_PAUSE_OFF);
}
else
DEBUGMSG(ZONE_COMMCHAN,("%s:not issuing %s notification: m_pH323ConfAdvise: 0x%08lX, m_pCtlChan:0x%08lX \r\n"
,_fx_, (bPause)?"pause":"un-pause", m_pH323ConfAdvise,m_pCtlChan));
}
}
if(bPause && IsStreamingNet())
{
ASSERT(IsComchOpen());
// deactivate the channel
DEBUGMSG(ZONE_COMMCHAN,("%s:(%s)transition to pause\r\n",_fx_,
(bIsSendDirection)?"send":"recv" ));
if(!bRemoteInitiated)
{
// locally initiated, so signal remote
if(bIsSendDirection)
{
DEBUGMSG (ZONE_COMMCHAN,("%s:signaling pause of %s channel\r\n",
_fx_, (bIsSendDirection)?"send":"recv" ));
// signal remote
MiscellaneousIndication mi;
mi.type.choice = logicalChannelInactive_chosen;
hr = m_pCtlChan->MiscChannelIndication(this, &mi);
if(!HR_SUCCEEDED(hr))
{
DEBUGMSG (ZONE_COMMCHAN,("%s:(%s) CC_Mute returned 0x%08lx\r\n",
_fx_, (bIsSendDirection)?"send":"recv", hr));
hr = hrSuccess; // don't care about signaling error, act normal
}
}
}
//
hr = m_pMediaStream->SetProperty(
(bIsSendDirection)? PROP_PAUSE_SEND:PROP_PAUSE_RECV,
&bPause, sizeof(bPause));
NetworkStreamFlagOff();
// LOOKLOOK - can't stop receive streams because they can't be restarted
// check this with GeorgeJ
// if(!IsStreamingStandby()) // need local stream for anything?
if(!IsStreamingStandby() && bIsSendDirection) // need local stream for anything?
{
DEBUGMSG(ZONE_COMMCHAN,("%s:(%s)stopping local stream\r\n",_fx_,
(bIsSendDirection)?"send":"recv"));
// can shut off local streaming now
hr = m_pMediaStream->Stop();
LocalStreamFlagOff();
}
}
else if(!bPause && !IsStreamingNet())
{
DEBUGMSG(ZONE_COMMCHAN,("%s:(%s)transition to unpause\r\n",_fx_,
(bIsSendDirection)?"send":"recv"));
if(IsComchOpen())
{
// activate the channel
if(!bRemoteInitiated)
{
// locally initiated, so signal remote
if(bIsSendDirection)
{
DEBUGMSG (ZONE_COMMCHAN,("%s:signaling UNpause of %s channel\r\n",
_fx_, (bIsSendDirection)?"send":"recv" ));
// signal remote
MiscellaneousIndication mi;
mi.type.choice = logicalChannelActive_chosen;
hr = m_pCtlChan->MiscChannelIndication(this, &mi);
if(!HR_SUCCEEDED(hr))
{
DEBUGMSG (ZONE_COMMCHAN,("%s:(%s) CC_UnMute returned 0x%08lx\r\n",
_fx_, (bIsSendDirection)?"send":"recv", hr));
hr = hrSuccess; // don't care about signaling error, act normal
}
}
}
else
{
// remotely initiated OR special case first time channel is unpaused
// after opening
AllowNotifications(); // stop supressing notifications
}
if(!IsPausedLocal())
{
// MUST ensure unpaused state before starting stream ????
hr = m_pMediaStream->SetProperty(
(bIsSendDirection)? PROP_PAUSE_SEND:PROP_PAUSE_RECV,
&bPause, sizeof(bPause));
// check local streaming state, start it if needed
if(!IsStreamingLocal())
{
DEBUGMSG(ZONE_COMMCHAN,("%s:(%s)starting local stream\r\n",_fx_,
(bIsSendDirection)?"send":"recv"));
// need to startup stream
hr = m_pMediaStream->Start();
LocalStreamFlagOn();
}
else
{
if(bIsSendDirection)
{
DEBUGMSG(ZONE_COMMCHAN,("%s:(%s)already streaming locally\r\n",_fx_,
(bIsSendDirection)?"send":"recv" ));
DEBUGMSG(ZONE_COMMCHAN,("%s:(%s)RESTARTING local stream\r\n",_fx_,
(bIsSendDirection)?"send":"recv"));
// This is temporary until it is possible to start the
// network side of a running stream
hr = m_pMediaStream->Stop();
hr = m_pMediaStream->Start();
}
}
NetworkStreamFlagOn();
//
// if this is a receive video channel, make the sender send an I-frame now
//
if(!bIsSendDirection && (GetTickCount() > (m_dwLastUpdateTick + MIN_IFRAME_REQ_TICKS)))
{
if((MEDIA_TYPE_H323VIDEO == m_MediaID))
{
MiscellaneousCommand mc;
// mc.logicalChannelNumber = ?; ** call control fills this in **
mc.type.choice = videoFastUpdatePicture_chosen;
// do the control channel signaling for THIS channel
hr = m_pCtlChan->MiscChannelCommand(this, &mc);
}
m_dwLastUpdateTick = GetTickCount();
}
}
}
else
ERRORMESSAGE(("%s:(%s) Not open: bPause=%d, streaming=%d\r\n", _fx_,
(bIsSendDirection)?"send":"recv", bPause, IsStreamingNet()));
}
else
{
ERRORMESSAGE(("%s:(%s) bPause=%d, streaming=%d\r\n", _fx_,
(bIsSendDirection)?"send":"recv", bPause, IsStreamingNet()));
}
return hr;
}
STDMETHODIMP ImpICommChan::SetProperty(DWORD prop, PVOID pBuf, UINT cbBuf)
{
FX_ENTRY("ImpICommChan::SetProperty");
BOOL bTemp;
HRESULT hr = hrSuccess;
if(!pBuf || !pBuf || !cbBuf)
return CHAN_E_INVALID_PARAM;
#define CHECKSIZEIN(type) if(cbBuf != sizeof(type)) return CHAN_E_INVALID_PARAM;
#define INPROP(type) *(type *)pBuf
#define SetMediaProperty() \
if(m_pMediaStream) \
{return m_pMediaStream->SetProperty(prop, pBuf, cbBuf); } \
else hr = CHAN_E_INVALID_PARAM;
switch (prop)
{
// (read only) case PROP_REMOTE_FORMAT_ID:
// (read only) case PROP_LOCAL_FORMAT_ID:
// (read only) case PROP_REMOTE_TS_CAPABLE:
case PROP_TS_TRADEOFF:
CHECKSIZEIN(DWORD);
if(bIsSendDirection) // set local T/S tradeoff, then signal remote
{
// scale value - input is 0-31, (lower number = higher quality and lower frame rate)
m_TemporalSpatialTradeoff = INPROP(DWORD);
DEBUGMSG (ZONE_COMMCHAN,("%s:TS tradeoff (tx) %d\r\n", _fx_, m_TemporalSpatialTradeoff));
// change our compression
if (m_pMediaStream)
{
HRESULT hr;
hr = m_pMediaStream->SetProperty(PROP_VIDEO_IMAGE_QUALITY,
&m_TemporalSpatialTradeoff, sizeof (m_TemporalSpatialTradeoff));
}
if(m_bPublicizeTSTradeoff && m_pCtlChan) // check our own capability and if in a call
{
// we said we supported TS tradeoff, so we have to signal our
// new value
MiscellaneousIndication mi;
// mi.logicalChannelNumber = ?; ** call control fills this in **
mi.type.choice = MIn_tp_vdTmprlSptlTrdOff_chosen;
mi.type.u.MIn_tp_vdTmprlSptlTrdOff = LOWORD(m_TemporalSpatialTradeoff);
// do the control channel signaling for THIS channel
hr = m_pCtlChan->MiscChannelIndication(this, &mi);
}
}
else // signal remote to change its T/S tradoff of its send channel
{
m_TemporalSpatialTradeoff = INPROP(DWORD);
DEBUGMSG (ZONE_COMMCHAN,("%s:TS tradeoff (rx) %d\r\n", _fx_, m_TemporalSpatialTradeoff));
if(m_bPublicizeTSTradeoff && m_pCtlChan)// check remote's TS capability
{
MiscellaneousCommand mc;
// mc.logicalChannelNumber = ?; ** call control fills this in **
mc.type.choice = MCd_tp_vdTmprlSptlTrdOff_chosen;
mc.type.u.MCd_tp_vdTmprlSptlTrdOff = LOWORD(m_TemporalSpatialTradeoff);
hr = m_pCtlChan->MiscChannelCommand(this, &mc);
}
else // remote said it does not support TS tradeoff
return CHAN_E_INVALID_PARAM;
}
break;
case PROP_CHANNEL_ENABLED:
CHECKSIZEIN(BOOL);
if(INPROP(BOOL))
{
m_dwFlags |= COMCH_ENABLED;
}
else
{
m_dwFlags &= ~COMCH_ENABLED;
}
break;
//
// Media streaming properties
//
case PROP_LOCAL_PAUSE_RECV:
case PROP_LOCAL_PAUSE_SEND:
CHECKSIZEIN(BOOL);
bTemp = INPROP(BOOL);
if(bTemp)
LocalPauseFlagOn();
else
LocalPauseFlagOff();
hr = PauseNet(bTemp, FALSE);
break;
case PROP_PAUSE_RECV:
case PROP_PAUSE_SEND:
CHECKSIZEIN(BOOL);
hr = PauseNet(INPROP(BOOL), FALSE);
break;
// case PROP_PAUSE_RECV:
// SetMediaProperty();
// break;
case PROP_VIDEO_PREVIEW_ON:
ASSERT(0);
break;
case PROP_VIDEO_PREVIEW_STANDBY:
CHECKSIZEIN(BOOL);
bTemp = INPROP(BOOL);
if(bTemp)
StandbyConfigFlagOn();
else
StandbyConfigFlagOff();
break;
default:
// we don't recognize this property, pass to media control
if(m_pMediaStream)
{
return m_pMediaStream->SetProperty(prop, pBuf, cbBuf);
}
else
hr = CHAN_E_INVALID_PARAM;
break;
}
return hr;
}
HRESULT ImpICommChan::EnableOpen(BOOL bEnable)
{
if(bEnable)
{
m_dwFlags |= COMCH_ENABLED;
}
else
{
m_dwFlags &= ~COMCH_ENABLED;
}
return hrSuccess;
}
HRESULT ImpICommChan::GetLocalParams(LPVOID lpvChannelParams, UINT uBufSize)
{
if(!lpvChannelParams || !pLocalParams || !uBufSize)
return CHAN_E_INVALID_PARAM;
if(uBufSize < uLocalParamSize)
return CHAN_E_INVALID_PARAM;
memcpy(lpvChannelParams, pLocalParams, uLocalParamSize);
return hrSuccess;
}
HRESULT ImpICommChan::ConfigureStream(MEDIA_FORMAT_ID idLocalFormat)
{
FX_ENTRY("ImpICommChan::ConfigureStream");
HRESULT hr;
ASSERT(m_pRTPChan && m_pCapObject);
LPVOID lpvFormatGoo;
UINT uFormatGooSize;
IUnknown *pUnknown=NULL;
// get format info for Configure()
if(bIsSendDirection)
{
m_pCapObject->GetEncodeFormatDetails(idLocalFormat, &lpvFormatGoo, &uFormatGooSize);
}
else
{
m_pCapObject->GetDecodeFormatDetails(idLocalFormat, &lpvFormatGoo, &uFormatGooSize);
}
hr = m_pMediaStream->Configure((BYTE*)lpvFormatGoo, uFormatGooSize,
(BYTE*)pLocalParams, uLocalParamSize,
(IUnknown*)(ImpICommChan *)this);
if(!HR_SUCCEEDED(hr))
{
ERRORMESSAGE(("%s: Configure returned 0x%08lX\r\n", _fx_, hr));
}
// SetNetworkInterface expects an IUnknown pointer
// the IUnknown wil be QI'd for either an IRTPSend or an IRTPRecv
// interface. The IUnknown should be free'd by the caller.
if (m_pRTPChan)
{
m_pRTPChan->QueryInterface(IID_IUnknown, (void**)&pUnknown);
ASSERT(pUnknown);
}
hr = m_pMediaStream->SetNetworkInterface(pUnknown);
if(!HR_SUCCEEDED(hr))
{
ERRORMESSAGE(("%s: SetNetworkInterface returned 0x%08lX\r\n", _fx_, hr));
}
if (pUnknown)
{
pUnknown->Release();
}
return hr;
}
HRESULT ImpICommChan::ConfigureCapability(LPVOID lpvRemoteChannelParams, UINT uRemoteParamSize,
LPVOID lpvLocalParams, UINT uGivenLocalParamSize)
{
HRESULT hr= hrSuccess;
if(!lpvRemoteChannelParams)
return CHAN_E_INVALID_PARAM;
if(pRemoteParams)
{
MemFree(pRemoteParams);
pRemoteParams = NULL;
}
// if uParamSize ==0, it means that the memory that lpvRemoteChannelParams points to
// is being supplied
if(uRemoteParamSize)
{
pRemoteParams = MemAlloc(uRemoteParamSize);
if(pRemoteParams)
{
memcpy(pRemoteParams, lpvRemoteChannelParams, uRemoteParamSize);
}
}
else
pRemoteParams = lpvRemoteChannelParams;
if(lpvLocalParams)
{
// memory for local parameters is always supplied by the caller
if (!uGivenLocalParamSize)
{
hr = CHAN_E_INVALID_PARAM;
goto EXIT;
}
if(pLocalParams)
{
MemFree(pLocalParams);
// not needed pLocalParams= NULL;
}
uLocalParamSize = uGivenLocalParamSize;
pLocalParams = lpvLocalParams;
}
EXIT:
return hr;
}
HRESULT ImpICommChan::OnChannelClose(DWORD dwStatus)
{
HRESULT hr = hrSuccess;
FX_ENTRY("ImpICommChan::OnChannelClose");
BOOL fCloseAction = FALSE;
SHOW_OBJ_ETIME("ImpICommChan::OnChannelClose");
m_dwFlags &= ~COMCH_OPEN_PENDING;
switch(dwStatus)
{
case CHANNEL_CLOSED:
DEBUGMSG(ZONE_COMMCHAN,("%s:closing (%s)\r\n"
,_fx_, (bIsSendDirection)?"send":"recv"));
if(IsComchOpen())
{
fCloseAction = TRUE;
m_dwFlags &= ~COMCH_OPEN;
}
else
{
ERRORMESSAGE(("%s: %d notification when not open (%s)\r\n", _fx_,
dwStatus,(bIsSendDirection)?"send":"recv"));
}
break;
//case CHANNEL_REJECTED:
//case CHANNEL_NO_CAPABILITY:
default:
break;
}
// clear general purpose channel handle
dwhChannel = 0;
// LOOKLOOK **** RIGHT HERE ***
// ** need to notify the UI of the channel event ON_CLOSING so that the last
// frame can be grabbed for rendering (a still picture is better than a black window)
// LOOKLOOK **** RIGHT HERE ***
// Now check preview state
if(IsStreamingStandby() && bIsSendDirection )
{
if (m_pMediaStream != NULL)
{
DEBUGMSG(ZONE_COMMCHAN,("%s:transition back to preview\r\n" ,_fx_));
// need to stop sending and reconfigure for preview
// make sure send is paused
DWORD dwProp = TRUE;
hr = m_pMediaStream->SetProperty (PROP_PAUSE_SEND,&dwProp, sizeof(dwProp));
if(!HR_SUCCEEDED(hr))
{
ERRORMESSAGE(("%s: m_pMediaStream->SetProperty returned 0x%08lX\r\n", _fx_, hr));
// do what now?
}
NetworkStreamFlagOff();
hr = m_pMediaStream->Stop();
LocalStreamFlagOff();
StandbyFlagOff();
ASSERT(hr == S_OK);
}
else
{
NetworkStreamFlagOff();
LocalStreamFlagOff();
}
if(fCloseAction)
{
// Cleanup RTP session. This is a NOP if the opposite direction is still open.
if (m_pRTPChan)
{
m_pRTPChan->Release();
m_pRTPChan = NULL;
}
}
}
else // not previewing
{
//
// Stop the media stream
//
if (m_pMediaStream)
{
hr = m_pMediaStream->Stop(); // probably not necessary
ASSERT(hr == S_OK);
// implement "capture device standby": don't unconfigure if
// the standby flag is set and it is a send stream.
if(!IsConfigStandby() || !bIsSendDirection)
{
if(!bIsSendDirection) // keep send stream reference until *this* object is released
{
m_pMediaStream->Release();
m_pMediaStream = NULL;
}
}
}
SHOW_OBJ_ETIME("ImpICommChan::OnChannelClose - stream stopped");
if(fCloseAction)
{
// Cleanup RTP session. This is a NOP if the opposite direction is still open.
if (m_pRTPChan)
{
m_pRTPChan->Release();
m_pRTPChan = NULL;
}
}
StreamFlagsOff();
}// end if not previewing
if(m_pH323ConfAdvise && m_pCtlChan)
{
DEBUGMSG(ZONE_COMMCHAN,("%s:issuing notification 0x%08lX\r\n",_fx_, dwStatus));
m_pH323ConfAdvise->ChannelEvent(this, m_pCtlChan->GetIConnIF(), dwStatus);
}
return hr;
}
HRESULT ImpICommChan::OnChannelOpening()
{
ASSERT((m_dwFlags & COMCH_OPEN_PENDING) ==0);
m_dwFlags |= COMCH_OPEN_PENDING;
return hrSuccess;
}
HRESULT ImpICommChan::OnChannelOpen(DWORD dwStatus)
{
HRESULT hr;
BOOL bConfigured = FALSE, bNewStream = FALSE; // these bools make error cleanup cleaner
FX_ENTRY("ImpICommChan::OnChannelOpen");
SHOW_OBJ_ETIME("ImpICommChan::OnChannelOpen");
// the open is no longer pending, regardless of success or failure
m_dwFlags &= ~COMCH_OPEN_PENDING;
m_dwLastUpdateTick = 0; // reset tick count of last I-frame request so that one
// will be requested
if(IsComchOpen())
{
ERRORMESSAGE(("%s: %d notification when open (%s)\r\n", _fx_,
dwStatus, (bIsSendDirection)?"send":"recv"));
}
switch(dwStatus)
{
case CHANNEL_OPEN:
m_dwFlags |= (COMCH_OPEN | COMCH_SUPPRESS_NOTIFICATION);
break;
default:
dwStatus = CHANNEL_OPEN_ERROR;
// fall through to notification
case CHANNEL_REJECTED:
case CHANNEL_NO_CAPABILITY:
goto NOTIFICATION;
break;
}
// The channel is open as far as call control is concerned.
// if previewing, the stream already exists. We don't want another, nor do we
// want to tear it down at channel close time or in error cases
if(!m_pMediaStream)
{
ASSERT(!IsStreamingLocal() &&m_pH323ConfAdvise); // can't be streaming without a stream
bNewStream = TRUE;
// Associate the media streaming endpoint with this channel
// see above
hr = m_pH323ConfAdvise->GetMediaChannel(&m_MediaID,
bIsSendDirection, &m_pMediaStream);
if(!HR_SUCCEEDED(hr))
{
ERRORMESSAGE(("%s: m_pH323ConfAdvise->GetMediaChannel returned 0x%08lX\r\n", _fx_, hr));
goto ERROR_NOTIFICATION;
}
}
if(IsStreamingLocal())
{
DEBUGMSG(ZONE_COMMCHAN,("%s:(%s)transition:preview -> send\r\n",_fx_,
(bIsSendDirection)?"send":"recv"));
// need to stop stream while configuring ( ***** check w/ RichP ******)
hr = m_pMediaStream->Stop();
LocalStreamFlagOff();
}
// notify upper layers of channel open now
if(m_pH323ConfAdvise && m_pCtlChan)
{
DEBUGMSG(ZONE_COMMCHAN,("%s:issuing CHANNEL_OPEN notification\r\n",_fx_));
m_pH323ConfAdvise->ChannelEvent(this, m_pCtlChan->GetIConnIF(), dwStatus);
}
dwStatus = CHANNEL_ACTIVE; // new status! notification is posted below
ASSERT(m_pRTPChan);
// get format info for Configure()
hr = ConfigureStream(m_LocalFmt);
if(!HR_SUCCEEDED(hr))
{
ERRORMESSAGE(("%s: Configure returned 0x%08lX\r\n", _fx_, hr));
goto ERROR_NOTIFICATION;
}
SHOW_OBJ_ETIME("ImpICommChan::OnChannelOpen - configured stream");
bConfigured = TRUE;
// turn on flow to the network
// SupressNotification() // pre-initialized above in both CHANNEL_OPEN_xxx cases
PauseNet(FALSE, TRUE); // unpause,
//dwStatus = CHANNEL_ACTIVE;
SHOW_OBJ_ETIME("ImpICommChan::OnChannelOpen - unpaused");
NOTIFICATION:
if(m_pH323ConfAdvise && m_pCtlChan)
{
DEBUGMSG(ZONE_COMMCHAN,("%s:issuing notification 0x%08lX\r\n",_fx_, dwStatus));
m_pH323ConfAdvise->ChannelEvent(this, m_pCtlChan->GetIConnIF(), dwStatus);
}
else
DEBUGMSG(ZONE_COMMCHAN,("%s: *** not issuing notification 0x%08lX m_pH323ConfAdvise: 0x%08lX, m_pCtlChan:0x%08lX \r\n"
,_fx_, dwStatus,m_pH323ConfAdvise,m_pCtlChan));
SHOW_OBJ_ETIME("ImpICommChan::OnChannelOpen - done ");
return hr;
ERROR_NOTIFICATION:
dwStatus = CHANNEL_OPEN_ERROR;
if(m_pMediaStream)
{
if(bNewStream) // was the media stream just created?
{
m_pMediaStream->Release();
m_pMediaStream = NULL;
}
}
if(m_pH323ConfAdvise && m_pCtlChan)
{
DEBUGMSG(ZONE_COMMCHAN,("%s:issuing notification 0x%08lX\r\n",_fx_, dwStatus));
m_pH323ConfAdvise->ChannelEvent(this, m_pCtlChan->GetIConnIF(), dwStatus);
}
else
DEBUGMSG(ZONE_COMMCHAN,("%s: *** not issuing notification 0x%08lX m_pH323ConfAdvise: 0x%08lX, m_pCtlChan:0x%08lX \r\n"
,_fx_, dwStatus,m_pH323ConfAdvise,m_pCtlChan));
// close the channel.
if(m_pCtlChan)
{
// close channel, but hr already contains the relevant return code
m_pCtlChan->CloseChannel(this);
}
return hr;
}
HRESULT ImpICommChan::Open(MEDIA_FORMAT_ID idLocalFormat, IH323Endpoint *pConnection)
{
HRESULT hr;
MEDIA_FORMAT_ID idRemoteFormat;
IConfAdvise * pConfAdvise = NULL;
if((m_dwFlags & COMCH_OPEN_PENDING) || IsComchOpen() || (idLocalFormat == INVALID_MEDIA_FORMAT) || !pConnection)
return CHAN_E_INVALID_PARAM;
if(!m_pCtlChan) // this channel is not part of a call
{
hr = pConnection->QueryInterface(IID_IConfAdvise, (void **)&pConfAdvise);
if(!HR_SUCCEEDED(hr))
goto EXIT;
hr = pConfAdvise->AddCommChannel(this);
if(!HR_SUCCEEDED(hr))
goto EXIT;
ASSERT(m_pCtlChan && m_pCapObject);
}
hr = m_pCapObject->ResolveToLocalFormat(idLocalFormat, &idRemoteFormat);
if(!HR_SUCCEEDED(hr))
goto EXIT;
// start the control channel stuff needed to open the channel
hr = m_pCtlChan->OpenChannel((ICtrlCommChan*)this, m_pCapObject,
idLocalFormat, idRemoteFormat);
EXIT:
if(pConfAdvise)
pConfAdvise->Release();
return hr;
}
HRESULT ImpICommChan::Close()
{
HRESULT hr = CHAN_E_INVALID_PARAM;
if(!IsComchOpen() || !m_pCtlChan)
goto EXIT;
if(!bIsSendDirection)
goto EXIT;
hr = m_pCtlChan->CloseChannel(this);
EXIT:
return hr;
}
HRESULT ImpICommChan::BeginControlSession(IControlChannel *pCtlChan, LPIH323PubCap pCapObject)
{
// this channel is now "in a call".
// LOOKLOOK - it might help to notify (ICommChannel notifications to client)
// that the channel is part of a call now.
ASSERT((m_pCtlChan == NULL) && pCtlChan && pCapObject);
if(m_pCapObject)
{
m_pCapObject->Release();
}
m_pCtlChan = pCtlChan;
m_pCapObject = pCapObject;
m_pCapObject->AddRef();
return hrSuccess;
}
HRESULT ImpICommChan::EndControlSession()
{
// this channel is no longer "in a call".
m_pCtlChan = NULL;
return hrSuccess;
}
BOOL ImpICommChan::SelectPorts(LPIControlChannel pCtlChannel)
{
// create the RTP channel
HRESULT hr;
PSOCKADDR_IN psin=NULL;
pCtlChannel->GetLocalAddress(&psin);
PORT savedPort = psin->sin_port;
if (!m_pRTPChan) {
UINT sessFlags = bIsSendDirection ? SESSIONF_SEND : SESSIONF_RECV;
UINT sessId;
GUID mediaGuid;
GetMediaType(&mediaGuid);
if (mediaGuid == MEDIA_TYPE_H323VIDEO)
{
sessFlags |= SESSIONF_VIDEO;
sessId = 2;
}
else
{
sessId = 1;
sessFlags |= SESSIONF_AUDIO;
}
psin->sin_port = 0; // zero port forces RTP to choose a port
hr = g_pIRTP->OpenSession(sessId, sessFlags,
(BYTE *)psin, sizeof(PSOCKADDR_IN),
&m_pRTPChan);
}
else
hr = m_pRTPChan->SetLocalAddress((BYTE *)psin,sizeof(SOCKADDR_IN));
psin->sin_port = savedPort;
return hr==S_OK;
}
// get the address and port of the base port that was selected by SelectPorts().
// in this typical implementation, that is the address/port of the RTCP channel
PSOCKADDR_IN ImpICommChan::GetLocalAddress()
{
#ifdef OLDSTUFF
return m_pRTPChan ? m_pRTPChan->GetChannelDescription()->pLocalAddr : NULL;
#else
const BYTE *pAddr;
UINT cbAddr;
HRESULT hr;
hr = m_pRTPChan->GetLocalAddress(&pAddr, &cbAddr);
return (SUCCEEDED(hr)) ? (PSOCKADDR_IN) pAddr : NULL;
#endif
}
STDMETHODIMP ImpICommChan::GetRemoteAddress(PSOCKADDR_IN pAddrOutput)
{
HRESULT hr;
if (!pAddrOutput)
{
return CHAN_E_INVALID_PARAM;
}
const BYTE *pAddr;
UINT cbAddr;
hr = m_pRTPChan->GetRemoteRTPAddress(&pAddr, &cbAddr);
if(SUCCEEDED(hr))
{
ASSERT(cbAddr == sizeof(SOCKADDR_IN));
*pAddrOutput = *((PSOCKADDR_IN) pAddr);
}
return hrSuccess;
}
UINT ImpICommChan::Reset()
{
UINT uret;
ASSERT(!IsComchOpen());
if (m_pRTPChan) {
uret = m_pRTPChan->Release();
m_pRTPChan = NULL;
} else
uret = 0;
return uret;
}
PORT ImpICommChan::GetLocalRTPPort()
{
#ifdef OLDSTUFF
return (m_pRTPChan ? ntohs(m_pRTPChan->GetChannelDescription()->pLocalAddr->sin_port) : 0);
#else
const BYTE *pAddr;
UINT cbAddr;
HRESULT hr;
hr = m_pRTPChan->GetLocalAddress(&pAddr, &cbAddr);
return (SUCCEEDED(hr)) ? ntohs(((PSOCKADDR_IN) pAddr)->sin_port) : 0;
#endif
}
PORT ImpICommChan::GetLocalRTCPPort()
{
#ifdef OLDSTUFF
return (m_pRTPChan ? ntohs(m_pRTPChan->GetChannelDescription()->pLocalRTCPAddr->sin_port) : 0);
#else
const BYTE *pAddr;
UINT cbAddr;
HRESULT hr;
hr = m_pRTPChan->GetLocalAddress(&pAddr, &cbAddr);
return (SUCCEEDED(hr)) ? ntohs(((PSOCKADDR_IN) pAddr)->sin_port)+1 : 0;
#endif
}
HRESULT ImpICommChan::AcceptRemoteRTCPAddress(PSOCKADDR_IN pSinC)
{
HRESULT hr;
#ifdef OLDSTUFF
if (!m_pRTPChan) {
RTPCHANNELDESC chanDesc = {0};
GetMediaType(&chanDesc.mediaId);
chanDesc.pRemoteRTCPAddr = pSinC;
hr = CreateRTPChannel(&chanDesc, &m_pRTPChan);
} else
hr = m_pRTPChan->SetRemoteAddresses(NULL,pSinC);
#else
hr = m_pRTPChan->SetRemoteRTCPAddress((BYTE *)pSinC, sizeof(SOCKADDR_IN));
#endif
return hr;
}
HRESULT ImpICommChan::AcceptRemoteAddress(PSOCKADDR_IN pSinD)
{
HRESULT hr;
hr = m_pRTPChan->SetRemoteRTPAddress((BYTE *)pSinD, sizeof(SOCKADDR_IN));
return hr;
}
HRESULT ImpICommChan::SetAdviseInterface(IH323ConfAdvise *pH323ConfAdvise)
{
if (!pH323ConfAdvise)
{
return CHAN_E_INVALID_PARAM;
}
m_pH323ConfAdvise = pH323ConfAdvise;
return hrSuccess;
}
STDMETHODIMP ImpICommChan::PictureUpdateRequest()
{
FX_ENTRY ("ImpICommChan::PictureUpdateRequest");
HRESULT hr;
if (!m_pCtlChan)
{
return CHAN_E_NOT_OPEN;
}
if(bIsSendDirection || (MEDIA_TYPE_H323VIDEO != m_MediaID))
{
return CHAN_E_INVALID_PARAM;
}
// issue miscellaneous command for picture update
MiscellaneousCommand mc;
// mc.logicalChannelNumber = ?; ** call control fills this in **
mc.type.choice = videoFastUpdatePicture_chosen;
// do the control channel signaling for THIS channel
hr = m_pCtlChan->MiscChannelCommand(this, &mc);
// record the tick count of this command
m_dwLastUpdateTick = GetTickCount();
return hr;
}
STDMETHODIMP ImpICommChan::GetVersionInfo(
PCC_VENDORINFO *ppLocalVendorInfo,
PCC_VENDORINFO *ppRemoteVendorInfo)
{
FX_ENTRY ("ImpICommChan::GetVersionInfo");
if (!m_pCtlChan)
{
return CHAN_E_INVALID_PARAM;
}
return m_pCtlChan->GetVersionInfo(ppLocalVendorInfo, ppRemoteVendorInfo);
}
ImpICommChan::ImpICommChan ()
:pRemoteParams(NULL),
m_pMediaStream(NULL),
pLocalParams(NULL),
uLocalParamSize(0),
m_pCtlChan(NULL),
m_pH323ConfAdvise(NULL),
m_pCapObject(NULL),
m_dwFlags(0),
dwhChannel(0),
m_LocalFmt(INVALID_MEDIA_FORMAT),
m_RemoteFmt(INVALID_MEDIA_FORMAT),
m_TemporalSpatialTradeoff(0), // default to highest resolution
m_bPublicizeTSTradeoff(FALSE),
m_uRef(1)
{
ZeroMemory(&m_MediaID, sizeof(m_MediaID));
}
ImpICommChan::~ImpICommChan ()
{
if(pRemoteParams)
MemFree(pRemoteParams);
if(pLocalParams)
MemFree(pLocalParams);
if(m_pMediaStream)
{
m_pMediaStream->Stop(); // probably not necessary
m_pMediaStream->Release();
m_pMediaStream = NULL;
}
if(m_pCapObject)
m_pCapObject->Release();
}