/*
 *
 * @APPLE_LICENSE_HEADER_START@
 * 
 * Copyright (c) 1999-2003 Apple Computer, Inc.  All Rights Reserved.
 * 
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Apple Public Source License
 * Version 2.0 (the 'License'). You may not use this file except in
 * compliance with the License. Please obtain a copy of the License at
 * http://www.opensource.apple.com/apsl/ and read it before using this
 * file.
 * 
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 * Please see the License for the specific language governing rights and
 * limitations under the License.
 * 
 * @APPLE_LICENSE_HEADER_END@
 *
 */

#include "playlist_elements.h"
#include "playlist_utils.h"
#include "OS.h"
#include "SocketUtils.h"
#ifndef __Win32__
#include <unistd.h>
#endif

// ************************
//
// MediaStream
//
// ************************
#define DROP_RTCP_TEST 0
#define DROPCOUNT  1// drop count RTCPs 

MediaStream::MediaStream(void)
{
    memset( (char *)&fData, '\0', sizeof(fData));
    fSend = true;
    fData.fSenderReportReady = true;
}
MediaStream::~MediaStream()
{
    if (fData.fSoundDescriptionBuffer) delete[] fData.fSoundDescriptionBuffer;
}


UInt32 MediaStream::GetACName(char* ioCNameBuffer)
{       
    //clear out the whole buffer
    ::memset(ioCNameBuffer, 0, kMaxCNameLen);
    
    //cName identifier
    ioCNameBuffer[0] = 1;
    
    //Unique cname is constructed from the base name and the current time
    qtss_sprintf(&ioCNameBuffer[2], " %s%"_64BITARG_"d", "QTSS", OS::Milliseconds() / 1000);
    UInt32 cNameLen = ::strlen(&ioCNameBuffer[2]);
    //2nd byte of CName should be length
    ioCNameBuffer[1] = (UInt8) cNameLen ;//doesn't count indicator or length byte
    cNameLen += 2; // add the identifier and len bytes to the result len
    //pad length to a 4 byte boundary
    UInt32 padLength = cNameLen % 4;
    if (padLength > 0)
        cNameLen += 4 - padLength;
    
    return cNameLen;
}

void MediaStream::TestAndIncSoundDescriptor(RTpPacket *packetPtr)
{ // currently not executed
    short test = 0;
    do 
    {
        if (!fData.fIsSoundStream) break;
        if (!packetPtr->HasSoundDescription()) break;
        if (!fData.fSoundDescriptionBuffer) break;

        SoundDescription *packetSDPtr = NULL;
        SoundDescription *savedSDPtr = (SoundDescription *) fData.fSoundDescriptionBuffer;
        (void) packetPtr->GetSoundDescriptionRef(&packetSDPtr);
        long descSize = packetPtr->fSoundDescriptionLen;
        
        if (descSize == 0) break;
        if (descSize > eMaxSoundDescriptionSize) break;
        
        if (fData.fSavedSoundDescSize == descSize)
        {   test = ::memcmp(packetSDPtr, fData.fSoundDescriptionBuffer, descSize);
        } 
        else test = 1; // they are different sizes so it is a new sample description

        if (test != 0) // they are different
        {   ::memcpy(savedSDPtr, packetSDPtr, descSize); 
            fData.fSavedSoundDescSize = descSize;
            fData.fSavedDataRefIndex ++ ; // it is different than saved so change the index
        }
        packetSDPtr->dataRefIndex = htons(fData.fSavedDataRefIndex);
         
    } while (false);
}


void MediaStream::UpdatePacketInStream(RTpPacket *packetPtr)
{
    UInt32  curSSRC = 0;
    UInt32  curRTpTimeStamp = 0;
    UInt16  curRTpSequenceNumber = 0;
    UInt32  newRTpTimeStamp = 0;
    UInt16  newRTpSequenceNumber = 0;
    UInt32  newSSRC = 0;
    unsigned char curPayload;
    unsigned char newPayload;
    packetPtr->GetHeaderInfo(&curRTpTimeStamp, &curRTpSequenceNumber, &curSSRC, &curPayload);

    newSSRC = fData.fInitSSRC;
    MapToStream(curRTpTimeStamp, curRTpSequenceNumber, curPayload, &newRTpTimeStamp, &newRTpSequenceNumber, &newPayload);
    
    if (fData.fIsVideoStream) { LogStr("video "); }
    if (fData.fIsSoundStream) { LogStr("audio "); }
    
    packetPtr->SetHeaderInfo(newRTpTimeStamp, newRTpSequenceNumber,newSSRC,newPayload);

    //TestAndIncSoundDescriptor(packetPtr); // put in to track QuickTime format sound descriptors and flag change in sample types
}

