1041 lines
26 KiB
C++
1041 lines
26 KiB
C++
// RTPSink.cpp : Implementation of CRTPSession
|
||
#include "stdafx.h"
|
||
#include <qossp.h>
|
||
#include "irtp.h"
|
||
#include "rtp.h"
|
||
#include "RTPSess.h"
|
||
//#include "rtpsink.h"
|
||
//#include "RTPMS.h"
|
||
//#include "RTPSamp.h"
|
||
#include "thread.h"
|
||
#include <rrcm.h>
|
||
|
||
#define DEFAULT_RTPBUF_SIZE 1500
|
||
|
||
#define IPPORT_FIRST_DYNAMIC 49152
|
||
#define IPPORT_FIRST_DYNAMIC_END (IPPORT_FIRST_DYNAMIC + 200)
|
||
#define IPPORT_FIRST_DYNAMIC_BEGIN (IPPORT_FIRST_DYNAMIC_END + 256)
|
||
|
||
// Port number allocation starts at IPPORT_FIRST_DYNAMIC_BEGIN.
|
||
// Everytime a port number is allocated we decrease g_alport, until
|
||
// we reach IPPORT_FIRST_DYNAMIC_END. We then reset it back to its
|
||
// original value (IPPORT_FIRST_DYNAMIC_BEGIN) and start this process
|
||
// all over again. This way we will avoid reusing the same port
|
||
// numbers between sessions.
|
||
u_short g_alport = IPPORT_FIRST_DYNAMIC_BEGIN;
|
||
void __cdecl RRCMNotification(int,DWORD_PTR,DWORD_PTR,DWORD_PTR);
|
||
|
||
|
||
|
||
#define IsMulticast(p) ((p->sin_addr.S_un.S_un_b.s_b1 & 0xF0) == 0xE0)
|
||
|
||
|
||
BOOL CRTP::m_WSInitialized = 0;
|
||
|
||
|
||
|
||
STDMETHODIMP CRTP::OpenSession(
|
||
UINT sessionId, // client specified unique identifier for the session
|
||
DWORD flags, // SESSIONF_SEND, SESSIONF_RECV, SESSIONF_MULTICAST etc.
|
||
BYTE *localAddr,
|
||
UINT cbAddr,
|
||
IRTPSession **ppIRTP) // [output] pointer to RTPSession
|
||
{
|
||
// the session is named by the sessionId
|
||
|
||
CRTPSession *pRTPSess ;
|
||
HRESULT hr= E_FAIL;
|
||
UINT mediaId = flags & (SESSIONF_AUDIO | SESSIONF_VIDEO);
|
||
|
||
EnterCriticalSection(&g_CritSect);
|
||
for (pRTPSess= CRTPSession::m_pSessFirst; pRTPSess; pRTPSess = pRTPSess->m_pSessNext ) {
|
||
// check for existing session of the same media type
|
||
// if the sessionId is not zero, also check for matching session id
|
||
if (sessionId == pRTPSess->m_sessionId)
|
||
if (mediaId == pRTPSess->m_mediaId)
|
||
break;
|
||
// if the local address or remote address is not NULL, search for an exising RTP session bound to
|
||
// the same address
|
||
// TODO
|
||
|
||
}
|
||
|
||
if (!pRTPSess)
|
||
{
|
||
if (!(flags & SESSIONF_EXISTING)) {
|
||
// create the session
|
||
ObjRTPSession *pObj;
|
||
DEBUGMSG(ZONE_DP,("Creating new RTP session\n"));
|
||
hr = ObjRTPSession::CreateInstance(&pObj);
|
||
if (hr == S_OK) {
|
||
pRTPSess = pObj; // pointer conversion
|
||
hr = pRTPSess->Initialize(sessionId, mediaId,localAddr,cbAddr);
|
||
if (hr != S_OK)
|
||
delete pObj;
|
||
}
|
||
}
|
||
else
|
||
hr = E_FAIL; // matching session does not exist
|
||
|
||
} else {
|
||
DEBUGMSG(ZONE_DP,("Reusing RTP session\n"));
|
||
hr = S_OK;
|
||
}
|
||
if (hr == S_OK) {
|
||
hr = ((IRTPSession *)pRTPSess)->QueryInterface(IID_IRTPSession,(void **) ppIRTP);
|
||
|
||
}
|
||
LeaveCriticalSection(&g_CritSect);
|
||
return hr;
|
||
}
|
||
|
||
|
||
CRTPSession *CRTPSession::m_pSessFirst = NULL;
|
||
/////////////////////////////////////////////////////////////////////////////
|
||
// CRTPSession
|
||
|
||
CRTPSession::CRTPSession()
|
||
: m_hRTPSession(NULL), m_uMaxPacketSize(1500),m_nBufsPosted(0),m_pRTPCallback(NULL),
|
||
m_fSendingSync(FALSE)
|
||
{
|
||
ZeroMemory(&m_sOverlapped,sizeof(m_sOverlapped));
|
||
ZeroMemory(&m_ss, sizeof(m_ss));
|
||
ZeroMemory(&m_rs, sizeof(m_rs));
|
||
m_sOverlapped.hEvent = (WSAEVENT)this;
|
||
}
|
||
|
||
/*
|
||
HRESULT CRTPSession::GetLocalAddress(
|
||
unsigned char *sockaddr,
|
||
UINT *paddrlen)
|
||
{
|
||
if (m_pRTPSess && *paddrlen >= sizeof(SOCKADDR_IN))
|
||
{
|
||
*paddrlen = sizeof(SOCKADDR_IN);
|
||
CopyMemory(sockaddr, m_pRTPSess->GetLocalAddress(), *paddrlen);
|
||
}
|
||
}
|
||
*/
|
||
HRESULT
|
||
CRTPSession::FinalRelease()
|
||
{
|
||
|
||
CRTPPacket1 *pRTPPacket;
|
||
// remove myself from the session list
|
||
EnterCriticalSection(&g_CritSect);
|
||
if (m_pSessFirst == this)
|
||
m_pSessFirst = m_pSessNext;
|
||
else {
|
||
CRTPSession *pRTPSess = m_pSessFirst;
|
||
while (pRTPSess && pRTPSess->m_pSessNext != this) {
|
||
pRTPSess = pRTPSess->m_pSessNext;
|
||
}
|
||
if (pRTPSess)
|
||
pRTPSess->m_pSessNext = m_pSessNext;
|
||
}
|
||
LeaveCriticalSection(&g_CritSect);
|
||
|
||
if (m_rtpsock) {
|
||
delete m_rtpsock;
|
||
m_rtpsock = NULL;
|
||
}
|
||
// BUGBUG: in case the buffers have not been canceled yet (an error case),
|
||
// they should complete now with WSA_OPERATION_ABORTED
|
||
// or WSAEINTR. This happens in the context of the RecvThread
|
||
if (m_nBufsPosted != 0)
|
||
Sleep(500); // time for APCs to be processed in RecvThread
|
||
|
||
// close the RTP session. Also ask RRCM to close the rtcp socket if its WS2
|
||
// because that is a more reliable way of cleaning up overlapped recvs than
|
||
// sending loopback packets.
|
||
CloseRTPSession (m_hRTPSession, NULL, TRUE );
|
||
|
||
if (m_rtcpsock) {
|
||
delete m_rtcpsock;
|
||
m_rtcpsock = NULL;
|
||
}
|
||
m_hRTPSession = 0;
|
||
// free receive buffers
|
||
while (m_FreePkts.Get(&pRTPPacket))
|
||
{
|
||
delete pRTPPacket;
|
||
}
|
||
|
||
|
||
return S_OK;
|
||
}
|
||
|
||
|
||
|
||
HRESULT CRTPSession::CreateRecvRTPStream(DWORD ssrc, IRTPRecv **ppIRTPRecv)
|
||
{
|
||
HRESULT hr;
|
||
Lock();
|
||
if (ssrc != 0)
|
||
return E_NOTIMPL;
|
||
|
||
#if 0
|
||
ObjRTPRecvSource *pRecvS;
|
||
hr = ObjRTPMediaStream::CreateInstance(&pMS);
|
||
|
||
if (SUCCEEDED(hr))
|
||
{
|
||
pMS->AddRef();
|
||
pMS->Init(this, m_mediaId);
|
||
hr = pMS->QueryInterface(IID_IRTPMediaStream, (void**)ppIRTPMediaStream);
|
||
pMS->Release();
|
||
}
|
||
#else
|
||
*ppIRTPRecv = this;
|
||
(*ppIRTPRecv)->AddRef();
|
||
hr = S_OK;
|
||
#endif
|
||
Unlock();
|
||
return hr;
|
||
}
|
||
|
||
|
||
|
||
ULONG GetRandom()
|
||
{
|
||
return GetTickCount();
|
||
}
|
||
|
||
HRESULT CRTPSession::Initialize(UINT sessionId, UINT mediaId, BYTE *pLocalAddr, UINT cbAddr)
|
||
{
|
||
DWORD APIstatus;
|
||
HRESULT hr = E_OUTOFMEMORY;
|
||
char tmpBfr[MAX_COMPUTERNAME_LENGTH + 1];
|
||
DWORD tmpBfrLen = sizeof(tmpBfr);
|
||
SDES_DATA sdesInfo[3];
|
||
ENCRYPT_INFO encryptInfo;
|
||
SOCKADDR_IN *pSockAddr;
|
||
|
||
|
||
m_sessionId = sessionId;
|
||
m_mediaId = mediaId;
|
||
m_rtpsock = new UDPSOCKET();
|
||
m_rtcpsock = new UDPSOCKET();
|
||
|
||
if (!m_rtpsock || !m_rtcpsock)
|
||
goto ERROR_EXIT;
|
||
|
||
|
||
if(!m_rtpsock->NewSock() || !m_rtcpsock->NewSock())
|
||
{
|
||
goto ERROR_EXIT;
|
||
}
|
||
|
||
// if the local address is specified do a bind on the sockets
|
||
if (pLocalAddr) {
|
||
// setup both channels for the current local address
|
||
hr = SetLocalAddress(pLocalAddr,cbAddr);
|
||
|
||
if (hr != S_OK)
|
||
goto ERROR_EXIT;
|
||
}
|
||
/*
|
||
// if the remote address is specified make a note of it
|
||
SetRemoteAddresses(pChanDesc->pRemoteAddr, pChanDesc->pRemoteRTCPAddr);
|
||
*/
|
||
// init send state
|
||
memset (&m_ss.sendStats,0,sizeof(m_ss.sendStats));
|
||
// init RTP send header
|
||
// time stamp and marker bit have to specified per packet
|
||
m_ss.hdr.p = 0; // no padding needed
|
||
m_ss.hdr.x = 0; // no extensions
|
||
m_ss.hdr.cc = 0; // no contributing sources
|
||
m_ss.hdr.seq = (WORD)GetRandom();
|
||
|
||
|
||
m_clockRate = (m_mediaId == SESSIONF_VIDEO ? 90000 : 8000); // typically 8KHz for audio
|
||
m_ss.hdr.pt = 0;
|
||
|
||
|
||
// Initialize list of overlapped structs
|
||
|
||
// build a Cname
|
||
memcpy(tmpBfr,"CName",6);
|
||
GetComputerName(tmpBfr,&tmpBfrLen);
|
||
|
||
// build the SDES information
|
||
sdesInfo[0].dwSdesType = 1;
|
||
memcpy (sdesInfo[0].sdesBfr, tmpBfr, strlen(tmpBfr)+1);
|
||
sdesInfo[0].dwSdesLength = strlen(sdesInfo[0].sdesBfr);
|
||
sdesInfo[0].dwSdesFrequency = 100;
|
||
sdesInfo[0].dwSdesEncrypted = 0;
|
||
|
||
// Build a Name
|
||
tmpBfrLen = sizeof(tmpBfr);
|
||
memcpy(tmpBfr,"UserName",9);
|
||
GetUserName(tmpBfr,&tmpBfrLen);
|
||
sdesInfo[1].dwSdesType = 2;
|
||
memcpy (sdesInfo[1].sdesBfr, tmpBfr, strlen(tmpBfr)+1);
|
||
sdesInfo[1].dwSdesLength = strlen(sdesInfo[1].sdesBfr);
|
||
sdesInfo[1].dwSdesFrequency = 25;
|
||
sdesInfo[1].dwSdesEncrypted = 0;
|
||
|
||
// end of SDES list
|
||
sdesInfo[2].dwSdesType = 0;
|
||
|
||
pSockAddr = m_rtcpsock->GetRemoteAddress();
|
||
#ifdef DEBUG
|
||
if (pSockAddr->sin_addr.s_addr == INADDR_ANY)
|
||
DEBUGMSG(ZONE_DP,("Null dest RTCP addr\n"));
|
||
#endif
|
||
|
||
// Create the RTP/RTCP session
|
||
|
||
m_hRTPSession = CreateRTPSession(
|
||
(m_rtpsock->GetSock()),
|
||
(m_rtcpsock->GetSock()),
|
||
(LPVOID)pSockAddr,
|
||
(pSockAddr->sin_addr.s_addr == INADDR_ANY)? 0 : sizeof(SOCKADDR_IN),
|
||
sdesInfo,
|
||
(DWORD)m_clockRate,
|
||
&encryptInfo,
|
||
0,
|
||
(PRRCM_EVENT_CALLBACK)RRCMNotification, // callback function
|
||
(DWORD_PTR) this, // callback info
|
||
RTCP_ON|H323_CONFERENCE,
|
||
0, //rtp session bandwidth
|
||
&APIstatus);
|
||
|
||
if (m_hRTPSession == NULL)
|
||
{
|
||
DEBUGMSG(ZONE_DP,("Couldnt create RRCM session\n"));
|
||
hr = GetLastError();
|
||
goto ERROR_EXIT;
|
||
}
|
||
|
||
m_Qos.SendingFlowspec.ServiceType = SERVICETYPE_NOTRAFFIC;
|
||
m_Qos.SendingFlowspec.TokenRate = QOS_NOT_SPECIFIED;
|
||
m_Qos.SendingFlowspec.TokenBucketSize = QOS_NOT_SPECIFIED;
|
||
m_Qos.SendingFlowspec.PeakBandwidth = QOS_NOT_SPECIFIED;
|
||
m_Qos.SendingFlowspec.Latency = QOS_NOT_SPECIFIED;
|
||
m_Qos.SendingFlowspec.DelayVariation = QOS_NOT_SPECIFIED;
|
||
m_Qos.SendingFlowspec.MaxSduSize = QOS_NOT_SPECIFIED;
|
||
m_Qos.ReceivingFlowspec = m_Qos.SendingFlowspec;
|
||
m_Qos.ProviderSpecific.buf = NULL;
|
||
m_Qos.ProviderSpecific.len = 0;
|
||
|
||
|
||
// insert RTPSession in global list
|
||
m_pSessNext = m_pSessFirst;
|
||
m_pSessFirst = this;
|
||
|
||
return S_OK;
|
||
|
||
ERROR_EXIT:
|
||
if (m_rtpsock)
|
||
{
|
||
delete m_rtpsock;
|
||
m_rtpsock = NULL;
|
||
}
|
||
if (m_rtcpsock)
|
||
{
|
||
delete m_rtcpsock;
|
||
m_rtcpsock = NULL;
|
||
}
|
||
|
||
return hr;
|
||
|
||
}
|
||
|
||
|
||
|
||
BOOL CRTPSession::SelectPorts()
|
||
{
|
||
|
||
// try port pairs in the dynamic range ( > 49152)
|
||
if (g_alport <= IPPORT_FIRST_DYNAMIC_END)
|
||
g_alport = IPPORT_FIRST_DYNAMIC_BEGIN;
|
||
|
||
|
||
|
||
for (;g_alport >= IPPORT_FIRST_DYNAMIC;)
|
||
{
|
||
m_rtpsock->SetLocalPort(g_alport);
|
||
|
||
if (m_rtpsock->BindMe() == 0)
|
||
{
|
||
/* it worked for the data, try the adjacent port for control*/
|
||
++g_alport;
|
||
|
||
m_rtcpsock->SetLocalPort(g_alport);
|
||
if (m_rtcpsock->BindMe() == 0)
|
||
{
|
||
g_alport-=3;
|
||
return TRUE;
|
||
}
|
||
else // start over at the previous even numbered port
|
||
{
|
||
if( WSAGetLastError() != WSAEADDRINUSE)
|
||
{
|
||
DEBUGMSG(ZONE_DP,("ObjRTPSession::SelectPorts failed with error %d\n",WSAGetLastError()));
|
||
goto ERROR_EXIT;
|
||
}
|
||
m_rtpsock->Cleanup();
|
||
if(!m_rtpsock->NewSock())
|
||
{
|
||
ASSERT(0);
|
||
return FALSE;
|
||
}
|
||
g_alport-=3;
|
||
continue;
|
||
}
|
||
|
||
}
|
||
if (WSAGetLastError() != WSAEADDRINUSE)
|
||
{
|
||
DEBUGMSG(ZONE_DP,("ObjRTPSession::SelectPorts failed with error %d\n",WSAGetLastError()));
|
||
goto ERROR_EXIT;
|
||
}
|
||
g_alport-=2; // try the next lower even numbered port
|
||
}
|
||
|
||
ERROR_EXIT:
|
||
m_rtcpsock->SetLocalPort(0);
|
||
m_rtpsock->SetLocalPort(0);
|
||
return FALSE;
|
||
}
|
||
|
||
STDMETHODIMP CRTPSession::SetLocalAddress(BYTE *pbAddr, UINT cbAddr)
|
||
{
|
||
HRESULT hr;
|
||
SOCKADDR_IN *pAddr = (SOCKADDR_IN *)pbAddr;
|
||
ASSERT(pbAddr);
|
||
|
||
Lock();
|
||
if ( IsMulticast(pAddr))
|
||
hr = SetMulticastAddress(pAddr);
|
||
else
|
||
if (m_rtpsock->GetLocalAddress()->sin_port != 0)
|
||
hr = S_OK; // already bound
|
||
else
|
||
{
|
||
m_rtpsock->SetLocalAddress(pAddr);
|
||
m_rtcpsock->SetLocalAddress(pAddr);
|
||
if (pAddr->sin_port != 0)
|
||
{
|
||
// port already chosen
|
||
m_rtcpsock->SetLocalPort(ntohs(pAddr->sin_port) + 1);
|
||
if (m_rtpsock->BindMe() != 0 || m_rtcpsock->BindMe() != 0)
|
||
{
|
||
hr = HRESULT_FROM_WIN32(GetLastError());
|
||
m_rtpsock->SetLocalPort(0);
|
||
m_rtcpsock->SetLocalPort(0);
|
||
}
|
||
else
|
||
hr = S_OK;
|
||
}
|
||
else
|
||
{
|
||
// client wants us to choose the port
|
||
|
||
if (SelectPorts()) {
|
||
hr = S_OK;
|
||
}
|
||
else
|
||
hr = HRESULT_FROM_WIN32(GetLastError());
|
||
}
|
||
}
|
||
Unlock();
|
||
return hr;
|
||
}
|
||
|
||
HRESULT
|
||
CRTPSession::SetMulticastAddress(PSOCKADDR_IN pAddr)
|
||
{
|
||
SOCKET s ;
|
||
SOCKADDR_IN rtcpAddr = *pAddr;
|
||
s = RRCMws.WSAJoinLeaf( m_rtpsock->GetSock(), (struct sockaddr *)pAddr, sizeof(SOCKADDR_IN), NULL, NULL, NULL, NULL, JL_BOTH);
|
||
if (s == INVALID_SOCKET)
|
||
return E_FAIL;
|
||
else {
|
||
rtcpAddr.sin_port = htons(ntohs(pAddr->sin_port)+1);
|
||
s = RRCMws.WSAJoinLeaf( m_rtcpsock->GetSock(), (struct sockaddr *)&rtcpAddr, sizeof(SOCKADDR_IN), NULL, NULL, NULL, NULL, JL_BOTH);
|
||
|
||
return S_OK;
|
||
}
|
||
}
|
||
|
||
|
||
STDMETHODIMP
|
||
CRTPSession::SetRemoteRTPAddress(BYTE *sockaddr, UINT cbAddr)
|
||
{
|
||
SOCKADDR_IN *pRTPAddr = (SOCKADDR_IN *)sockaddr;
|
||
Lock();
|
||
|
||
if (pRTPAddr) {
|
||
#ifdef DEBUG
|
||
if (m_rtpsock->GetRemoteAddress()->sin_addr.s_addr != INADDR_ANY
|
||
&& m_rtpsock->GetRemoteAddress()->sin_addr.s_addr != pRTPAddr->sin_addr.s_addr) {
|
||
DEBUGMSG(ZONE_DP,("Changing RTP Session remote address (already set)!\n"));
|
||
}
|
||
#endif
|
||
m_rtpsock->SetRemoteAddr(pRTPAddr);
|
||
}
|
||
|
||
Unlock();
|
||
return S_OK;
|
||
}
|
||
|
||
|
||
STDMETHODIMP
|
||
CRTPSession::SetRemoteRTCPAddress(BYTE *sockaddr, UINT cbAddr)
|
||
{
|
||
SOCKADDR_IN *pRTCPAddr = (SOCKADDR_IN *)sockaddr;
|
||
|
||
Lock();
|
||
|
||
if (pRTCPAddr) {
|
||
#ifdef DEBUG
|
||
if (m_rtcpsock->GetRemoteAddress()->sin_addr.s_addr != INADDR_ANY
|
||
&& m_rtcpsock->GetRemoteAddress()->sin_addr.s_addr != pRTCPAddr->sin_addr.s_addr) {
|
||
DEBUGMSG(ZONE_DP,("Changing RTCP Session remote address (already set)!\n"));
|
||
}
|
||
#endif
|
||
m_rtcpsock->SetRemoteAddr(pRTCPAddr);
|
||
if (m_hRTPSession)
|
||
updateRTCPDestinationAddress( m_hRTPSession,
|
||
(SOCKADDR *)m_rtcpsock->GetRemoteAddress(), sizeof(SOCKADDR_IN));
|
||
}
|
||
Unlock();
|
||
return S_OK;
|
||
}
|
||
|
||
STDMETHODIMP
|
||
CRTPSession::GetLocalAddress(const BYTE **sockaddr, UINT *pcbAddr)
|
||
{
|
||
if (sockaddr && pcbAddr)
|
||
{
|
||
Lock();
|
||
*sockaddr = (BYTE *)m_rtpsock->GetLocalAddress();
|
||
*pcbAddr = sizeof(SOCKADDR_IN);
|
||
Unlock();
|
||
return S_OK;
|
||
} else
|
||
{
|
||
return E_INVALIDARG;
|
||
}
|
||
}
|
||
|
||
STDMETHODIMP
|
||
CRTPSession::GetRemoteRTPAddress(const BYTE **sockaddr, UINT *pcbAddr)
|
||
{
|
||
if (sockaddr && pcbAddr )
|
||
{
|
||
Lock();
|
||
*sockaddr = (BYTE *)m_rtpsock->GetRemoteAddress();
|
||
*pcbAddr = sizeof(SOCKADDR_IN);
|
||
Unlock();
|
||
return S_OK;
|
||
} else
|
||
{
|
||
return E_INVALIDARG;
|
||
}
|
||
}
|
||
|
||
STDMETHODIMP
|
||
CRTPSession::GetRemoteRTCPAddress(const BYTE **sockaddr, UINT *pcbAddr)
|
||
{
|
||
if (sockaddr && pcbAddr)
|
||
{
|
||
Lock();
|
||
*sockaddr = (BYTE *)m_rtcpsock->GetRemoteAddress();
|
||
*pcbAddr = sizeof(SOCKADDR_IN);
|
||
Unlock();
|
||
return S_OK;
|
||
} else
|
||
{
|
||
return E_INVALIDARG;
|
||
}
|
||
}
|
||
|
||
STDMETHODIMP
|
||
CRTPSession::SetSendFlowspec(FLOWSPEC *pFlowspec)
|
||
{
|
||
QOS_DESTADDR qosDest;
|
||
DWORD cbRet;
|
||
int optval = pFlowspec->MaxSduSize;
|
||
// Set the RTP socket to not buffer more than one packet
|
||
// This will allow us to influence the packet scheduling.
|
||
if(RRCMws.setsockopt(m_rtpsock->GetSock(),SOL_SOCKET, SO_SNDBUF,(char *)&optval,sizeof(optval)) != 0)
|
||
{
|
||
|
||
RRCM_DBG_MSG ("setsockopt failed ", GetLastError(),
|
||
__FILE__, __LINE__, DBG_ERROR);
|
||
}
|
||
|
||
if (WSQOSEnabled && m_rtpsock)
|
||
{
|
||
|
||
m_Qos.SendingFlowspec = *pFlowspec;
|
||
m_Qos.ProviderSpecific.buf = (char *)&qosDest; // NULL
|
||
m_Qos.ProviderSpecific.len = sizeof (qosDest); // 0
|
||
|
||
// check to see if the receive flowspec has already been
|
||
// set. If it has, specify NOCHANGE for the receive service
|
||
// type. If not, specify NOTRAFFIC. This is done to circumvent
|
||
// a bug in the Win98 QOS/RSVP layer.
|
||
|
||
if (m_Qos.ReceivingFlowspec.TokenRate == QOS_NOT_SPECIFIED)
|
||
{
|
||
m_Qos.ReceivingFlowspec.ServiceType = SERVICETYPE_NOTRAFFIC;
|
||
}
|
||
else
|
||
{
|
||
m_Qos.ReceivingFlowspec.ServiceType = SERVICETYPE_NOCHANGE;
|
||
}
|
||
|
||
qosDest.ObjectHdr.ObjectType = QOS_OBJECT_DESTADDR;
|
||
qosDest.ObjectHdr.ObjectLength = sizeof(qosDest);
|
||
qosDest.SocketAddress = (PSOCKADDR)m_rtpsock->GetRemoteAddress();
|
||
qosDest.SocketAddressLength = sizeof(SOCKADDR_IN);
|
||
if (RRCMws.WSAIoctl(m_rtpsock->GetSock(),SIO_SET_QOS, &m_Qos, sizeof(m_Qos), NULL, 0, &cbRet, NULL,NULL) == 0)
|
||
return S_OK;
|
||
else
|
||
return GetLastError();
|
||
} else
|
||
return E_NOTIMPL;
|
||
|
||
}
|
||
|
||
STDMETHODIMP
|
||
CRTPSession::SetRecvFlowspec(FLOWSPEC *pFlowspec)
|
||
{
|
||
|
||
SOCKADDR_IN *pAddr = NULL;
|
||
|
||
DWORD cbRet;
|
||
if (WSQOSEnabled && m_rtpsock)
|
||
{
|
||
|
||
pAddr = m_rtpsock->GetRemoteAddress();
|
||
|
||
m_Qos.ReceivingFlowspec = *pFlowspec;
|
||
m_Qos.ProviderSpecific.buf = NULL;
|
||
m_Qos.ProviderSpecific.len = 0;
|
||
|
||
// check to see if the send flowspec has already been
|
||
// set. If it has, specify NOCHANGE for the receive service
|
||
// type. If not, specify NOTRAFFIC. This is done to circumvent
|
||
// a bug in the Win98 QOS/RSVP layer.
|
||
|
||
if (m_Qos.SendingFlowspec.TokenRate == QOS_NOT_SPECIFIED)
|
||
{
|
||
m_Qos.SendingFlowspec.ServiceType = SERVICETYPE_NOTRAFFIC;
|
||
}
|
||
else
|
||
{
|
||
m_Qos.SendingFlowspec.ServiceType = SERVICETYPE_NOCHANGE;
|
||
}
|
||
|
||
if (RRCMws.WSAIoctl(m_rtpsock->GetSock(),SIO_SET_QOS, &m_Qos, sizeof(m_Qos), NULL, 0, &cbRet, NULL,NULL) == 0)
|
||
return S_OK;
|
||
else
|
||
return GetLastError();
|
||
} else
|
||
return E_NOTIMPL;
|
||
}
|
||
|
||
// set the size used for receive packet buffers
|
||
STDMETHODIMP
|
||
CRTPSession::SetMaxPacketSize(UINT maxPacketSize)
|
||
{
|
||
m_uMaxPacketSize = maxPacketSize;
|
||
return S_OK;
|
||
}
|
||
|
||
HRESULT CRTPSession::SetRecvNotification(
|
||
PRTPRECVCALLBACK pRTPRecvCB, // pointer to callback function
|
||
DWORD_PTR dwCB, // callback arg
|
||
UINT nBufs // suggested number of receives to post
|
||
)
|
||
{
|
||
CRTPPacket1 *pRTPPacket;
|
||
if (!m_hRTPSession)
|
||
return E_FAIL;
|
||
|
||
m_pRTPCallback = pRTPRecvCB;
|
||
m_dwCallback = dwCB;
|
||
|
||
if (m_nBufsPosted >= nBufs)
|
||
return S_OK; // packets already posted
|
||
|
||
int nBufsToAllocate = nBufs - m_nBufsPosted - m_FreePkts.GetCount();
|
||
// allocate packets if necessary
|
||
while (nBufsToAllocate-- > 0)
|
||
{
|
||
if (pRTPPacket = new CRTPPacket1) {
|
||
if (!SUCCEEDED(pRTPPacket->Init(m_uMaxPacketSize)))
|
||
{
|
||
delete pRTPPacket;
|
||
break;
|
||
}
|
||
m_FreePkts.Put(pRTPPacket);
|
||
}
|
||
else
|
||
break;
|
||
}
|
||
PostRecv();
|
||
return m_nBufsPosted ? S_OK : E_OUTOFMEMORY;
|
||
}
|
||
|
||
HRESULT
|
||
CRTPSession::CancelRecvNotification()
|
||
{
|
||
m_pRTPCallback = NULL;
|
||
if (m_rtpsock) {
|
||
struct sockaddr myaddr;
|
||
int myaddrlen = sizeof(myaddr);
|
||
UINT i;
|
||
char buf = 0;
|
||
WSABUF wsabuf;
|
||
DWORD bytesSent;
|
||
UINT nBufsPosted;
|
||
wsabuf.buf = &buf;
|
||
wsabuf.len = 0;
|
||
BOOL fCanceled = FALSE;
|
||
if (RRCMws.getsockname(m_rtpsock->GetSock(),&myaddr,&myaddrlen)== 0) {
|
||
// send loopback packets (as many as there are recvs outstanding)
|
||
// on this socket to get back our buffers.
|
||
// NOTE: Winsock 2 on Win95 seems to have a bug where we get recv callbacks
|
||
// within sendto() rather than in the subsequent SleepEx, so we
|
||
// have to make a local copy of m_nBufsPosted
|
||
for (i=0, nBufsPosted = m_nBufsPosted;i < nBufsPosted;i++) {
|
||
if (RRCMws.sendTo(m_rtpsock->GetSock(),&wsabuf,1,&bytesSent,0,&myaddr, myaddrlen, NULL, NULL) < 0) {
|
||
DEBUGMSG(ZONE_DP,("CancelRecv: loopback send failed\n"));
|
||
break;
|
||
}
|
||
}
|
||
fCanceled = (i > 0);
|
||
} else {
|
||
DEBUGMSG(ZONE_DP,("RTPState::CancelRecv: getsockname returned %d\n",GetLastError()));
|
||
}
|
||
if (fCanceled)
|
||
while (m_nBufsPosted) {
|
||
DWORD dwStatus;
|
||
dwStatus = SleepEx(200,TRUE);
|
||
ASSERT(dwStatus==WAIT_IO_COMPLETION);
|
||
if (dwStatus !=WAIT_IO_COMPLETION)
|
||
break; // timed out => bail
|
||
}
|
||
|
||
|
||
}
|
||
return S_OK;
|
||
}
|
||
|
||
HRESULT
|
||
CRTPSession::PostRecv()
|
||
{
|
||
HRESULT hr;
|
||
DWORD dwError = 0;
|
||
DWORD dwRcvFlag;
|
||
WSAOVERLAPPED *pOverlapped;
|
||
DWORD nBytesRcvd;
|
||
CRTPPacket1 *pRTPPacket;
|
||
|
||
if (!m_hRTPSession || !m_pRTPCallback)
|
||
return E_FAIL;
|
||
|
||
// post buffers in the free queue
|
||
while (m_FreePkts.Get(&pRTPPacket))
|
||
{
|
||
pOverlapped = (WSAOVERLAPPED *)(pRTPPacket->GetOverlapped());
|
||
pOverlapped->hEvent = (WSAEVENT) this;
|
||
|
||
m_rcvSockAddrLen = sizeof(SOCKADDR);
|
||
|
||
dwRcvFlag = 0;
|
||
pRTPPacket->RestoreSize();
|
||
dwError = RRCMws.recvFrom (m_rtpsock->GetSock(),
|
||
pRTPPacket->GetWSABUF(),
|
||
1,
|
||
&nBytesRcvd,
|
||
&dwRcvFlag,
|
||
&m_rcvSockAddr,
|
||
&m_rcvSockAddrLen,
|
||
pOverlapped,
|
||
(LPWSAOVERLAPPED_COMPLETION_ROUTINE)WS2RecvCB);
|
||
if (dwError == SOCKET_ERROR) {
|
||
dwError = WSAGetLastError();
|
||
if (dwError != WSA_IO_PENDING) {
|
||
DEBUGMSG(ZONE_DP,("RTP recv error %d\n",dwError));
|
||
// m_rs.rcvStats.packetErrors++;
|
||
// return the buffer to the free list
|
||
m_FreePkts.Put(pRTPPacket);
|
||
break;
|
||
}
|
||
|
||
}
|
||
++m_nBufsPosted;
|
||
}
|
||
return m_nBufsPosted ? S_OK : S_FALSE;
|
||
}
|
||
|
||
HRESULT
|
||
CRTPSession::FreePacket(WSABUF *pBuf)
|
||
{
|
||
m_FreePkts.Put(CRTPPacket1::GetRTPPacketFromWSABUF(pBuf));
|
||
PostRecv();
|
||
return S_OK;
|
||
}
|
||
|
||
/*----------------------------------------------------------------------------
|
||
* Function: WS2SendCB
|
||
* Description: Winsock callback provided by the application to Winsock
|
||
*
|
||
* Input:
|
||
*
|
||
* Return: None
|
||
*--------------------------------------------------------------------------*/
|
||
void CALLBACK WS2SendCB (DWORD dwError,
|
||
DWORD cbTransferred,
|
||
LPWSAOVERLAPPED lpOverlapped,
|
||
DWORD dwFlags)
|
||
{
|
||
CRTPSession *pSess;
|
||
//get the RTPSession pointer so that we can mark the
|
||
//IO complete on the object
|
||
pSess= (CRTPSession *)lpOverlapped->hEvent;
|
||
ASSERT (&pSess->m_sOverlapped == lpOverlapped);
|
||
pSess->m_lastSendError = dwError;
|
||
pSess->m_fSendingSync=FALSE;
|
||
}
|
||
|
||
|
||
void CALLBACK WS2RecvCB (DWORD dwError,
|
||
DWORD len,
|
||
LPWSAOVERLAPPED lpOverlapped,
|
||
DWORD dwFlags)
|
||
{
|
||
|
||
CRTPSession *pRTP;
|
||
CRTPPacket1 *pRTPPacket;
|
||
|
||
DWORD ts, ssrc;
|
||
|
||
// GEORGEJ: catch Winsock 2 bug (94903) where I get a bogus callback
|
||
// after WSARecv returns WSAEMSGSIZE.
|
||
if (!dwError && ((int) len < 0)) {
|
||
RRCM_DBG_MSG ("RTP : RCV Callback : bad cbTransferred", len,
|
||
__FILE__, __LINE__, DBG_ERROR);
|
||
return;
|
||
}
|
||
pRTP = (CRTPSession *)lpOverlapped->hEvent; // cached by PostRecv
|
||
ASSERT(pRTP);
|
||
ASSERT(lpOverlapped);
|
||
ASSERT(pRTP->m_nBufsPosted > 0);
|
||
--pRTP->m_nBufsPosted; // one recv just completed
|
||
|
||
// Winsock 2 sometimes chooses to indicate a buffer-too-small
|
||
// error via the dwFlags parameter.
|
||
if (dwFlags & MSG_PARTIAL)
|
||
dwError = WSAEMSGSIZE;
|
||
|
||
pRTPPacket = CRTPPacket1::GetRTPPacketFromOverlapped(lpOverlapped);
|
||
if (!dwError)
|
||
{
|
||
// validate RTP header and update receive stats
|
||
dwError = RTPReceiveCheck(
|
||
pRTP->m_hRTPSession,
|
||
pRTP->m_rtpsock->GetSock(),
|
||
pRTPPacket->GetWSABUF()->buf,
|
||
len,
|
||
&pRTP->m_rcvSockAddr,
|
||
pRTP->m_rcvSockAddrLen
|
||
);
|
||
}
|
||
if (!pRTP->m_pRTPCallback)
|
||
{
|
||
// we have stopped doing notifications
|
||
// return the buffer to the free list
|
||
pRTP->FreePacket(pRTPPacket->GetWSABUF());
|
||
}
|
||
else if (!dwError) {
|
||
// call the callback
|
||
//++pRTP->m_nBufsRecvd;
|
||
// convert the RTP header fields to host order
|
||
pRTPPacket->SetTimestamp(ntohl(pRTPPacket->GetTimestamp()));
|
||
pRTPPacket->SetSeq(ntohs(( u_short)pRTPPacket->GetSeq()));
|
||
pRTPPacket->SetActual(len);
|
||
//LOG((LOGMSG_NET_RECVD,pRTPPacket->GetTimestamp(), pRTPPacket->GetSeq(), GetTickCount()));
|
||
if (!pRTP->m_pRTPCallback(pRTP->m_dwCallback, pRTPPacket->GetWSABUF()))
|
||
pRTP->FreePacket(pRTPPacket->GetWSABUF());
|
||
} else {
|
||
// packet error
|
||
// repost the buffer
|
||
pRTP->PostRecv();
|
||
}
|
||
}
|
||
|
||
// the way its defined now, this Send() method is synchronous or asynchronous
|
||
// depending on whether pOverlapped is NULL or not
|
||
HRESULT CRTPSession::Send(
|
||
WSABUF *pWsabufs,
|
||
UINT nWsabufs,
|
||
WSAOVERLAPPED *pOverlapped,
|
||
LPWSAOVERLAPPED_COMPLETION_ROUTINE pWSAPC )
|
||
{
|
||
DWORD dwError;
|
||
|
||
Lock();
|
||
RTP_HDR_T *pHdr = (RTP_HDR_T *)pWsabufs[0].buf;
|
||
// convert RTP header fields to network-order
|
||
pHdr->ts = htonl (pHdr->ts);
|
||
pHdr->seq = htons(pHdr->seq);
|
||
//*pHdr = m_ss.hdr;
|
||
pHdr->seq = htons(++m_ss.hdr.seq);
|
||
// update send stats
|
||
//m_ss.packetsSent++;
|
||
//m_ss.bytesSent += cbBuf-sizeof(RTP_HDR_MIN_LEN);
|
||
//bIOPending=TRUE; // reset when send completes
|
||
|
||
dwError = RTPSendTo (
|
||
m_hRTPSession,
|
||
(m_rtpsock->GetSock()),
|
||
pWsabufs,
|
||
nWsabufs,
|
||
&m_numBytesSend,
|
||
0,
|
||
(LPVOID)m_rtpsock->GetRemoteAddress(),
|
||
sizeof(SOCKADDR),
|
||
pOverlapped,
|
||
pWSAPC);
|
||
|
||
if (dwError == SOCKET_ERROR) {
|
||
dwError = WSAGetLastError();
|
||
if (dwError != WSA_IO_PENDING) {
|
||
DEBUGMSG(1, ("RTPSendTo error %d\n",dwError));
|
||
m_lastSendError = dwError;
|
||
m_ss.sendStats.packetErrors++;
|
||
m_fSendingSync = FALSE;
|
||
goto ErrorExit;
|
||
}
|
||
dwError = 0; // return success for ERROR_IO_PENDING
|
||
}
|
||
|
||
ErrorExit:
|
||
Unlock();
|
||
return dwError;
|
||
}
|
||
void CRTPSession::RTCPNotify(
|
||
int rrcmEvent,
|
||
DWORD_PTR dwSSRC,
|
||
DWORD_PTR rtcpsock)
|
||
{
|
||
|
||
switch (rrcmEvent) {
|
||
case RRCM_RECV_RTCP_SNDR_REPORT_EVENT:
|
||
GetRTCPReport();
|
||
//DispRTCPReport(rtcpsock);
|
||
break;
|
||
case RRCM_RECV_RTCP_RECV_REPORT_EVENT:
|
||
GetRTCPReport();
|
||
break;
|
||
case RRCM_NEW_SOURCE_EVENT:
|
||
RRCM_DBG_MSG ("RTP : New SSRC", 0,
|
||
__FILE__, __LINE__, DBG_TRACE);
|
||
break;
|
||
default:
|
||
RRCM_DBG_MSG ("RTP : RRCMNotification", rrcmEvent,
|
||
__FILE__, __LINE__, DBG_TRACE);
|
||
break;
|
||
}
|
||
}
|
||
|
||
void RRCMNotification(
|
||
// RRCM_EVENT_T rrcmEvent,
|
||
int rrcmEvent,
|
||
DWORD_PTR dwSSRC,
|
||
DWORD_PTR rtcpsock,
|
||
DWORD_PTR dwUser)
|
||
{
|
||
if (dwUser)
|
||
((CRTPSession *)dwUser)->RTCPNotify(rrcmEvent,dwSSRC,rtcpsock);
|
||
|
||
|
||
}
|
||
|
||
// Get the useful fields from the RTCP report and store them
|
||
// Only works for unicast sessions now (one sender, one receiver)
|
||
BOOL CRTPSession::GetRTCPReport()
|
||
{
|
||
#define MAX_RTCP_REPORT 2
|
||
RTCP_REPORT rtcpReport[MAX_RTCP_REPORT];
|
||
DWORD moreEntries = 0;
|
||
DWORD numEntry = 0;
|
||
DWORD i;
|
||
|
||
ZeroMemory(rtcpReport,sizeof(rtcpReport));
|
||
// Get latest RTCP report
|
||
// for all SSRCs in this session
|
||
if (RTCPReportRequest ( m_rtcpsock->GetSock(),
|
||
0, &numEntry,
|
||
&moreEntries, MAX_RTCP_REPORT,
|
||
rtcpReport,
|
||
0,NULL,0))
|
||
return FALSE;
|
||
|
||
for (i = 0; i < numEntry; i++)
|
||
{
|
||
if (rtcpReport[i].status & LOCAL_SSRC_RPT)
|
||
{
|
||
m_ss.sendStats.ssrc = rtcpReport[i].ssrc;
|
||
m_ss.sendStats.packetsSent = rtcpReport[i].dwSrcNumPcktRealTime;
|
||
m_ss.sendStats.bytesSent = rtcpReport[i].dwSrcNumByteRealTime;
|
||
} else {
|
||
m_rs.rcvStats.ssrc = rtcpReport[i].ssrc;
|
||
m_rs.rcvStats.packetsSent = rtcpReport[i].dwSrcNumPckt;
|
||
m_rs.rcvStats.bytesSent = rtcpReport[i].dwSrcNumByte;
|
||
m_rs.rcvStats.packetsLost = rtcpReport[i].SrcNumLost;
|
||
m_rs.rcvStats.jitter = rtcpReport[i].SrcJitter;
|
||
// Get the SR timestamp information
|
||
m_rs.ntpTime = ((NTP_TS)rtcpReport[i].dwSrcNtpMsw << 32) + rtcpReport[i].dwSrcNtpLsw;
|
||
m_rs.rtpTime = rtcpReport[i].dwSrcRtpTs;
|
||
|
||
// check if any feedback information
|
||
if (rtcpReport[i].status & FEEDBACK_FOR_LOCAL_SSRC_PRESENT)
|
||
{
|
||
DWORD prevPacketsLost = m_ss.sendStats.packetsLost;
|
||
|
||
m_ss.sendStats.packetsLost = rtcpReport[i].feedback.cumNumPcktLost;
|
||
/*
|
||
if (prevPacketsLost != m_ss.sendStats.packetsLost) {
|
||
DEBUGMSG(ZONE_DP,("RTCP: fraction Lost=%d/256 , totalLost =%d, StreamClock=%d\n",rtcpReport[i].feedback.fractionLost,m_ss.sendStats.packetsLost,m_clockRate));
|
||
}
|
||
*/
|
||
m_ss.sendStats.jitter = rtcpReport[i].feedback.dwInterJitter;
|
||
}
|
||
}
|
||
|
||
}
|
||
m_ss.sendStats.packetsDelivered = m_ss.sendStats.packetsSent - m_ss.sendStats.packetsLost;
|
||
|
||
return TRUE;
|
||
|
||
}
|
||
|
||
// CRTPPacket1 methods
|
||
|
||
HRESULT CRTPPacket1::Init(UINT uMaxPacketSize)
|
||
{
|
||
m_wsabuf.buf = new char [uMaxPacketSize];
|
||
if (!m_wsabuf.buf)
|
||
return E_OUTOFMEMORY;
|
||
m_wsabuf.len = uMaxPacketSize;
|
||
m_cbSize = uMaxPacketSize;
|
||
|
||
return S_OK;
|
||
}
|
||
|
||
CRTPPacket1::~CRTPPacket1()
|
||
{
|
||
if (m_wsabuf.buf)
|
||
delete [] m_wsabuf.buf;
|
||
}
|
||
|
||
|