void MediaStream::MapToStream(UInt32 curRTpTimeStamp, UInt16 curRTpSequenceNumber, unsigned char curPayload, UInt32 *outRTpTimeStampPtr, UInt16 *outRTpSequenceNumberPtr, unsigned char *outPayloadPtr)
{

    if (fData.fNewMovieStarted == true)     // this is the first packet in a new movie
    {   
        fData.fNewMovieStarted = false; 
    }  
            
    fData.fCurStreamRTpSequenceNumber++; //  the stream sequence number
    
    UInt64 curSequenceNumber = (UInt64) fData.fCurStreamRTpSequenceNumber + (UInt64) fData.fSeqRandomOffset;    
    UInt64 curTimeStamp = (UInt64) curRTpTimeStamp + (UInt64) fData.fMediaStartOffsetMediaScale + (UInt64) fData.fRTpRandomOffset;
        
    UInt32 outTime = (UInt32) ( (UInt64) curTimeStamp & (UInt64) 0xFFFFFFFF ); 
    UInt16 outSeq =  (UInt16) ( (UInt64) curSequenceNumber & (UInt64) 0xFFFF ); 
    unsigned char outPayload = curPayload;
    Assert (fData.fMovieMediaTypePtr != NULL);// should always be valid
    PayLoad *firstPayLoadPtr =  (fData.fMovieMediaTypePtr)->fPayLoadTypes.Begin(); // potential problem; assumes first payload per track is this payload
    if (firstPayLoadPtr)
    {   
        outPayload = (unsigned char) ( 0x7F & firstPayLoadPtr->payloadID);
        outPayload |= (curPayload & 0x80);// the movie payload marker
    }

//  qtss_printf("MediaStream::MapToStream outTime = %lu\n", outTime);
//  qtss_printf("MediaStream::MapToStream calculated time = %lu\n",(UInt32) curTimeInScale); 

    if (outRTpTimeStampPtr) *outRTpTimeStampPtr = outTime;
    if (outRTpSequenceNumberPtr) *outRTpSequenceNumberPtr = outSeq;
    if (outPayloadPtr)  *outPayloadPtr = outPayload;
}

void MediaStream::BuildStaticRTCpReport()
{
    char theTempCName[kMaxCNameLen];
    UInt32 cNameLen = GetACName(theTempCName);
    
    //write the SR & SDES headers
    UInt32* theSRWriter = (UInt32*)&fData.fSenderReportBuffer;
    *theSRWriter = htonl(0x80c80006);
    theSRWriter += 7;
    //SDES length is the length of the CName, plus 2 32bit words
    *theSRWriter = htonl(0x81ca0000 + (cNameLen >> 2) + 1);
    ::memcpy(&fData.fSenderReportBuffer[kSenderReportSizeInBytes], theTempCName, cNameLen);
    fData.fSenderReportSize = kSenderReportSizeInBytes + cNameLen;
}

void MediaStream::InitIfAudio()
{
    if (fData.fStreamMediaTypePtr)
    {
        SimpleString audioStr("audio");
        fData.fIsSoundStream = SimpleParser::Compare(&audioStr, &(fData.fStreamMediaTypePtr->fTheTypeStr), false );
        if (fData.fIsSoundStream)
        {               
            fData.fSoundDescriptionBuffer = new char[eMaxSoundDescriptionSize];
            ::memset(fData.fSoundDescriptionBuffer, 0, eMaxSoundDescriptionSize );
        }
        else
        {
            SimpleString videoStr("video");
            fData.fIsVideoStream = SimpleParser::Compare(&videoStr, &(fData.fStreamMediaTypePtr->fTheTypeStr), false );
        }
    }   

}


void MediaStream::StreamStart(SInt64 startTime)
{

    fData.fStreamStartTime = startTime;
    fData.fMovieEndTime = startTime;
    fData.fLastSenderReportTime = 0;

    //for RTCp SRs, we also need to store the play time in NTP
//  fData.fNTPPlayTime = OS::TimeMilli_To_1900Fixed64Secs(startTime);
    fData.fNTPPlayTime = PlayListUtils::TimeMilli_To_1900Fixed64Secs(startTime);
    fData.fCurStreamRTpSequenceNumber =  0;
    fData.fMovieStartOffset = 0;    
    
    fData.fNewStreamStarted = true;
    fData.fSeqRandomOffset = PlayListUtils::Random();
    fData.fRTpRandomOffset = PlayListUtils::Random();

    
//  fData.fSeqRandomOffset = -1000; // test roll over
//  fData.fRTpRandomOffset = -100000; // test roll over
    
    InitIfAudio();
    
    //Build a static RTCp sender report (this way, only the info that changes
    //on the fly will have to be written)
    BuildStaticRTCpReport();    
}

void MediaStream::MovieStart(SInt64 startTime)
{
    fData.fNewMovieStarted = true;
    fData.fMovieStartTime = startTime;
    
    if (fData.fMovieMediaTypePtr && fData.fRTPFilePtr)
    {   UInt32 trackID = fData.fMovieMediaTypePtr->fTrackID;
        fData.fMovieMediaTypePtr->fTimeScale = fData.fRTPFilePtr->GetTrackTimeScale(trackID);
        
        UInt64 lastMovieduration = (UInt64) ( (Float64)  (fData.fLastMovieDurationSecs * (Float64) PlayListUtils::eMilli) ) ; // add the length of the last movie
        fData.fMovieStartOffset += lastMovieduration; 
        
        if (fData.fNewStreamStarted)
        {   fData.fNewStreamStarted = false;
            fData.fMovieEndTime = startTime; // first movie in stream has 0 movieInterval time.
        }
                
        Float64 mediaOffSet =   (Float64) (SInt64)fData.fMovieStartOffset / (Float64)  PlayListUtils::eMilli; // convert to float seconds
        mediaOffSet = mediaOffSet  * (Float64)  fData.fMovieMediaTypePtr->fTimeScale; // mediaOffset in media time scale
        fData.fMediaStartOffsetMediaScale =  (UInt64) mediaOffSet; // convert float time units to UInt64

        fData.fLastMovieDurationSecs = fData.fRTPFilePtr->GetMovieDuration();
    }


}

void MediaStream::MovieEnd(SInt64 endTime)
{
    fData.fMovieEndTime = endTime;
    fData.fMovieMediaTypePtr = NULL;
    fData.fRTPFilePtr = NULL;
}

SInt16  MediaStream::Accounting(RTpPacket *packetPtr)
{
    SInt16 result = -1;
    
    fData.fBytesSent += packetPtr->fLength;
    fData.fPacketsSent ++;
        
    UInt32 lastTime = fData.fTimeStamp;     
    unsigned char payload;
    packetPtr->GetHeaderInfo(&fData.fTimeStamp, &fData.fLastSequenceNumber, &fData.fSsrc, &payload);
    
    fData.fLastTimeStampOffset = fData.fTimeStamp - lastTime;   
    
    result = 0;
    
    return result;
}

SInt16  MediaStream::Send(RTpPacket *packetPtr)
{

    SInt16 result = -1;
    
    do 
    {
        if (fData.fMovieMediaTypePtr == NULL) break;
        
        UpdatePacketInStream(packetPtr);
        
        result = Accounting(packetPtr);
        if (result) break;
        
        if (fSend)
        {   result = fData.fSocketPair->SendRTp(packetPtr->fThePacket, packetPtr->fLength);
        }
    }
    while (false);
    
    return result;
}

void MediaStream::ReceiveOnPorts()
{
    fData.fSocketPair->RecvRTp(fData.fPortRTpReadBuff.fReadBuffer, ReceiveBuffer::kReadBufferSize, &fData.fPortRTpReadBuff.fReceiveLen);
    fData.fSocketPair->RecvRTCp(fData.fPortRTCpReadBuff.fReadBuffer, ReceiveBuffer::kReadBufferSize, &fData.fPortRTCpReadBuff.fReceiveLen);
}

#if DROP_RTCP_TEST
static int numRTCPPackets = 0;
static int numStartDropPackets = 0;
#endif

int MediaStream::UpdateSenderReport(SInt64 theTime)
{

    if (NULL == fData.fMovieMediaTypePtr)
        return 0;
            
    int result = 0;
    
    SInt64 timeToSend = fData.fLastSenderReportTime + (kSenderReportIntervalInSecs * PlayListUtils::eMilli);

#if DROP_RTCP_TEST
    if (theTime > timeToSend )
    {   
        if (fData.fIsSoundStream)
        {   numRTCPPackets ++;
            if ( (numRTCPPackets <= numStartDropPackets + DROPCOUNT) )
            {   qtss_printf("skip sound RTCP #%d  time=%qd\n",numRTCPPackets, theTime / PlayListUtils::eMilli);
                fData.fLastSenderReportTime = theTime;
                return 0;
            }
            else
            {   qtss_printf("send sound RTCP #%d time=%qd\n",numRTCPPackets, theTime / PlayListUtils::eMilli);
                numStartDropPackets = numRTCPPackets;           
            }
        }
    }
#endif
    
    if (theTime > timeToSend)
    {
        fData.fLastSenderReportTime = theTime;
        UInt32* theReport = (UInt32*) fData.fSenderReportBuffer;
        
        theReport++;
        *theReport = htonl(fData.fSsrc);
        
        theReport++;
        SInt64* theNTPTimestampP = (SInt64*)theReport;      
        *theNTPTimestampP = OS::HostToNetworkSInt64(fData.fNTPPlayTime +
                                PlayListUtils::TimeMilli_To_Fixed64Secs(theTime - fData.fStreamStartTime));
                                
        theReport += 2;
        *theReport = htonl(fData.fTimeStamp);

        Float64 curTimeInScale =   (Float64) (SInt64) PlayListUtils::Milliseconds() / (Float64)  PlayListUtils::eMilli; // convert to float seconds
        curTimeInScale = curTimeInScale  * (Float64)  fData.fMovieMediaTypePtr->fTimeScale; // curTime in media time scale
        curTimeInScale +=(Float64) fData.fRTpRandomOffset;
        curTimeInScale = (UInt32) ( (UInt64) curTimeInScale & (UInt64) 0xFFFFFFFF ); 

        //qtss_printf("MediaStream::UpdateSenderReport RTCP timestamp = %lu\n",(UInt32) curTimeInScale);
        *theReport = htonl((UInt32) curTimeInScale);
        
        theReport++;        
        fData.fPacketCount = (UInt32) fData.fPacketsSent;
        *theReport = htonl(fData.fPacketCount);
    
        theReport++;
        fData.fByteCount = (UInt32)  fData.fBytesSent; 
        *theReport = htonl(fData.fByteCount);
        
        theReport += 2;
        *theReport = htonl(fData.fSsrc);
        
        LogStr("Sender Report\n");
        LogUInt("NTP ",(UInt32) ((*theNTPTimestampP) >> 32)," ");
        LogUInt(" ",(UInt32) ((*theNTPTimestampP) & 0xFFFFFFFF), "\n" );
        LogUInt("time stamp = ", fData.fTimeStamp, "\n");
        LogInt("SSRC = ", fData.fSsrc, "\n");
        LogUInt("Packets sent = ", fData.fPacketCount, "\n");
        LogUInt("Bytes sent = ", fData.fByteCount, "\n");
        LogBuffer();
        result = fData.fSocketPair->SendRTCp(fData.fSenderReportBuffer, fData.fSenderReportSize);

    }

    return result;
}

// ************************
//
// UDPSOCKETPAIR
//
// ************************


void UDPSocketPair::Close()
{
    if (fMultiCastJoined)
        this->LeaveMulticast();
        
#ifdef __Win32__
    if (fSocketRTp != 0) ::closesocket(fSocketRTp);
    if (fSocketRTCp != 0) ::closesocket(fSocketRTCp);
#else   
    if (fSocketRTp != 0) ::close(fSocketRTp);
    if (fSocketRTCp != 0) ::close(fSocketRTCp);
#endif
    fSocketRTp = 0;
    fSocketRTCp = 0;
}

SInt16 UDPSocketPair::Open()
{
    SInt16 result = 0;
    do
    {
        Close();
        
        fSocketRTp = ::socket(PF_INET, SOCK_DGRAM, 0);
        if (fSocketRTp == kInvalidSocket)
        {   result = kInvalidSocket; 
            break;
        }
        
        fSocketRTCp = ::socket(PF_INET, SOCK_DGRAM, 0);
        if (fSocketRTCp == kInvalidSocket)
        {   result = kInvalidSocket; 
            break;
        }
        
#ifdef __Win32__
        u_long one = 1;
        (void)::ioctlsocket(fSocketRTp, FIONBIO, &one);
        (void)::ioctlsocket(fSocketRTCp, FIONBIO, &one);
#else
        int flag; 
        int val;
        flag = ::fcntl(fSocketRTp, F_GETFL, 0);
        val = ::fcntl(fSocketRTp, F_SETFL, flag | O_NONBLOCK);
        if( val < 0 ) { result = -1; break; }


        flag = ::fcntl(fSocketRTCp, F_GETFL, 0);
        val = ::fcntl(fSocketRTCp, F_SETFL, flag | O_NONBLOCK);
        if( val < 0 ) { result = -1; break; }
#endif          
    } while (false);
    
    if (result != 0)
    {   if (fSocketRTp != 0) ::close(fSocketRTp);
        if (fSocketRTCp != 0) ::close(fSocketRTCp);
        fSocketRTp = 0;
        fSocketRTCp = 0;
    }
            
    return result;
}

void UDPSocketPair::InitPorts(UInt32 addr)
{
    ::memset(&fLocalAddrRTp, 0, sizeof(fLocalAddrRTp));
    fLocalAddrRTp.sin_family = PF_INET;
    fLocalAddrRTp.sin_port = htons(0);
    fLocalAddrRTp.sin_addr.s_addr = htonl(addr);

    ::memset(&fLocalAddrRTCp, 0, sizeof(fLocalAddrRTCp));
    fLocalAddrRTCp.sin_family = PF_INET;
    fLocalAddrRTCp.sin_port = htons(0);
    fLocalAddrRTCp.sin_addr.s_addr = htonl(addr);

    ::memset(&fDestAddrRTp, 0, sizeof(fDestAddrRTp));
    fDestAddrRTp.sin_family = PF_INET;
    fDestAddrRTp.sin_port = htons(0);
    fDestAddrRTp.sin_addr.s_addr = htonl(addr);

    ::memset(&fDestAddrRTCp, 0, sizeof(fDestAddrRTCp));
    fDestAddrRTCp.sin_family = PF_INET;
    fDestAddrRTCp.sin_port = htons(0);
    fDestAddrRTCp.sin_addr.s_addr = htonl(addr);
}

SInt16 UDPSocketPair::Bind(UInt32 addr)
{
    int err = -1;
     
    if ( (fSocketRTp == kInvalidSocket) || (fSocketRTCp == kInvalidSocket) )
        return -1;
    
    InitPorts(addr);
        
    UInt32 PortRTp = eSourcePortStart;
    UInt32 PortRTCp = PortRTp + 1; // keep them together for clarity

    for (int count = eSourcePortStart; count < eSourcePortRange; count ++)
    {
        PortRTp = count;
        Assert( (PortRTp & 1) == 0); // must be even
        count += 1;
        PortRTCp = count;
        Assert( (PortRTCp & 1) == 1);// must be odd and one more than rtp port
        
        fLocalAddrRTp.sin_port = htons( (UInt16) PortRTp);
        fLocalAddrRTCp.sin_port = htons( (UInt16) PortRTCp);
            
        //qtss_printf("Attempting to bind to rtp port %d \n",PortRTp);
        
        err = ::bind(fSocketRTp, (sockaddr *)&fLocalAddrRTp, sizeof(fLocalAddrRTp));
        if (err != 0)
        {   
            //qtss_printf("UDPSocketPair::Bind Error binding to rtp port %d \n",PortRTp);
            InitPorts(addr);
            continue;
        }

        err = ::bind(fSocketRTCp, (sockaddr *)&fLocalAddrRTCp, sizeof(fLocalAddrRTCp)); 
        if (err != 0)
        {
            //qtss_printf("UDPSocketPair::Bind Error binding to rtcp port %d \n",PortRTp);
            Close();
            Open();
            InitPorts(addr);
            continue;
        }  
        
        if (err == 0) 
        {   
            //qtss_printf("Bound to rtp port = %d, rtcp port = %d \n",PortRTp, PortRTCp);
            fState |= kBound;
            break;
        }
    }
    
    return err;
}


SInt16 UDPSocketPair::SendTo(int socket, sockaddr *destAddrPtr, char* inBuffer, UInt32 inLength )
{
    SInt16 result = -1;
    do
    {
        if (inBuffer == NULL) break;        
        if (destAddrPtr == NULL) break;         
        if (socket == kInvalidSocket) break;
        
        //qtss_printf("Sending data to %d. Addr = %d inLength = %d\n", ntohs(theAddr->sin_port), ntohl(theAddr->sin_addr.s_addr), inLength);
        ::sendto(socket, inBuffer, inLength, 0, destAddrPtr, sizeof(sockaddr));
        
        result = 0;
    } while (false);
        
    //if (result != 0) qtss_printf("UDP SENDTO ERROR!\n");
    return result;
}


SInt16 UDPSocketPair::SendRTp(char* inBuffer, UInt32 inLength)
{   
    if (fBroadcasterSession != NULL)
    {   OSMutexLocker locker(fBroadcasterSession->GetMutex());
        return (SInt16) fBroadcasterSession->SendPacket(inBuffer,inLength,fChannel);
    }
    else
        return SendTo(fSocketRTp, (sockaddr*)&fDestAddrRTp, inBuffer, inLength );
}

SInt16 UDPSocketPair::SendRTCp(char* inBuffer, UInt32 inLength)
{   
    if (fBroadcasterSession != NULL)
    {   OSMutexLocker locker(fBroadcasterSession->GetMutex());
        return (SInt16) fBroadcasterSession->SendPacket(inBuffer,inLength,fChannel+1);
    }
    else
        return SendTo(fSocketRTCp, (sockaddr*)&fDestAddrRTCp, inBuffer, inLength );
}

SInt16  UDPSocketPair::SetDestination (char *destAddress,UInt16 destPortRTp, UInt16 destPortRTCp)
{
    SInt16 result = -1;

    if (destAddress != NULL)
    {   UInt32 netAddress = inet_addr(destAddress);
    
        fDestAddrRTp = fLocalAddrRTp; 
        fDestAddrRTp.sin_port = htons(destPortRTp); 
        fDestAddrRTp.sin_addr.s_addr = netAddress;
        
        fDestAddrRTCp = fLocalAddrRTCp;
        fDestAddrRTCp.sin_port = htons(destPortRTCp);       
        fDestAddrRTCp.sin_addr.s_addr =  netAddress;
        
        fIsMultiCast = SocketUtils::IsMulticastIPAddr(ntohl(netAddress));

        result = 0;
    }
    return result;
}

SInt16 UDPSocketPair::SetMulticastInterface()
{
    // set the outgoing interface for multicast datagrams on this socket
    in_addr theLocalAddr;
    ::memset(&theLocalAddr, 0, sizeof(theLocalAddr));
    
    theLocalAddr.s_addr = fLocalAddrRTp.sin_addr.s_addr;
    int err = setsockopt(fSocketRTp, IPPROTO_IP, IP_MULTICAST_IF, (char*)&theLocalAddr, sizeof(theLocalAddr));

    return err; 
}

SInt16 UDPSocketPair::JoinMulticast()
{
    int err = 0;

    
    UInt32 localAddr = fLocalAddrRTp.sin_addr.s_addr; // Already in network byte order

#if __solaris__
    if( localAddr == htonl(INADDR_ANY) )
         localAddr = htonl(SocketUtils::GetIPAddr(0));
#endif

    struct ip_mreq  theMulti;
    ::memset(&theMulti, 0, sizeof(theMulti));

    theMulti.imr_multiaddr.s_addr = fDestAddrRTp.sin_addr.s_addr;
    theMulti.imr_interface.s_addr = localAddr;
    err = setsockopt(fSocketRTp, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*)&theMulti, sizeof(theMulti));
    (void) setsockopt(fSocketRTCp, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*)&theMulti, sizeof(theMulti));

    if (err == 0)
        fMultiCastJoined = true;
        
    return err;
}


SInt16 UDPSocketPair::SetTTL(SInt16 timeToLive)
{
    // set the ttl
    int nOptVal = (int)timeToLive;
    int err = 0;
    
    err = setsockopt(fSocketRTp, IPPROTO_IP, IP_MULTICAST_TTL, (char*)&nOptVal, sizeof(nOptVal));
    if (err != 0) return err;

    err = setsockopt(fSocketRTCp, IPPROTO_IP, IP_MULTICAST_TTL, (char*)&nOptVal, sizeof(nOptVal));
    return err; 
}

SInt16 UDPSocketPair::LeaveMulticast()
{ 
   UInt32 localAddr = fLocalAddrRTp.sin_addr.s_addr; // Already in network byte order

#if __solaris__
    if( localAddr == htonl(INADDR_ANY) )
         localAddr = htonl(SocketUtils::GetIPAddr(0));
#endif

    struct ip_mreq  theMulti;
    ::memset(&theMulti, 0, sizeof(theMulti));
    
    theMulti.imr_multiaddr.s_addr = fDestAddrRTp.sin_addr.s_addr;
    theMulti.imr_interface.s_addr = localAddr;
    
    int err = setsockopt(fSocketRTp, IPPROTO_IP, IP_DROP_MEMBERSHIP, (char*)&theMulti, sizeof(theMulti));
    (void) setsockopt(fSocketRTCp, IPPROTO_IP, IP_DROP_MEMBERSHIP, (char*)&theMulti, sizeof(theMulti));
    return err;
}

SInt16 UDPSocketPair::RecvFrom(sockaddr *RecvRTpddrPtr, int socket, char* ioBuffer, UInt32 inBufLen, UInt32* outRecvLen)
{
    SInt16  result = -1;
    
    do
    {   
        if (ioBuffer == NULL) break;
        if (RecvRTpddrPtr == NULL) break;
        if (socket == kInvalidSocket) break;
        
        sockaddr_in theAddr;
#if __Win32__ || __osf__ || __sgi__  || __hpux__ 
        int addrLen = sizeof(theAddr);
#else
        socklen_t addrLen = sizeof(theAddr);
#endif
        
        SInt32 theRecvLen = ::recvfrom(socket, ioBuffer, inBufLen, 0, (sockaddr*)&theAddr, &addrLen);       
        if (theRecvLen == -1) break;
        
        if (outRecvLen) *outRecvLen = (UInt32)theRecvLen;
    } while (false);
    return result;      
}

SInt16 UDPSocketPair::RecvRTp(char* ioBuffer, UInt32 inBufLen, UInt32* outRecvLen)
{
    return RecvFrom( (sockaddr *)&fDestAddrRTp, fSocketRTp, ioBuffer, inBufLen, outRecvLen);
}

SInt16 UDPSocketPair::RecvRTCp(char* ioBuffer, UInt32 inBufLen, UInt32* outRecvLen)
{
    return  RecvFrom( (sockaddr *)&fDestAddrRTCp, fSocketRTCp, ioBuffer, inBufLen, outRecvLen);
}

SInt16 UDPSocketPair::SetMultiCastOptions(SInt16 ttl)
{
    SInt16 err = 0;

    if (this->fIsMultiCast) do// set by SetDestination
    {               
        err = this->SetTTL(ttl);
        WarnV(err == 0 , "failed to set ttl");
        if (err != 0) break;

        err = this->SetMulticastInterface();
        WarnV(err == 0 , "failed to set multicast socket option");
        if (err != 0) break;
        
        err = this->JoinMulticast();
        WarnV(err == 0 , "failed to join multicast");
        if (err != 0) break;
        
    } while (false);
        
    return err;
}

SInt16 UDPSocketPair::OpenAndBind( UInt16 rtpPort,UInt16 rtcpPort,char *destAddress) 
{
    SInt16 err = -1;
        
    do
    {
        err = this->Open();
        if (err != 0) break;
        
        err = this->Bind(INADDR_ANY);
        if (err != 0) break;
        
        err = this->SetDestination (destAddress, rtpPort, rtcpPort);    
        if (err != 0) break;
        
    } while (false);
    
    if (err)
    {   
        this->Close();
    }
    
    return err;
};

// ************************
//
// RTpPacket
//
// ************************

SInt16 RTpPacket::GetSoundDescriptionRef(SoundDescription **soundDescriptionPtr)
{
    SInt16 result = -1;
    
    if (fThePacket && soundDescriptionPtr)
    {
        long minSoundLength = sizeof(SoundHeader) + sizeof(SoundDescription) + kRTpHeaderSize;
        if ( fLength >= minSoundLength  )
        {
            char *offsetPtr = fThePacket + kRTpHeaderSize + sizeof(SoundHeader);
            *soundDescriptionPtr = (SoundDescription *) offsetPtr;
            long descSize = ntohl( (**soundDescriptionPtr).descSize);
            fSoundDescriptionLen = descSize;
            result = 0;
        }
    }
    
    return result;
}

bool RTpPacket::HasSoundDescription() 
{
    bool result = false;
    
    if (fThePacket)
    {
    
//      WritePacketToLog(fThePacket, fLength);
        long minSoundLength = sizeof(SoundHeader) + sizeof(SoundDescription) + kRTpHeaderSize;
        if (fLength >= minSoundLength ) 
        {
            char *offsetPtr = fThePacket + kRTpHeaderSize;
            SoundHeader* testHeaderPtr = (SoundHeader*) offsetPtr;
            do
            {
                if (testHeaderPtr->bytes[0] != 0x17) break;
                if (testHeaderPtr->sndtype[0] != 's') break;
                if (testHeaderPtr->sndtype[1] != 'o') break;
                if (testHeaderPtr->sndtype[2] != 'u') break;
                if (testHeaderPtr->sndtype[3] != 'n') break;

                fHasSoundDescription = result = true;
                
            } while (false);
        }
        
        if (result) 
        {   
            LogStr("has sound description soun\n");
        }
        PrintLogBuffer(true);
    }
    
    return result;
}

SInt16 RTpPacket::GetHeaderInfo(UInt32 *timeStampPtr, UInt16 *sequencePtr,UInt32 *SSRCPtr, unsigned char*payloadTypeAndMarkPtr)
{
    SInt16 result = -1;
    
    
    unsigned char *header8Ptr = (unsigned char *) fThePacket;
    UInt16* header16Ptr = (UInt16*)fThePacket;
    UInt32* header32Ptr = (UInt32*)fThePacket;  
    
    if (fThePacket && timeStampPtr && sequencePtr && SSRCPtr && payloadTypeAndMarkPtr)
    {
        *payloadTypeAndMarkPtr = header8Ptr[cPayloadType];
        *sequencePtr = ntohs(header16Ptr[cSequenceNumber]);
        *timeStampPtr = ntohl(header32Ptr[cTimeStamp]);
        *SSRCPtr = ntohl(header32Ptr[cSSRC]);
        result = 0; 
    }
    
    return result;
}



SInt16 RTpPacket::SetHeaderInfo(UInt32 timeStamp, UInt16 sequence, UInt32 SSRC, unsigned char payloadTypeAndMark)
{
    SInt16 result = -1;
    
    unsigned char *header8Ptr = (unsigned char *) fThePacket;
    UInt16* header16Ptr = (UInt16*)fThePacket;
    UInt32* header32Ptr = (UInt32*)fThePacket;  
    
    
    if (fThePacket)
    {
        LogInt("sequence = ", sequence, " ");
        LogUInt("time = ", timeStamp, " ");
//      LogUInt("old payload = ",( UInt32 ) (header8Ptr[cPayloadType] & 0x7F)," ");
        LogUInt("payload = ",( UInt32 ) (payloadTypeAndMark & 0x7F) ," ");
//      LogUInt("old marker = ", ( UInt32 ) (header8Ptr[cPayloadType] & 0x80) ," ");
//      LogUInt("marker = ", ( UInt32 ) (payloadTypeAndMark & 0x80) ," ");
        LogUInt("ssrc = ", SSRC, "\n");

        header8Ptr[cPayloadType] = payloadTypeAndMark;
        header16Ptr[cSequenceNumber] = htons(sequence);
        header32Ptr[cTimeStamp] = htonl(timeStamp);     
        header32Ptr[cSSRC] = htonl(SSRC);       
        result = 0; 

        LogBuffer();
    }

    
    return result;
}


