小计总结001

在这里插入图片描述

mdeMrCommDef.h


#ifndef __X_COMM_TYPES_H__
#define __X_COMM_TYPES_H__

#include <getopt.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <unistd.h>
#include <inttypes.h>

#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include "XpocSipDefine.h"
#include "XpocModStruct.h"
#include "XpocProcTimer.h"
#include "eXosip2/eXosip.h"
#include "XpocCommStruct.h"
#include "XpocCommFunc.h"

typedef struct
{
    UINT    ulUCallCtxId;
}StreamInfoS;

typedef struct
{
    UINT                 ulCurrMsgId;
    UINT                 ulCBId;
    UINT                 ulCallCtxId;
    UCHAR                StreamNum;
    StreamInfoS          StreamInfoArray[MEDIA_TYPE_MAX];
    //eXosip_event_t       *ptSpSipHandle;   /* sip addr */
    UINT                 ulCceLegId;
}MdeCtrlBlockS;

typedef struct
{
    UCHAR           rtpWorkerThreadNum; /* 2 */
    USHORT          udpRtpPort;         /* 29010 */
    USHORT          udpRtcpPort;        /* 29010 */
    USHORT          tcpPort;            /* 29000 */

    // added by shm, 解决外网端口映射问题
    USHORT          udpRmtRtpPort;         /* 29010 */
    USHORT          udpRmtRtcpPort;        /* 29010 */
    USHORT          tcpRmtPort;            /* 29000 */

    /* 约定 : 本地保存的IP为主机字节序 */
    UINT            ulLocalIp; 
    UINT            ulAceRmtIp;
} MdeCfgInfoS;

#define MAX_RTP_SOCKET_NUM 1000
typedef struct
{
    MediaFdS        udpRtpFd;
    MediaFdS        udpRtcpFd;
    SINT            udpEpollFd; /* udp epoll */
    ThreadInfo_t    *ptSendThread;
}UdpAddrInfoS;

typedef struct
{
    MediaFdS        tcpListenFd;
    USHORT          tcpFdNum;
    MediaFdS        tcpRtpFdList[MAX_RTP_SOCKET_NUM];
    SINT            tcpEpollFd; /* tcp epoll */
    ThreadInfo_t    *ptSendThread;
}TcpAddrInfoS;

typedef struct
{
    MdeCfgInfoS     stCfgInfo;  /* static config: local IP+PORT, remote IP+PORT, dataArea max num, etc */

    UdpAddrInfoS    stUdpAddrInfo;
    TcpAddrInfoS    stTcpAddrInfo;
} MdeGlbData_S;

// added by shm, 记录rtcp保活消息的信息
typedef struct
{
    UINT nSsrc;
    UINT ulIp;
    UINT nPort;
    UINT uTimestamp;    // 加入MediaKeepaliveInfoList时的时间戳
    UINT uUseFlag;
}MediaKeepaliveInfo;

#define MAX_SSRC_BUFFER 5000
typedef struct
{
    MediaKeepaliveInfo stSsrc[MAX_SSRC_BUFFER];
    int nUserNum;
}MediaKeepaliveInfoList;

#define CLEAR_UDP_KEEPALIVE_TM_INTERVAL  (5)  /* 清理rtcp保活时间间隔, 暂时设定每五分钟清理一次 */
#define UDP_KEEPALIVE_INFO_TIMEOUT     (120) // 最少保留2分钟
typedef enum 
{
    TIMER_EVENT_NULL          = 0,
    TIMER_CLEAR_UDP_KEEPALIVE = 1
}MdeTimerEvent;

#endif  /* __X_COMM_TYPES_H__ */






  1. mdeMrCommFunc.c


#include "mdeMrCommFunc.h"
#include "mdeMrResourceFunc.h"
#include <netinet/tcp.h>  /* for TCP_XXX defines */

extern UINT g_UdpRcvCount;
extern UINT g_UdpSndCount;
extern UINT g_TcpRcvCount;
extern UINT g_TcpSndCount;

#if 0
#endif

extern pthread_rwlock_t  g_lockRtcpKeepalive;
extern MediaKeepaliveInfoList g_RtcpKeepaliveList;

extern pthread_rwlock_t  g_lockRtpKeepalive;
extern MediaKeepaliveInfoList g_RtpKeepaliveList;

UINT mdePrintMediaInfo(T_ModCtxS *ptModCtx, UINT ulCallCtxId)
{
    XPOC_TRACEFUNC_IN;
    X_NULL_POINTER_CHK_ONE_PARA(ptModCtx);

    UINT ulReturn = XPOC_ERR_SUCCESS;

    T_MdeCallCtxTable stCC;
    T_MdeUCallCtxTable stUCC;

    ulReturn = XMdeCallCtxOpr(XDB_OPR_QRY, NULL, 0, &ulCallCtxId, &stCC);
    if(XPOC_ERR_SUCCESS != ulReturn)
    {
        return XPOC_ERR_FAIL;
    }
    x_DebugLog("[KEY]CCInfo:CCId[%d],servCallId[%d],UCCNum[%d],CBNum[%d].\n", 
              stCC.ulRecordIdx, stCC.ulServCCId, stCC.stUCCIdList.UcNum, stCC.stCBIdList.ulCBNum);

    UINT i = 0;
    for(i=0; i<MAX_UC_NUM && i<stCC.stUCCIdList.UcNum; i++)
    {
        if(stCC.stUCCIdList.stUCCId[i].IsUsedFg && stCC.stUCCIdList.stUCCId[i].ulUCallCtxId > 0)
        {
            ulReturn = XMdeUCallCtxOpr(XDB_OPR_QRY, NULL, 0, 0, 0, NULL, &stCC.stUCCIdList.stUCCId[i].ulUCallCtxId, &stUCC);
            if(XPOC_ERR_SUCCESS != ulReturn)
            {
                x_DebugLog("[KEY]UCCInfo:CCId[%d],UCCId[%d],servId[%s],ssrc[0x%x(%u)].\n", 
                        stUCC.ulCallCtxId, stUCC.ulRecordIdx, stUCC.aucServId, stUCC.ulSsrc, stUCC.ulSsrc);
            }
        }
    }

    MdeCtrlBlockS *ptCBTmp = NULL;
    for(i=0; i<MAX_CB_NUM && i<stCC.stCBIdList.ulCBNum; i++)
    {
        if(1)//ptCC->stCBIdList..IsUsedFg)
        {
            
            ptCBTmp = XPOCLocateCtrlBlock(stCC.stCBIdList.ulCBId[i]);
            if(ptCBTmp)
            {
                x_DebugLog("[KEY]CBInfo: CCId[%d],CBId[%d], StreamNum[%d],UCC[%d; %d].\n", 
                        ptCBTmp->ulCallCtxId, ptCBTmp->ulCBId, ptCBTmp->StreamNum, 
                        ptCBTmp->StreamInfoArray[0].ulUCallCtxId, ptCBTmp->StreamInfoArray[1].ulUCallCtxId);
            }
        }
    }

    XPOC_TRACEFUNC_OUT;
    return XPOC_ERR_SUCCESS;
}

UINT mdeSaveTransTargetCCId2SrcCC(UINT ulSrcMediaCCId, servTypeE enCurServType, UINT ulTransMediaCCId)
{
    XPOC_TRACEFUNC_IN;

    T_MdeCallCtxTable stSrcMediaCC;
    memset(&stSrcMediaCC, 0, sizeof(T_MdeCallCtxTable));
    stSrcMediaCC.ulRecordIdx = ulSrcMediaCCId;
    UINT ulReturn = XMdeCallCtxOpr(XDB_OPR_QRY, NULL, 0, &stSrcMediaCC.ulRecordIdx, &stSrcMediaCC);
    if(XPOC_ERR_SUCCESS != ulReturn)
    {
        return ulReturn;
    }

    UCHAR i = 0;
    if(Serv_VideoTransPri == enCurServType || Serv_VideoTransGrp == enCurServType)
    {
        for(i=0; i<CALL_MAX_NUM; i++)
        {
            if(0 == stSrcMediaCC.ulTransToCCId[i])
            {
                stSrcMediaCC.ulTransToCCId[i] = ulTransMediaCCId;
                break;
            }
        }
    }
    
    (void)XMdeCallCtxOpr(XDB_OPR_MOD, NULL, 0, &stSrcMediaCC.ulRecordIdx, &stSrcMediaCC);

    x_InfoLog("srcMedia CC[%d] will trans media pkt to CC[%d:%d].", 
            stSrcMediaCC.ulRecordIdx, i, stSrcMediaCC.ulTransToCCId[i]);

    XPOC_TRACEFUNC_OUT;
    return XPOC_ERR_SUCCESS;
}

UINT mdeSaveRcrdCCId2SrcUCC(UINT ulSrcMediaCCId, UINT ulRcrdCCId, servTypeE servType, char *ptServId)
{
    XPOC_TRACEFUNC_IN;
    X_NULL_POINTER_CHK_ONE_PARA(ptServId);

    UINT ulReturn = XPOC_ERR_SUCCESS;

    T_MdeCallCtxTable stSrcMediaCC;
    memset(&stSrcMediaCC, 0, sizeof(T_MdeCallCtxTable));
    stSrcMediaCC.ulRecordIdx = ulSrcMediaCCId;
    ulReturn = XMdeCallCtxOpr(XDB_OPR_QRY, NULL, 0, &stSrcMediaCC.ulRecordIdx, &stSrcMediaCC);
    if(XPOC_ERR_SUCCESS != ulReturn)
    {
        return ulReturn;
    }
    stSrcMediaCC.ulRcrdCCId = ulRcrdCCId;
    ulReturn = XMdeCallCtxOpr(XDB_OPR_MOD, NULL, 0, &stSrcMediaCC.ulRecordIdx, &stSrcMediaCC);

    T_MdeUCallCtxTable stUCC;
    UINT i = 0;
    for(i=0; i<MAX_UC_NUM && i<stSrcMediaCC.stUCCIdList.UcNum; i++)
    {
        if(stSrcMediaCC.stUCCIdList.stUCCId[i].IsUsedFg && stSrcMediaCC.stUCCIdList.stUCCId[i].ulUCallCtxId > 0)
        {
            memset(&stUCC, 0, sizeof(T_MdeUCallCtxTable));
            stUCC.ulRecordIdx = stSrcMediaCC.stUCCIdList.stUCCId[i].ulUCallCtxId;
            ulReturn = XMdeUCallCtxOpr(XDB_OPR_QRY, NULL, 0, 0, 0, NULL, &stUCC.ulRecordIdx, &stUCC);
            if(XPOC_ERR_SUCCESS != ulReturn)
            {
                continue;
            }
            
            if((Serv_AudioRcrdPri == servType && (0==strcmp(stUCC.aucServId, ptServId)))
                || (Serv_VideoRcrdPri == servType && (0==strcmp(stUCC.aucServId, ptServId)))
                || (Serv_AudioRcrdGrp == servType)
                || (Serv_VideoRcrdGrp == servType))
            {
                stUCC.ulRcrdCCId = ulRcrdCCId;
                ulReturn = XMdeUCallCtxOpr(XDB_OPR_MOD, NULL, 0, 0, 0, NULL, &stUCC.ulRecordIdx, &stUCC);

                x_InfoLog("CC[%d] will record media pkt of this Call to UCC[%d]servId[%s].",
                            stSrcMediaCC.ulRecordIdx, stUCC.ulRecordIdx, stUCC.aucServId);
                //break;
            }
        }
    }

    XPOC_TRACEFUNC_OUT;
    return XPOC_ERR_SUCCESS;
}

UINT mdeSaveMediaDescription2CC(MediaDescriptionS *ptAudio, MediaDescriptionS *ptVideo, T_MdeCallCtxTable *ptCC)
{
    XPOC_TRACEFUNC_IN;
    X_NULL_POINTER_CHK_THREE_PARA(ptAudio, ptVideo, ptCC);

    if(INVALID_VALUE_UCHAR != ptAudio->bCodecNum && 0 != ptAudio->bCodecNum)
    {
        ptCC->MediaStreams[ptCC->MediaStreamNum].IsValidFg = TRUE;
        ptCC->MediaStreams[ptCC->MediaStreamNum].bCodecNum = 1; //now use the first media type from audio media line
        ptCC->MediaStreams[ptCC->MediaStreamNum].mediaType = MEDIA_AUDIO;
        memcpy(&ptCC->MediaStreams[ptCC->MediaStreamNum].mediaCodec[0], &ptAudio->stSdpCodec[0], sizeof(SdpCodecS));
        ptCC->MediaStreamNum++;
    }
    if(INVALID_VALUE_UCHAR != ptVideo->bCodecNum && 0 != ptVideo->bCodecNum)
    {
        ptCC->MediaStreams[ptCC->MediaStreamNum].IsValidFg = TRUE;
        ptCC->MediaStreams[ptCC->MediaStreamNum].bCodecNum = 1;
        ptCC->MediaStreams[ptCC->MediaStreamNum].mediaType = MEDIA_VIDEO;
        memcpy(&ptCC->MediaStreams[ptCC->MediaStreamNum].mediaCodec[0], &ptVideo->stSdpCodec[0], sizeof(SdpCodecS));
        ptCC->MediaStreamNum ++;
    }

    XPOC_TRACEFUNC_OUT;
    return XPOC_ERR_SUCCESS;
}


UINT mdeSaveMediaDescription2UCC(MediaDescriptionS *ptAudio, MediaDescriptionS *ptVideo, T_MdeUCallCtxTable *ptUCC)
{
    XPOC_TRACEFUNC_IN;
    X_NULL_POINTER_CHK_ONE_PARA(ptUCC);

    if(MEDIA_AUDIO == ptUCC->enMediaType && ptAudio)
    {
        ptUCC->ulSsrc = ptAudio->ulSsrc;
        ptUCC->enMediaType = MEDIA_AUDIO;
        memcpy(&ptUCC->stMediaInfo, ptAudio, sizeof(MediaDescriptionS));
    }
    else if(MEDIA_VIDEO == ptUCC->enMediaType && ptVideo)
    {
        ptUCC->ulSsrc = ptVideo->ulSsrc;
        ptUCC->enMediaType = MEDIA_VIDEO;
        memcpy(&ptUCC->stMediaInfo, ptVideo, sizeof(MediaDescriptionS));
    }

    XPOC_TRACEFUNC_OUT;
    return XPOC_ERR_SUCCESS;
}


#if 0
#endif


/*****************  函数说明  **********************
* 函数名    : Rm_IdxManageInitCtx
* 功能说明  : 初始化队列索引管理上下文
*************************************************/
UINT Rm_IdxManageInitCtx(
    OUT MrIdxMangeCtx_t *pManageCtx, 
    OUT NodeIndex_S * patMrNodeIndex, 
    IN  UINT u32NodeIndexCount)
{
    UINT iRet = XPOC_ERR_SUCCESS;

    if(NULL == pManageCtx || NULL == patMrNodeIndex || 0 == u32NodeIndexCount)
    {
        iRet = XPOC_ERR_FAIL;
        x_ErrorLog("Invalid param: pManageCtx[%p], patMrNodeIndex[%p], u32NodeIndexCount[%u].",
            pManageCtx, patMrNodeIndex, u32NodeIndexCount);
        return iRet;
    }

    do
    {
        /* Initialize free list of manage ctx.*/
        volatile MrNodeIndexList_t *pstFreeList = &pManageCtx->stFreeList;
        pstFreeList->s32Count = 0;
        pstFreeList->u32LockStatus = RM_LIST_UNLOCKED;
        /*------------------------------------------------------*/
        pstFreeList->EmptyNode.u32PktNodeIndex = 0xFFFFFFFF;
        pstFreeList->EmptyNode.pNext = NULL;
        pstFreeList->pHead = &pstFreeList->EmptyNode;
        pstFreeList->pTail = &pstFreeList->EmptyNode;

        /* Insert all free node. */
        UINT u32Index = 0;
        for (u32Index = 0; u32Index < u32NodeIndexCount; u32Index++)
        {
            patMrNodeIndex[u32Index].u32PktNodeIndex = u32Index;
            patMrNodeIndex[u32Index].pNext = NULL;
            /* Insert to freeList tail. */
            pstFreeList->pTail->pNext = (PNodeIndex_S)&patMrNodeIndex[u32Index];
            pstFreeList->pTail = (NodeIndex_S*)pstFreeList->pTail->pNext;
            pstFreeList->s32Count += 1;
        }
        /*------------------------------------------------------*/

        /* Initialize free list of manage ctx.*/
        volatile MrNodeIndexList_t* pSendingList = &pManageCtx->stSendList;
        pSendingList->s32Count = 0;
        pSendingList->u32LockStatus = RM_LIST_UNLOCKED;
        /*------------------------------------------------------*/
        /* The EMPTY node */
        pSendingList->EmptyNode.u32PktNodeIndex = 0xFFFFFFFF;
        pSendingList->EmptyNode.pNext = NULL;
        pSendingList->pHead = &pSendingList->EmptyNode;
        pSendingList->pTail = &pSendingList->EmptyNode;
        /*------------------------------------------------------*/
    } while (0);

    return iRet;
}

/*************************************************
*****************  函数说明 **********************
* 函数名    : Rm_IdxManageGetFromHead
* 功能说明  : 从头部开始获取结点,获取后队列跳过结点(注,FIFO,从队尾插入)
*************************************************/
UINT Rm_IdxManageGetFromHead
(
    INOUT volatile MrNodeIndexList_t *pIndexList, 
    OUT NodeIndex_S** ppNodeIndex
)
{
    UINT iRet = XPOC_ERR_SUCCESS;

    if(NULL == pIndexList || NULL == ppNodeIndex)
    {
        iRet = XPOC_ERR_FAIL;
        x_ErrorLog("Invalid param: pIndexList[%p], ppNodeIndex[%p].",
            pIndexList, ppNodeIndex);
        return iRet;
    }

    do
    {
        *ppNodeIndex = NULL;
        
        /* step 1: 获取"队列操作权" */
        while (RM_LIST_UNLOCKED != __sync_val_compare_and_swap(&pIndexList->u32LockStatus, RM_LIST_UNLOCKED, RM_LIST_LOCKED))
        {
            /* FIXME: 不断循环操作还是主动放弃当前 CPU 时间片? */
            /* sched_yield(); */
        }

        /* step 2: 获取节点 */
        do
        {
            //当前队列为空
            if(pIndexList->pHead == pIndexList->pTail)
            {
                iRet = XPOC_ERR_FAIL;
                break;
            }
            
            NodeIndex_S *pSnap = (NodeIndex_S*)pIndexList->pHead->pNext;
            
            //队列最后一个结点
            if(pSnap == pIndexList->pTail)
            {
                pIndexList->pTail = pIndexList->pHead;
            }
            pIndexList->pHead->pNext = pSnap->pNext;
            
            pSnap->pNext = NULL;
            *ppNodeIndex = pSnap;
            pIndexList->s32Count -= 1;
        } while(0);
        
        /* step 3: 释放"队列操作权" */
        while (RM_LIST_LOCKED != __sync_val_compare_and_swap(&pIndexList->u32LockStatus, RM_LIST_LOCKED, RM_LIST_UNLOCKED))
        {
            /* FIXME: 这里一次性就能操作成功 */
        }
    } while(0);

    return iRet;
}

/*************************************************
*****************  函数说明  **********************
* 函数名    : Rm_IdxManageInsertToTail
* 功能说明  : 插入结点到尾部(注,FIFO,从头部开始获取结点)
*************************************************/
UINT Rm_IdxManageInsertToTail(volatile MrNodeIndexList_t *pIndexList, NodeIndex_S* pNodeIndex)
{
    UINT iRet = XPOC_ERR_SUCCESS;
    X_NULL_POINTER_CHK_TWO_PARA(pIndexList, pNodeIndex);
    
    do
    {
        pNodeIndex->pNext = NULL;

        /* step 1: 获取"队列操作权" */
        while (RM_LIST_UNLOCKED != __sync_val_compare_and_swap(&pIndexList->u32LockStatus, RM_LIST_UNLOCKED, RM_LIST_LOCKED))
        {
            /* FIXME: 不断循环操作还是主动放弃当前 CPU 时间片? */
            /* sched_yield(); */
        }

        /* step 2: 插入节点 */
        do
        {
            pIndexList->pTail->pNext = (PNodeIndex_S)pNodeIndex;
            pIndexList->pTail = pNodeIndex;
            pIndexList->s32Count += 1;
        
        } while(0);

        /* step 3: 释放"队列操作权" */
        while(RM_LIST_LOCKED != __sync_val_compare_and_swap(&pIndexList->u32LockStatus, RM_LIST_LOCKED, RM_LIST_UNLOCKED))
        {
            /* FIXME: 这里一次性就能操作成功 */
        }
    } while(0);

    return iRet;    
}
///   End: 链表操作/支持函数     

///Bigin: 队列索引管理函数   
/*************************************************
*****************  函数说明 **********************
* 函数名    : Rm_IdxManageGetFreeNode
* 功能说明  : 获取可以接收数据(空闲)的结点
*************************************************/
UINT Rm_IdxManageGetFreeNode(MrIdxMangeCtx_t* pManageCtx, NodeIndex_S **ppNodeIndex)
{
    X_NULL_POINTER_CHK_TWO_PARA(pManageCtx, ppNodeIndex);
    
    return Rm_IdxManageGetFromHead(&pManageCtx->stFreeList, ppNodeIndex);
}

/*************************************************
*****************  函数说明 **********************
* 函数名    : Rm_IdxManageInsertToFreeList
* 功能说明  : 将空闲结点插入空闲列表
*************************************************/
UINT Rm_IdxManageInsertToFreeList(MrIdxMangeCtx_t* pManageCtx, NodeIndex_S* pNodeIndex)
{
    X_NULL_POINTER_CHK_TWO_PARA(pManageCtx, pNodeIndex);

    return Rm_IdxManageInsertToTail(&pManageCtx->stFreeList, pNodeIndex);
}

/*************************************************
*****************  函数说明 **********************
* 函数名    : Rm_IdxManageGetSendNode
* 功能说明  : 获取要发送的结点
*************************************************/
UINT Rm_IdxManageGetSendNode(MrIdxMangeCtx_t* pManageCtx, NodeIndex_S **ppNodeIndex)
{
    X_NULL_POINTER_CHK_TWO_PARA(pManageCtx, ppNodeIndex);
    
    return Rm_IdxManageGetFromHead(&pManageCtx->stSendList, ppNodeIndex);
}

/*************************************************
*****************  函数说明 **********************
* 函数名    : Rm_IdxManageInsertToSendingList
* 功能说明  : 将要发送的结点插入发送列表
*************************************************/
UINT Rm_IdxManageInsertToSendingList(MrIdxMangeCtx_t* pManageCtx, NodeIndex_S* pNodeIndex)
{
    X_NULL_POINTER_CHK_TWO_PARA(pManageCtx, pNodeIndex);
    
    return Rm_IdxManageInsertToTail(&pManageCtx->stSendList, pNodeIndex);
}
///End: 队列索引管理函数   


#if 0
#endif


UINT F_sendAudioPkt(T_MdeUCallCtxTable *pFromUCC, T_MdeUCallCtxTable *pToUCC, PktNode_S *ptNode)
{   
    XPOC_TRACEFUNC_IN;
    X_NULL_POINTER_CHK_THREE_PARA(pFromUCC, pToUCC, ptNode);

    struct sockaddr_in  stDstAddr;
    stDstAddr.sin_family = AF_INET;
    stDstAddr.sin_addr.s_addr = htonl(pToUCC->stMediaInfo.ulIpAddr);
    stDstAddr.sin_port = htons(pToUCC->stMediaInfo.rtpPort);

    if(ptNode->BufLen > DP_BUFFER_SIZE)
    {
        x_WarnLog("usRtpLen[%u] over RTCP_MAX_SIZE[%u].", ptNode->BufLen, DP_BUFFER_SIZE);
        return XPOC_ERR_FAIL;
    }

    T_ModCtxS *ptModCtx = Fplat_GetModuleCtx(XMOD_TYPE_MDE);
    MdeGlbData_S* ptGlbData = (MdeGlbData_S*)ptModCtx->pAppData;
    SINT iSocketFd = ptGlbData->stUdpAddrInfo.udpRtpFd.iSocketFd;
    
    SINT sendLen = sendto(iSocketFd, ptNode->PktBuf, ptNode->BufLen, 0, &stDstAddr, sizeof(struct sockaddr_in));

    if(g_UdpSndCount >= INVALID_VALUE_UINT)
        g_UdpSndCount = 1;
    else
        ++g_UdpSndCount;

    char *ptStrIp = inet_ntoa(stDstAddr.sin_addr);
    USHORT usPort = ntohs(stDstAddr.sin_port);
    if(sendLen <= 0)
    {
        x_ErrorLog("[PKT][UDP][RTP]send rtp pkt fa.sendto[%s:%d] in iSocketFd[%d].buffLen[%d],servId[%s->%s],errno[%d].", 
            ptStrIp, usPort, iSocketFd, ptNode->BufLen, pFromUCC->aucServId, pToUCC->aucServId, errno);
    }
    else
    {
        x_DebugLog("[PKT][UDP][RTP]send rtp pkt ok.sendto[%s:%d] in iSocketFd[%d].buffLen[%d],servId[%s->%s].", 
            ptStrIp, usPort, iSocketFd, ptNode->BufLen, pFromUCC->aucServId, pToUCC->aucServId);
    }

    XPOC_TRACEFUNC_OUT;
    return XPOC_ERR_SUCCESS;
}

UINT F_sendVideoPkt(T_MdeUCallCtxTable *pFromUCC, T_MdeUCallCtxTable *pToUCC, PktNode_S *ptNode)
{
    XPOC_TRACEFUNC_IN;
    X_NULL_POINTER_CHK_THREE_PARA(pFromUCC, pToUCC, ptNode);

    //x_InfoLog("test for log lose. 1");

    if(ptNode->BufLen > DP_BUFFER_SIZE)
    {
        x_WarnLog("usRtpLen[%u] over RTCP_MAX_SIZE[%u].", ptNode->BufLen, DP_BUFFER_SIZE);
        return XPOC_ERR_FAIL;
    }

    //x_InfoLog("test for log lose. 2");

    T_ModCtxS *ptModCtx = Fplat_GetModuleCtx(XMOD_TYPE_MDE);
    MdeGlbData_S* ptGlbData = (MdeGlbData_S*)ptModCtx->pAppData;
    SINT iSocketFd = ptGlbData->stTcpAddrInfo.tcpRtpFdList[pToUCC->usTcpRtpFdIdx].iSocketFd;

    SINT sendLen = send(iSocketFd, ptNode->PktBuf, ptNode->BufLen, 0); 

    if(g_TcpSndCount >= INVALID_VALUE_UINT)
        g_TcpSndCount = 1;
    else
        g_TcpSndCount++;

    //x_InfoLog("test for log lose. 3. pkt[r:%ld,s:%ld].", g_TcpRcvCount, g_TcpSndCount);

    if(sendLen <= 0)
    {
        x_ErrorLog("[PKT][TCP][RTP]pkt send fa.NodIdx[%d],buffLen[%d],[FD][%d:%d->%d:%d],UCCId[%d->%d],servId[%s->%s],errno[%d].", 
            ptNode->ulPktNodeIdx, ptNode->BufLen, 
            pFromUCC->usTcpRtpFdIdx, ptGlbData->stTcpAddrInfo.tcpRtpFdList[pFromUCC->usTcpRtpFdIdx].iSocketFd,
            pToUCC->usTcpRtpFdIdx, ptGlbData->stTcpAddrInfo.tcpRtpFdList[pToUCC->usTcpRtpFdIdx].iSocketFd, 
            pFromUCC->ulRecordIdx, pToUCC->ulRecordIdx, pFromUCC->aucServId, pToUCC->aucServId, errno);
    }
    else
    {
        x_DebugLog("[PKT][TCP][RTP]pkt send ok.NodIdx[%d],buffLen[%d],[FD][%d:%d->%d:%d],UCCId[%d->%d],servId[%s->%s],pkt[r:%u,s:%u].",
            ptNode->ulPktNodeIdx, ptNode->BufLen, 
            pFromUCC->usTcpRtpFdIdx, ptGlbData->stTcpAddrInfo.tcpRtpFdList[pFromUCC->usTcpRtpFdIdx].iSocketFd,
            pToUCC->usTcpRtpFdIdx, ptGlbData->stTcpAddrInfo.tcpRtpFdList[pToUCC->usTcpRtpFdIdx].iSocketFd, 
            pFromUCC->ulRecordIdx, pToUCC->ulRecordIdx, pFromUCC->aucServId, pToUCC->aucServId,
            g_TcpRcvCount, g_TcpSndCount);

    }

    //x_InfoLog("test for log lose. 4");

    XPOC_TRACEFUNC_OUT;
    return XPOC_ERR_SUCCESS;
}

UINT F_sendOtherPkt(T_MdeUCallCtxTable *pFromUCC, T_MdeUCallCtxTable *pToUCC, PktNode_S *ptNode)
{
    XPOC_TRACEFUNC_IN;
    X_NULL_POINTER_CHK_THREE_PARA(pFromUCC, pToUCC, ptNode);

    struct sockaddr_in  stDstAddr;
    stDstAddr.sin_family = AF_INET;
    stDstAddr.sin_port = htons(pToUCC->stMediaInfo.rtpPort);
    stDstAddr.sin_addr.s_addr = htonl(pToUCC->stMediaInfo.ulIpAddr);

    T_ModCtxS *ptModCtx = Fplat_GetModuleCtx(XMOD_TYPE_MDE);
    MdeGlbData_S* ptGlbData = (MdeGlbData_S*)ptModCtx->pAppData;
    SINT iSocketFd = ptGlbData->stUdpAddrInfo.udpRtpFd.iSocketFd;
    SINT sendLen = sendto(iSocketFd, ptNode->PktBuf, ptNode->BufLen, 0, &stDstAddr, sizeof(struct sockaddr_in));

    char *ptStrIp = inet_ntoa(stDstAddr.sin_addr);
    USHORT usPort = ntohs(stDstAddr.sin_port);
    if(sendLen <= 0)
    {
        x_ErrorLog("[PKT][RTP]other rtp pkt sendto[%s:%d] failed.buffLen[%d],servId[%s->%s],errno[%d].", 
            ptStrIp, usPort, ptNode->BufLen, pFromUCC->aucServId, pToUCC->aucServId, errno);
    }
    else
    {
        x_DebugLog("[PKT][RTP]other rtp pkt sendto[%s:%d] OK.buffLen[%d],servId[%s->%s].",
            ptStrIp, usPort, ptNode->BufLen, pFromUCC->aucServId, pToUCC->aucServId);
    }

    XPOC_TRACEFUNC_OUT;
    return XPOC_ERR_SUCCESS;
}

SINT F_setTcpBuff(int sockFd)
{
    // SOL_TCP   SOL_SOCKET

    int retval = 0;

    int optval = 0;
    socklen_t opt_len = sizeof(optval);
    retval = getsockopt(sockFd, SOL_SOCKET, SO_RCVBUF, (void *)&optval, &opt_len);
    if(0 != retval)
    {
        x_ErrorLog("[TCP] fd[%d]S getsockopt SO_RCVBUF fail. optval[%d],retval[%d].", sockFd, optval, retval);
    }
    else
    {
        x_DebugLog("[TCP] fd[%d]S getsockopt SO_RCVBUF OK. optval[%d].", sockFd, optval);
    }

    optval = 0;
    retval = getsockopt(sockFd, SOL_SOCKET, SO_SNDBUF, (void *)&optval, &opt_len);
    if(0 != retval)
    {
        x_ErrorLog("[TCP] fd[%d]S getsockopt SO_SNDBUF fail. optval[%d],retval[%d].", sockFd, optval, retval);
    }
    else
    {
        x_DebugLog("[TCP] fd[%d]S getsockopt SO_SNDBUF OK. optval[%d].", sockFd, optval);
    }

    
    int nRcvBuffLen = 1*1024*1024;
    int nSndBuffLen = 1*1024*1024;
    retval = setsockopt(sockFd, SOL_SOCKET, SO_RCVBUF, (void*)&nRcvBuffLen, (socklen_t)sizeof(int));
    if(0 != retval)
    {
        x_ErrorLog("[TCP] fd[%d] setsockopt SO_RCVBUF failed.", sockFd);
    }
    else
    {
        x_DebugLog("[TCP] fd[%d] setsockopt SO_RCVBUF[%d] OK.", sockFd, nRcvBuffLen);
    }

    retval = setsockopt(sockFd, SOL_SOCKET, SO_SNDBUF, (void*)&nSndBuffLen, (socklen_t)sizeof(int));
    if(0 != retval)
    {
        x_ErrorLog("[TCP] fd[%d] setsockopt SO_SNDBUF failed.", sockFd);
    }
    else
    {
        x_DebugLog("[TCP] fd[%d] setsockopt SO_SNDBUF[%d] OK.", sockFd, nSndBuffLen);
    }


//    int optval;
//    socklen_t opt_len = sizeof(opt_val);
    optval = 0;
    retval = getsockopt(sockFd, SOL_SOCKET, SO_RCVBUF, (void *)&optval, &opt_len);
    if(0 != retval)
    {
        x_ErrorLog("[TCP] fd[%d]E getsockopt SO_RCVBUF fail. optval[%d],retval[%d].", sockFd, optval, retval);
    }
    else
    {
        x_DebugLog("[TCP] fd[%d]E getsockopt SO_RCVBUF OK. optval[%d].", sockFd, optval);
    }

    optval = 0;
    retval = getsockopt(sockFd, SOL_SOCKET, SO_SNDBUF, (void *)&optval, &opt_len);
    if(0 != retval)
    {
        x_ErrorLog("[TCP] fd[%d]E getsockopt SO_SNDBUF fail. optval[%d],retval[%d].", sockFd, optval, retval);
    }
    else
    {
        x_ErrorLog("[TCP] fd[%d]E getsockopt SO_SNDBUF OK. optval[%d].", sockFd, optval);
    }


    int opt1val = 0;
    socklen_t opt1_len = sizeof(opt1val);
    retval = getsockopt(sockFd,SOL_TCP, SO_RCVBUF, (void *)&opt1val, &opt1_len);
    if(0 != retval)
    {
        x_ErrorLog("[TCP] fd[%d] getsockopt SOL_TCP SO_RCVBUF fail. optval[%d],retval[%d].", sockFd, opt1val, retval);
    }
    else
    {
        x_DebugLog("[TCP] fd[%d] getsockopt SOL_TCP SO_RCVBUF OK. optval[%d].", sockFd, opt1val);
    }

    opt1val = 0;
    retval = getsockopt(sockFd,SOL_TCP, SO_SNDBUF, (void *)&opt1val, &opt1_len);
    if(0 != retval)
    {
        x_ErrorLog("[TCP] fd[%d] getsockopt SOL_TCP SO_SNDBUF fail. optval[%d],retval[%d].", sockFd, opt1val, retval);
    }
    else
    {
        x_DebugLog("[TCP] fd[%d] getsockopt SOL_TCP SO_SNDBUF OK. optval[%d].", sockFd, opt1val);
    }

    return retval;
}

/*创建socket,bind端口*/
SINT F_createUdpSocket(UINT IpV4, USHORT Port)
{
    SINT sockFd = -1;
    
    /* Opening UDP sockets */ 
    if((sockFd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) 
    {
        x_ErrorLog("[UDP][Socket] open udp socket failed. errno = %d.\n", errno);
        return -1;
    }

    struct sockaddr_in Address;
    memset(&Address, 0, sizeof(Address));
    Address.sin_family = AF_INET;
    Address.sin_port = htons(Port);
    Address.sin_addr.s_addr = htonl(IpV4);

    char *ptStrIp = inet_ntoa(Address.sin_addr);
    USHORT usPort = ntohs(Address.sin_port);

    /* Binding the socket */
    if(bind(sockFd, (struct sockaddr *)&Address, sizeof(Address)) < 0) 
    {
        close(sockFd);
        x_ErrorLog("[UDP][Socket] bind udp socket failed. addr[%s:%d]. errno = %d.\n", ptStrIp, usPort, errno);
        return -1;
    }

    x_InfoLog("[UDP][Socket] bind udp socket OK. sockFd[%d], addr[%s:%d].\n", sockFd, ptStrIp, usPort);
    
    return sockFd;
}

//int ioctlsocket( int s, long cmd, u_long * argp);
SINT F_createTcpSocket(UINT IpV4, USHORT Port)
{
    SINT sockFd = -1;
    
    /* Opening TCP sockets */ 
    if((sockFd = socket(AF_INET, SOCK_STREAM|SOCK_NONBLOCK, IPPROTO_TCP)) < 0) 
    {
        x_ErrorLog("[TCP][Socket] open tcp socket failed. errno = %d.\n", errno);
        return -1;
    }

//    int imode = 1;  
//    int retVal = ioctlsocket(sockFd, FIONBIO, (u_long *)&imode);  
//    if(0 != retVal)
//    {
//        close(sockFd);
//        x_WarnLog("ioctlsocket failed.\n");
//        return -1;
//    }

    (void)F_setTcpBuff(sockFd);


    struct sockaddr_in Address;
    memset(&Address, 0, sizeof(Address));
    Address.sin_family = AF_INET;
    Address.sin_addr.s_addr = htonl(IpV4);
    Address.sin_port = htons(Port);

    char *ptStrIp = inet_ntoa(Address.sin_addr);
    USHORT usPort = ntohs(Address.sin_port);

    int opt = 1;
    setsockopt( sockFd, SOL_SOCKET, SO_REUSEADDR, (const void *)&opt, sizeof(opt) ); 
//    int enable = 1;
//    setsockopt(sockFd, IPPROTO_TCP, TCP_NODELAY, (void*)&enable, sizeof(enable));

    if(bind(sockFd, (struct sockaddr *)&Address, sizeof(Address)) < 0) 
    {
        close(sockFd);
        x_ErrorLog("[TCP][Socket] bind tcp socket failed. addr[%s:%d]. errno = %d.\n", ptStrIp, usPort, errno);
        return -1;
    }

//    int bufflen = 1;
//    setsockopt(sockFd, SOL_SOCKET, SO_SNDBUF, (void*)&bufflen, sizeof(bufflen));

    if(listen(sockFd, 20) < 0)
    {
        close(sockFd);
        x_ErrorLog("[TCP][Socket] listen tcp socket failed. addr[%s:%d]. errno = %d.\n", ptStrIp, usPort, errno);
        return -1;
    }

    x_InfoLog("[TCP][Socket] create tcp socket OK. listenFd[%d], addr[%s:%d].\n", sockFd, ptStrIp, usPort);

    return sockFd;
}

UINT F_epollCreate(UINT ulMaxFdNum, SINT *ptEpollFd, char *ptExt)
{
    if((*ptEpollFd = epoll_create(ulMaxFdNum)) < 0)
    {
        x_FatalLog("[X][EPOLL] create failed for DP mod. errno=%d.",  errno);
        abort();
    }

    x_InfoLog("[X][EPOLL] create OK, epollFd[%d],ext[%s].",  *ptEpollFd, ptExt);

    return XPOC_ERR_SUCCESS;
}

UINT F_epollAddFd(SINT epollFd, SINT sockFd, void *ptr, char *ptExt)
{
    struct epoll_event  Event;

    Event.events = EPOLLIN;  /* fd can read, level trigger mode(default) */ 
    Event.data.ptr = ptr;
    if(epoll_ctl(epollFd, EPOLL_CTL_ADD, sockFd, &Event) < 0) 
    {
        x_ErrorLog("[X][EPOLL] CTL(ADD) failed. epollFd[%d],[Fd][%d],errno[%d].", epollFd, sockFd, errno);    
        return XPOC_ERR_FAIL;
    }
    
    x_InfoLog("[X][EPOLL] CTL(ADD) OK. epollFd[%d],[Fd][%d],ext[%s].", epollFd, sockFd, ptExt);

    return XPOC_ERR_SUCCESS;
}

UINT F_epollDelFd(SINT epollFd, SINT sockFd, char *ptExt)
{
    /* remove the rtp socket from epoll set */    
    struct epoll_event  Event;
    if(epoll_ctl(epollFd, EPOLL_CTL_DEL, sockFd, &Event) < 0)
    {
        x_ErrorLog("[X][EPOLL] CTL(DEL) failed. epollFd[%d],[Fd][%d],errno[%d].", epollFd, sockFd, errno); 
        return XPOC_ERR_FAIL;
    }

    x_InfoLog("[X][EPOLL] CTL(DEL) OK. epollFd[%d],[Fd][%d],ext[%s].", epollFd, sockFd, ptExt);

    return XPOC_ERR_SUCCESS;
}

// added by shm, 接收到被叫的200 OK时,从早前的的rtcp保活信息中查找对应的映射端口
UINT findRtcpNetInfo(UINT nSsrc, MediaKeepaliveInfo * pInfo, UINT bDelete)
{
    //x_DebugLog("Ssrc is [%u]", nSsrc);
    int nFindFlag = 0;
    int i = 0;
    int j = 0;

    pthread_rwlock_rdlock(&g_lockRtcpKeepalive);
    
    for (i = 0, j = 0; j < g_RtcpKeepaliveList.nUserNum && i < MAX_SSRC_BUFFER; i++)
    {
        if (g_RtcpKeepaliveList.stSsrc[i].uUseFlag)
        {
            ++j;

            if (nSsrc == g_RtcpKeepaliveList.stSsrc[i].nSsrc)
            {
                *pInfo = g_RtcpKeepaliveList.stSsrc[i];

                if (bDelete)
                {
                    // 删除
                    memset(&g_RtcpKeepaliveList.stSsrc[i], 0, sizeof(MediaKeepaliveInfo));
                    g_RtcpKeepaliveList.stSsrc[i].uUseFlag = 0;
                    --g_RtcpKeepaliveList.nUserNum; 
                }

                nFindFlag = 1;
                break;
            }
        }
    }

    pthread_rwlock_unlock(&g_lockRtcpKeepalive);

    XPOC_TRACEFUNC_OUT;
    if (nFindFlag)
    {
        return XPOC_ERR_SUCCESS;
    }
    else
    {
        return XPOC_ERR_FAIL;
    }
}

// added by shm, 缓存早于200 OK的rtcp保活信息,解决首次话权释放消息丢失问题
UINT addRtcpNetInfo(MediaKeepaliveInfo stInfo)
{
    XPOC_TRACEFUNC_IN;
    int nSuccess  = 0;
    int i = 0;

    pthread_rwlock_wrlock(&g_lockRtcpKeepalive);

    // 查找第一个可用位置
    for (i = 0; i < MAX_SSRC_BUFFER; i++)
    {
        if (!g_RtcpKeepaliveList.stSsrc[i].uUseFlag)
        {
            struct in_addr addrTmp;
            memcpy(&addrTmp, &stInfo.ulIp, 4);
            char *ptCliIp_ucc_str = inet_ntoa(addrTmp);
            x_InfoLog("Add a rtcp keepalive info: ssrc[0x%x(%u)], NetAddr[%s:%d]", stInfo.nSsrc, stInfo.nSsrc, ptCliIp_ucc_str, stInfo.nPort);

            g_RtcpKeepaliveList.stSsrc[i] = stInfo;
            g_RtcpKeepaliveList.stSsrc[i].uUseFlag = 1;
            g_RtcpKeepaliveList.stSsrc[i].uTimestamp = time(0);

            ++g_RtcpKeepaliveList.nUserNum;

            nSuccess = 1;
            break;
        }
    }

    pthread_rwlock_unlock(&g_lockRtcpKeepalive);

    XPOC_TRACEFUNC_OUT;

    if (nSuccess)
    {
        return XPOC_ERR_SUCCESS;
    }
    else
    {
        struct in_addr addrTmp;
        memcpy(&addrTmp, &stInfo.ulIp, 4);
        char *ptCliIp_ucc_str = inet_ntoa(addrTmp);
        x_WarnLog("Rtcp ssrc buffer is full, size : [%d], ssrc[0x%x(%u)], NetAddr[%s:%d]", 
                    g_RtcpKeepaliveList.nUserNum, stInfo.nSsrc, stInfo.nSsrc, ptCliIp_ucc_str, stInfo.nPort);
        return XPOC_ERR_FAIL;
    }
}

// added by shm, 缓存早于200 OK的rtp保活信息,解决首次说话语音不全问题
UINT findRtpNetInfo(UINT nSsrc, MediaKeepaliveInfo * pInfo, UINT bDelete)
{
    x_InfoLog("Ssrc is [%u]", nSsrc);
    int nFindFlag = 0;
    int i = 0;
    int j = 0;

    pthread_rwlock_rdlock(&g_lockRtpKeepalive);
    
    for (i = 0, j = 0; j < g_RtpKeepaliveList.nUserNum && i < MAX_SSRC_BUFFER; i++)
    {
        if (g_RtpKeepaliveList.stSsrc[i].uUseFlag)
        {
            ++j;

            if (nSsrc == g_RtpKeepaliveList.stSsrc[i].nSsrc)
            {
                *pInfo = g_RtpKeepaliveList.stSsrc[i];

                if (bDelete)
                {
                    // 删除
                    memset(&g_RtpKeepaliveList.stSsrc[i], 0, sizeof(MediaKeepaliveInfo));
                    g_RtpKeepaliveList.stSsrc[i].uUseFlag = 0;
                    --g_RtpKeepaliveList.nUserNum; 
                }

                nFindFlag = 1;
                break;
            }
        }
    }

    pthread_rwlock_unlock(&g_lockRtpKeepalive);

    XPOC_TRACEFUNC_OUT;
    if (nFindFlag)
    {
        return XPOC_ERR_SUCCESS;
    }
    else
    {
        return XPOC_ERR_FAIL;
    }
}

UINT addRtpNetInfo(MediaKeepaliveInfo stInfo)
{
    XPOC_TRACEFUNC_IN;
    int nSuccess  = 0;
    int i = 0;

    pthread_rwlock_wrlock(&g_lockRtpKeepalive);

    // 查找第一个可用位置
    for (i = 0; i < MAX_SSRC_BUFFER; i++)
    {
        if (!g_RtpKeepaliveList.stSsrc[i].uUseFlag)
        {
            struct in_addr addrTmp;
            memcpy(&addrTmp, &stInfo.ulIp, 4);
            char *ptCliIp_ucc_str = inet_ntoa(addrTmp);
            x_InfoLog("Add a rtp keepalive info: ssrc [0x%x(%u)], net addr: [%s:%d]", stInfo.nSsrc, stInfo.nSsrc, ptCliIp_ucc_str, stInfo.nPort);

            g_RtpKeepaliveList.stSsrc[i] = stInfo;
            g_RtpKeepaliveList.stSsrc[i].uUseFlag = 1;
            g_RtpKeepaliveList.stSsrc[i].uTimestamp = time(0);

            ++g_RtpKeepaliveList.nUserNum;

            nSuccess = 1;
            break;
        }
    }

    pthread_rwlock_unlock(&g_lockRtpKeepalive);

    XPOC_TRACEFUNC_OUT;

    if (nSuccess)
    {
        return XPOC_ERR_SUCCESS;
    }
    else
    {
        struct in_addr addrTmp;
        memcpy(&addrTmp, &stInfo.ulIp, 4);
        char *ptCliIp_ucc_str = inet_ntoa(addrTmp);
        x_WarnLog("Rtcp ssrc buffer is full, size : [%d], ssrc[0x%x(%u)], NetAddr[%s:%d]", 
                    g_RtcpKeepaliveList.nUserNum, stInfo.nSsrc, stInfo.nSsrc, ptCliIp_ucc_str, stInfo.nPort);
        return XPOC_ERR_FAIL;
    }
}

// added by shm, 防止有异常加入的ssrc,导致内存占用;每五分钟清理一次,清理两分钟以前的数据
UINT clearMediaKeepaliveInfo(void)
{
    XPOC_TRACEFUNC_IN;
    int i = 0;
    UINT uNow = time(0);
    int  nRcrdRtpSSrcNum = 0; 
    int  nRcrdRtcpSSrcNum = 0;

    pthread_rwlock_wrlock(&g_lockRtcpKeepalive);
    nRcrdRtcpSSrcNum =  g_RtcpKeepaliveList.nUserNum;
    for (i = 0; i < MAX_SSRC_BUFFER; i++)
    {
        if (g_RtcpKeepaliveList.stSsrc[i].uUseFlag && uNow - g_RtcpKeepaliveList.stSsrc[i].uTimestamp > UDP_KEEPALIVE_INFO_TIMEOUT)
        {
            // 删除
            memset(&g_RtcpKeepaliveList.stSsrc[i], 0, sizeof(MediaKeepaliveInfo));
            g_RtcpKeepaliveList.stSsrc[i].uUseFlag = 0;
            --g_RtcpKeepaliveList.nUserNum;
        }
    }
    pthread_rwlock_unlock(&g_lockRtcpKeepalive);

    pthread_rwlock_wrlock(&g_lockRtpKeepalive);
    nRcrdRtpSSrcNum = g_RtpKeepaliveList.nUserNum;
    for (i = 0; i < MAX_SSRC_BUFFER; i++)
    {
        if (g_RtpKeepaliveList.stSsrc[i].uUseFlag && uNow - g_RtpKeepaliveList.stSsrc[i].uTimestamp > UDP_KEEPALIVE_INFO_TIMEOUT)
        {
            // 删除
            memset(&g_RtpKeepaliveList.stSsrc[i], 0, sizeof(MediaKeepaliveInfo));
            g_RtpKeepaliveList.stSsrc[i].uUseFlag = 0;
            --g_RtpKeepaliveList.nUserNum;
        }
    }
    pthread_rwlock_unlock(&g_lockRtpKeepalive);

    x_InfoLog("Clear ssrc buffer. rtp [%d->%d], rtcp [%d->%d]", 
            nRcrdRtpSSrcNum, g_RtpKeepaliveList.nUserNum, nRcrdRtcpSSrcNum, g_RtcpKeepaliveList.nUserNum);

    XPOC_TRACEFUNC_OUT;

    return XPOC_ERR_SUCCESS;
}





mdeMrMediaFunc.c

#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>  /* for TCP_XXX defines */

#include "rtcp_mod_decode.h"
#include "rtcp_mod_encode.h"
#include "rtcp_mod_proc.h"

#include "XpocDBInterface.h"

#include "mdeMrCommFunc.h"
#include "mdeMrResourceFunc.h"
#include "mdeMrMediaFunc.h"

extern UINT g_UdpRcvCount;
extern UINT g_UdpSndCount;
extern UINT g_TcpRcvCount;
extern UINT g_TcpSndCount;

extern T_ServerCfg g_tServerCfg;

ThreadInfo_t g_threadDpSendRtp[MAX_NUM_OF_DP_THREAD];/* 媒体处理线程,现在UDP,TCP 分别用一个. 其他的未用到. */
extern const char *glbFDType[FD_TYPE_BUTT];

//static void _set_tcp_nodelay(int fd);

#if 0
#endif


/*  MDE主要创建如下几个线程:
    1. UDP接收线程 and TCP接收线程;
    2. audio|floor 发送线程; 
    3. video 发送线程;
*/

UINT mdeInitDpThread(T_ModCtxS *ptModCtx)
{
    XPOC_TRACEFUNC_IN;
    X_NULL_POINTER_CHK_ONE_PARA(ptModCtx);

    UINT ulReturn = XPOC_ERR_SUCCESS;

    /* [1] create epoll recv thread */
    pthread_t threadId;
    ulReturn = pthread_create(&threadId, 0, Dp_RecvUdpPacket, NULL);
    if(0 != ulReturn)
    {
        x_ErrorLog("[X][UDP] main recv thread create failed. mainThreadId[%lu], result[%lu].\n", threadId, ulReturn);
        return ulReturn;
    }
    else
    {
        x_InfoLog("[X][UDP] main recv thread create OK. mainThreadId[%lu].\n", threadId);
    }

    ulReturn = pthread_create(&threadId, 0, Dp_RecvTcpPacket, NULL);
    if(0 != ulReturn)
    {
        x_ErrorLog("[X][TCP] main recv thread create failed. mainThreadId[%lu], result[%lu].\n", threadId, ulReturn);
        return ulReturn;
    }
    else
    {
        x_InfoLog("[X][TCP] main recv thread create OK. mainThreadId[%lu].\n", threadId);
    }

    /*[2] Create number of RTP Worker threads */
    MdeGlbData_S* ptGlbData = (MdeGlbData_S*)ptModCtx->pAppData;
    PktQueue_S *ptBuff = NULL;

    UCHAR i = 0;
    for (i = 0; i < ptGlbData->stCfgInfo.rtpWorkerThreadNum; i++)
    {
        ptBuff = Fplat_Malloc(sizeof(PktQueue_S), 0);
        if(!ptBuff)
        {
            x_ErrorLog("RTP packet data buffer queue alloc failed.\n");
            return XPOC_ERR_FAIL;
        }
        memset(ptBuff, 0x00, sizeof(PktQueue_S));
        memset(&g_threadDpSendRtp[i], 0x00, sizeof(ThreadInfo_t));

        ptBuff->PktNode = (PktNode_S*)Fplat_Malloc(sizeof(PktNode_S) * g_tServerCfg.nMediaQueueSize, 0);
        ptBuff->astPktNodeIndex = (NodeIndex_S*)Fplat_Malloc(sizeof(NodeIndex_S) * g_tServerCfg.nMediaQueueSize, 0);
        if (!ptBuff->PktNode || !ptBuff->astPktNodeIndex)
        {
            x_ErrorLog("Alloc pkt node or node idex failed.\n");
            exit(-1);
        }

        /******************************************************************************/
        /* 初始化队列索引管理上下文 */
        Rm_IdxManageInitCtx(&ptBuff->stIndexManageCtx, ptBuff->astPktNodeIndex, g_tServerCfg.nMediaQueueSize);
        /******************************************************************************/

        g_threadDpSendRtp[i].pPktQueue = (PktQueue_S*)ptBuff;        
        g_threadDpSendRtp[i].enThreadType = (1==i) ? PROTO_TCP : PROTO_UDP;  /* 记录当前线程处理的协议类型. 0--用于UDP处理, 1--用于TCP处理 */
        ulReturn = pthread_create(&(g_threadDpSendRtp[i].ThreadId), 0, Dp_HandlePktFromQueue, (void*)&g_threadDpSendRtp[i]);
        if(0 != ulReturn)
        {
            Fplat_Free(ptBuff);
            x_ErrorLog("[X]RTP handle thread create failed. thread[%d:%lu], result[%d].\n", 
                    i, g_threadDpSendRtp[i].ThreadId, ulReturn);
            return XPOC_ERR_FAIL;
        }
        else
        {
            x_InfoLog("[X]RTP handle thread create OK. thread[%d:%lu], pktQueue[%p], queue size [%d].\n", 
                    i, g_threadDpSendRtp[i].ThreadId, g_threadDpSendRtp[i].pPktQueue, g_tServerCfg.nMediaQueueSize);
        }
    }

    XPOC_TRACEFUNC_OUT;
    return XPOC_ERR_SUCCESS;
}

/* 修改经过公网穿越后的媒体远端地址(如果有变化). 需要区分 udp/tcp 处理 */
/* 1. 经过网络穿越时, 来自终端的 rtp/rtcp 包的发送端口会变化 
   2. 经过网络穿越的 rtp包与 rtcp包的源端口没有明显的 rtcp-port = rtp-port+1 的关系 */
/*  UDP: RTP/RTCP头部编码格式
    RTP:
    stream setup by SDP--1
    PT                 --1
    sequence number    --2
    timestamp          --4
    ssrc               --4

    RTCP:
    version + p + subtype --1
    pt                --1
    length            --2
    ssrc              --4
    name              --4

    TCP RTP 在标准RTP之前再加2bytes的长度位 用于粘包情况时的解包.
*/
UINT mdeSaveClientAddr2UCC(UCHAR *ptRecvBuf, struct sockaddr_in *ptCliAddr, MediaFdS *ptMediaFd, UINT *ptUCCId)
{
    XPOC_TRACEFUNC_IN;
    X_NULL_POINTER_CHK_FOUR_PARA(ptRecvBuf, ptCliAddr, ptMediaFd, ptUCCId);

    /* 接收到当前消息的client address */
    USHORT usClientPort = ntohs(ptCliAddr->sin_port);
    UINT ulClientIp     = ntohl(ptCliAddr->sin_addr.s_addr);
    char *ptClientIp_str= inet_ntoa(ptCliAddr->sin_addr);

	USHORT usHeadPara = 0;
    UCHAR  ucHeadParaLen = 0;
    Byte_2_U16(ptRecvBuf, usHeadPara, ucHeadParaLen);/* UDP data部分头两个字节  */
    
    /* decode udp RTP/RTCP for ssrc. get UCC by ssrc. */
    UINT ulSsrc = 0;
    UCHAR  ucSsrcLen = 0;
    if(FD_TYPE_UDP_RTP == ptMediaFd->fdType)
    {
        if(0 == usHeadPara)
            Byte_2_U32(ptRecvBuf+4, ulSsrc, ucSsrcLen)
        else
            Byte_2_U32(ptRecvBuf+8, ulSsrc, ucSsrcLen)
        x_DebugLog("[PKT][UDP][RTP]recv pkt from sockfd[%d].ssrc[0x%x(%u)],client addr[%s:%d].", 
                    ptMediaFd->iSocketFd, ulSsrc, ulSsrc, ptClientIp_str, usClientPort);
    }
    else if(FD_TYPE_UDP_RTCP == ptMediaFd->fdType)
    {
        Byte_2_U32(ptRecvBuf+4, ulSsrc, ucSsrcLen);
        x_DebugLog("[PKT][UDP][RTCP]recv pkt from sockfd[%d].ssrc[0x%x(%u)],client addr[%s:%d].", 
                    ptMediaFd->iSocketFd, ulSsrc, ulSsrc, ptClientIp_str, usClientPort);
    }
//    else if(FD_TYPE_TCP_RTP == ptMediaFd->fdType) //tcp 在此不哟个再根据 ssrc定位 UCC.
//    {
//        Byte_2_U32(ptRecvBuf+10, ulSsrc, ucSsrcLen);/* 8->10  TCP data部分增加了长度位, 偏移多两个字节  */
//        x_InfoLog("[PKT][TCP][RTP]recv pkt from sockfd[%d].fdType[%d],ssrc[0x%x--%d],client addr[%s:%d].", 
//                    ptMediaFd->iSocketFd, ptMediaFd->fdType, ulSsrc, ulSsrc, ptClientIp_str, usClientPort);
//    }

    UINT ulReturn = XPOC_ERR_SUCCESS;
    T_MdeUCallCtxTable stUCC;
    memset(&stUCC, 0, sizeof(T_MdeUCallCtxTable));
    if(0 != *ptUCCId && INVALID_VALUE_UINT != *ptUCCId)
    {
        stUCC.ulRecordIdx = *ptUCCId;
        ulReturn = XMdeUCallCtxOpr(XDB_OPR_QRY, NULL, 0, 0, 0, NULL, &stUCC.ulRecordIdx, &stUCC);
    }
    else  /* when receive udp, get UCC by ssrc*/
    {
        ulReturn = XMdeUCallCtxOpr(XDB_OPR_QRY, NULL, 0, 0, 0, &ulSsrc, NULL, &stUCC);
    }

    if(XPOC_ERR_SUCCESS != ulReturn)
    {
        MediaKeepaliveInfo stInfo;

        // added by shm
        if(FD_TYPE_UDP_RTCP == ptMediaFd->fdType)  
        {
            if (XPOC_ERR_SUCCESS != findRtcpNetInfo(ulSsrc, &stInfo, 0))   // 读锁
            {
                stInfo.ulIp  = ulClientIp;
                stInfo.nPort = usClientPort;
                stInfo.nSsrc = ulSsrc;
                addRtcpNetInfo(stInfo);     // 写锁
            }
        }
        else if(FD_TYPE_UDP_RTP == ptMediaFd->fdType)
        {
            if (XPOC_ERR_SUCCESS != findRtpNetInfo(ulSsrc, &stInfo, 0))   // 读锁
            {
                stInfo.ulIp  = ulClientIp;
                stInfo.nPort = usClientPort;
                stInfo.nSsrc = ulSsrc;
                addRtpNetInfo(stInfo);     // 写锁
            }
        }
        // x_WarnLog("recv pkt:sockfd[%d] fdType[%d],from origServId[%s],ssrc[0x%x--%d],client addr[%s:%d].", 
        //        ptMediaFd->iSocketFd, ptMediaFd->fdType, stUCC.aucServId,  ulSsrc, ulSsrc, ptClientIp_str, usClientPort);
        return ulReturn;
    }

    UINT ulRmtNetIP = htonl(stUCC.stMediaInfo.ulIpAddr);
    struct in_addr addrTmp;
    memcpy(&addrTmp, &ulRmtNetIP, 4);
    char *ptCliIp_ucc_str = inet_ntoa(addrTmp);
    // x_DebugLog("recv pkt:sockfd[%d] fdType[%d],from origServId[%s],client addr[%s:%d]; UCC client addr[%s:%d-%d].", 
    //            ptMediaFd->iSocketFd, ptMediaFd->fdType, stUCC.aucServId, ptClientIp_str, usClientPort, 
    //            ptCliIp_ucc_str, stUCC.stMediaInfo.rtpPort, stUCC.stMediaInfo.rtcpPort);

    if(FD_TYPE_UDP_RTP == ptMediaFd->fdType)
    {
        //对方可能是从NAT发送过来的,需要记录当前的 IP port.
        if((stUCC.stMediaInfo.ulIpAddr != ulClientIp) || (stUCC.stMediaInfo.rtpPort != usClientPort))
        {
            x_InfoLog("Will update addr:sockfd[%d] fdType[%d],from origServId[%s],client addr[%s:%d]; UCC client addr[%s:%d-%d].", 
               ptMediaFd->iSocketFd, ptMediaFd->fdType, stUCC.aucServId, ptClientIp_str, usClientPort, 
               ptCliIp_ucc_str, stUCC.stMediaInfo.rtpPort, stUCC.stMediaInfo.rtcpPort);

            stUCC.stMediaInfo.ulIpAddr = ulClientIp;
            stUCC.stMediaInfo.rtpPort = usClientPort;
            (void)XMdeUCallCtxOpr(XDB_OPR_MOD, NULL, 0, 0, 0, NULL, &stUCC.ulRecordIdx, &stUCC);
        }
    }
    else if(FD_TYPE_UDP_RTCP == ptMediaFd->fdType)
    {
        if((stUCC.stMediaInfo.ulIpAddr != ulClientIp) || (stUCC.stMediaInfo.rtcpPort != usClientPort))
        {
            x_InfoLog("Will update addr:sockfd[%d] fdType[%d],from origServId[%s],client addr[%s:%d]; UCC client addr[%s:%d-%d].", 
               ptMediaFd->iSocketFd, ptMediaFd->fdType, stUCC.aucServId, ptClientIp_str, usClientPort, 
               ptCliIp_ucc_str, stUCC.stMediaInfo.rtpPort, stUCC.stMediaInfo.rtcpPort);

            stUCC.stMediaInfo.ulIpAddr = ulClientIp;
            stUCC.stMediaInfo.rtcpPort = usClientPort;
            (void)XMdeUCallCtxOpr(XDB_OPR_MOD, NULL, 0, 0, 0, NULL, &stUCC.ulRecordIdx, &stUCC);
        }
    }
    else if(FD_TYPE_TCP_RTP == ptMediaFd->fdType)
    {
        if((stUCC.stMediaInfo.ulIpAddr != ptMediaFd->ulCliIp) || (stUCC.stMediaInfo.rtpPort != ptMediaFd->rtpCliPort))
        {
            x_InfoLog("Will update addr:sockfd[%d] fdType[%d],from origServId[%s],client addr[%s:%d]; UCC client addr[%s:%d-%d].", 
               ptMediaFd->iSocketFd, ptMediaFd->fdType, stUCC.aucServId, ptClientIp_str, usClientPort, 
               ptCliIp_ucc_str, stUCC.stMediaInfo.rtpPort, stUCC.stMediaInfo.rtcpPort);

            stUCC.stMediaInfo.ulIpAddr = ptMediaFd->ulCliIp;
            stUCC.stMediaInfo.rtpPort = ptMediaFd->rtpCliPort;
            stUCC.stMediaInfo.rtcpPort = ptMediaFd->rtcpCliPort;
            stUCC.usTcpRtpFdIdx = ptMediaFd->recordIdx;
            (void)XMdeUCallCtxOpr(XDB_OPR_MOD, NULL, 0, 0, 0, NULL, &stUCC.ulRecordIdx, &stUCC);
        }
    }

    *ptUCCId = stUCC.ulRecordIdx;

    XPOC_TRACEFUNC_OUT;
    return XPOC_ERR_SUCCESS;
}


#if 0
#endif


/*转发数据包*/
UINT Dp_SendRtpPkt(T_MdeUCallCtxTable *pFromUCC, T_MdeUCallCtxTable *pToUCC, PktNode_S *ptNode)
{
    X_NULL_POINTER_CHK_THREE_PARA(pFromUCC, pToUCC, ptNode);

    if(MEDIA_AUDIO == pFromUCC->enMediaType) /*转发音频*/
    {
        (void)F_sendAudioPkt(pFromUCC, pToUCC, ptNode);
    }
    else if(MEDIA_VIDEO == pFromUCC->enMediaType)  /*转发视频*/
    {
       (void)F_sendVideoPkt(pFromUCC, pToUCC, ptNode);
    }
    else /*其他类型,Other-XPOC,话权等控制消息*/
    {
        (void)F_sendOtherPkt(pFromUCC, pToUCC, ptNode);
    }

    return XPOC_ERR_SUCCESS;
}

/* rtcp现在只有话权消息在录音录像时直接转发, 不作媒体转换.  */
UINT Dp_SendRtcpPkt(T_MdeUCallCtxTable *pToUCC, PktNode_S *ptNode)
{
    XPOC_TRACEFUNC_IN;
    X_NULL_POINTER_CHK_TWO_PARA(pToUCC, ptNode);

    T_ModCtxS *ptModCtx = Fplat_GetModuleCtx(XMOD_TYPE_MDE);
    MdeGlbData_S* ptGlbData = (MdeGlbData_S*)ptModCtx->pAppData;

    SINT sockSendFd = ptGlbData->stUdpAddrInfo.udpRtcpFd.iSocketFd;  

    socklen_t iAddrLen = sizeof(struct sockaddr_in);
    struct sockaddr_in  stDstAddr;
    stDstAddr.sin_family = AF_INET;
    stDstAddr.sin_port = htons(pToUCC->stMediaInfo.rtcpPort);
    stDstAddr.sin_addr.s_addr = htonl(pToUCC->stMediaInfo.ulIpAddr);

    UCHAR *pRtcpBuff = ptNode->PktBuf;
    UINT ulBufLen = ptNode->BufLen;

    SINT WriteLen = sendto(sockSendFd, pRtcpBuff, ulBufLen, 0, &stDstAddr, iAddrLen);
    if(WriteLen <= 0)
    {
        x_ErrorLog("rtcp sendto[0x%x:%d],sockFd[%d],len[%d],ret[%d],errno[%d].\n",
                  pToUCC->stMediaInfo.ulIpAddr, pToUCC->stMediaInfo.rtcpPort, sockSendFd, ulBufLen, WriteLen, errno);
    }

    XPOC_TRACEFUNC_OUT;
    return WriteLen;
}

/* 待转发和下推的媒体源呼叫中存在一方用户也是转发/下推呼叫的参与方, 注意不要把媒体返向发回去. */
/* 媒体源转发给转发(组)用户或RCE. */
UINT Dp_TransRtpPkt(T_MdeUCallCtxTable *ptFromUCC, UINT ulTargetCC, PktNode_S *ptNode)
{
    XPOC_TRACEFUNC_IN;

    x_InfoLog("trans pkt: servId[%s] --> CCId[%d].", ptFromUCC->aucServId, ulTargetCC);

    UINT ulReturn = XPOC_ERR_SUCCESS;

    T_MdeCallCtxTable stTransToCC;
    T_MdeUCallCtxTable stTransToUCC;

    memset(&stTransToCC, 0, sizeof(T_MdeCallCtxTable));
    stTransToCC.ulRecordIdx = ulTargetCC;
    ulReturn = XMdeCallCtxOpr(XDB_OPR_QRY, NULL, 0, &stTransToCC.ulRecordIdx, &stTransToCC);
    if(XPOC_ERR_SUCCESS != ulReturn)
        return XPOC_ERR_SUCCESS;

    int i = 0;
    for(i = 0; i < stTransToCC.stUCCIdList.UcNum && i < MAX_UC_NUM; i++)
    {
        if(TRUE != stTransToCC.stUCCIdList.stUCCId[i].IsUsedFg)
            continue;

        memset(&stTransToUCC, 0, sizeof(T_MdeUCallCtxTable));
        ulReturn = XMdeUCallCtxOpr(XDB_OPR_QRY, NULL, 0, 0, 0, NULL, &stTransToCC.stUCCIdList.stUCCId[i].ulUCallCtxId, &stTransToUCC);
        if(XPOC_ERR_SUCCESS != ulReturn)
            continue;

        if(MEDIA_VIDEO != stTransToUCC.enMediaType) /* 只转发视频, 音频不转发. */
        {
            x_WarnLog("from media is not video, not send.");
            continue;
        }

        /* record转发 */
        /* A<--videocall-->B, A<--transto-->C, 此时媒体的真实走向是 A/B所对应的mediaUCC转发媒体给C.
           此时如果在A/C上设置了录像, 需要把 视频呼叫这路媒体再转发一份给A<--transto-->C这路业务对应的record上. 
           这里对于目的方是转发的主叫方,如果主叫方被录像也需要转发到RCE. 
            对于视频转发, 虽然A是发送方,不会被转发到. 但是在录像时A是作为视频源. B转发给C的视频在外部表象上是A转发出去的.因此视频转发的转发方A录像时应该把B-->C的媒体录像.
           */
        if(0 != stTransToUCC.ulRcrdCCId && INVALID_VALUE_UINT != stTransToUCC.ulRcrdCCId)
        {
            (void)Dp_RecordRtpPkt(&stTransToUCC, stTransToUCC.ulRcrdCCId, ptNode);
        }

        if((SDP_MODE_SENDRECV != stTransToUCC.stMediaInfo.enTopoly)&&(SDP_MODE_RECVONLY != stTransToUCC.stMediaInfo.enTopoly))
        {
            x_InfoLog("[RTP][TRANS]TransToUCC:servId[%s],topoly[%d(1-sendonly,2-recvonly,3-sendrecv,4-inactive)]. not send.", 
                      stTransToUCC.aucServId, stTransToUCC.stMediaInfo.enTopoly);
            continue;
        }

        if(0 == strcmp(stTransToUCC.aucServId, ptFromUCC->aucServId)) /* 目的方是视频源, 跳过 */
        {
            x_DebugLog("[RTP][TRANS]from == to, not send.");
            continue;
        }

        if(stTransToUCC.ucTransCallerFg)/* 目的方不能是转发的主叫方 */
        {
            x_DebugLog("[RTP][TRANS]target is pushOwn, not send.");
            continue;
        }

        x_DebugLog("[RTP][TRANS]trans this video pkt.");

        (void)Dp_SendRtpPkt(ptFromUCC, &stTransToUCC, ptNode);
    }

    XPOC_TRACEFUNC_OUT;
    return XPOC_ERR_SUCCESS;

}

UINT Dp_RecordRtpPkt(T_MdeUCallCtxTable *ptFromUCC, UINT ulTargetCC, PktNode_S *ptNode)
{
    XPOC_TRACEFUNC_IN;

    UINT ulReturn = XPOC_ERR_SUCCESS;

    T_MdeCallCtxTable stToCC;
    T_MdeUCallCtxTable stToUCC;

    memset(&stToCC, 0, sizeof(T_MdeCallCtxTable));
    stToCC.ulRecordIdx = ulTargetCC;
    ulReturn = XMdeCallCtxOpr(XDB_OPR_QRY, NULL, 0, &stToCC.ulRecordIdx, &stToCC);
    if(XPOC_ERR_SUCCESS != ulReturn)
        return XPOC_ERR_SUCCESS;

    int i = 0;
    for(i = 0; i < stToCC.stUCCIdList.UcNum && i < MAX_UC_NUM; i++)
    {
        if(TRUE != stToCC.stUCCIdList.stUCCId[i].IsUsedFg)
            continue;

        memset(&stToUCC, 0, sizeof(T_MdeUCallCtxTable));
        ulReturn = XMdeUCallCtxOpr(XDB_OPR_QRY, NULL, 0, 0, 0, NULL, &stToCC.stUCCIdList.stUCCId[i].ulUCallCtxId, &stToUCC);
        if(XPOC_ERR_SUCCESS != ulReturn)
            continue;

        if(stToUCC.enMediaType != ptFromUCC->enMediaType)
        {
            x_DebugLog("[RTP][RCRD]from.media[%d] != to.media[%d], do nothing.", ptFromUCC->enMediaType, stToUCC.enMediaType);
            continue;
        }

        x_DebugLog("[RTP][RCRD]record rtp pkt(media:%d): servId[%s]-->mediaCCId[%d].", 
                    ptFromUCC->enMediaType, ptFromUCC->aucServId, stToCC.ulRecordIdx);
        (void)Dp_SendRtpPkt(ptFromUCC, &stToUCC, ptNode);
    }

    XPOC_TRACEFUNC_OUT;
    return XPOC_ERR_SUCCESS;

}

UINT Dp_RecordRtcpPkt(T_MdeUCallCtxTable *ptFromUCC, UINT ulTargetCC, PktNode_S *ptNode)
{
    XPOC_TRACEFUNC_IN;

    UINT ulReturn = XPOC_ERR_SUCCESS;

    T_MdeCallCtxTable stToCC;
    T_MdeUCallCtxTable stToUCC;

    memset(&stToCC, 0, sizeof(T_MdeCallCtxTable));
    stToCC.ulRecordIdx = ulTargetCC;
    ulReturn = XMdeCallCtxOpr(XDB_OPR_QRY, NULL, 0, &stToCC.ulRecordIdx, &stToCC);
    if(XPOC_ERR_SUCCESS != ulReturn)
        return XPOC_ERR_SUCCESS;

    int i = 0;
    for(i = 0; i < stToCC.stUCCIdList.UcNum && i < MAX_UC_NUM; i++)
    {
        if(TRUE != stToCC.stUCCIdList.stUCCId[i].IsUsedFg)
            continue;

        memset(&stToUCC, 0, sizeof(T_MdeUCallCtxTable));
        ulReturn = XMdeUCallCtxOpr(XDB_OPR_QRY, NULL, 0, 0, 0, NULL, &stToCC.stUCCIdList.stUCCId[i].ulUCallCtxId, &stToUCC);
        if(XPOC_ERR_SUCCESS != ulReturn)
            continue;

        if(stToUCC.enMediaType != ptFromUCC->enMediaType)
        {
            x_InfoLog("[RTCP][RCRD]from.media[%d] != to.media[%d].", ptFromUCC->enMediaType, stToUCC.enMediaType);
            continue;
        }

        x_DebugLog("[RTCP][RCRD]record rtcp pkt: servId[%s]-->mediaCCId[%d].", ptFromUCC->aucServId, stToCC.ulRecordIdx);
        UINT WriteLen = Dp_SendRtcpPkt(&stToUCC, ptNode);
        if(WriteLen <= 0)
        {
            x_ErrorLog("Media Rtcp Msg Send Failed!UCCId[%d],udn[%s]\n",
                        stToCC.stUCCIdList.stUCCId[i].ulUCallCtxId, stToUCC.aucServId);
            return XPOC_ERR_FAIL;
        }   
    }

    XPOC_TRACEFUNC_OUT;
    return XPOC_ERR_SUCCESS;

}


/* 分发RTP包 */
UINT Dp_DistributeRtpPkt(PktNode_S *ptNode)
{
    XPOC_TRACEFUNC_IN;
    X_NULL_POINTER_CHK_ONE_PARA(ptNode);

    UINT ulReturn = XPOC_ERR_SUCCESS;
    T_MdeCallCtxTable stCC;
    T_MdeUCallCtxTable stFromUCC;
    T_MdeUCallCtxTable stToUCC;

    ulReturn = XMdeUCallCtxOpr(XDB_OPR_QRY, NULL, 0, 0, 0, NULL, &ptNode->ulUCCId, &stFromUCC);
    if(XPOC_ERR_SUCCESS != ulReturn)
    {
        x_ErrorLog("Qry UCallCtx failed, uccid [%d].", ptNode->ulUCCId);
        return ulReturn;
    }

    /*发送端的UCC 非 sendrecv || sendonly, 不转发此UCC的媒体流*/
    if(SDP_MODE_SENDRECV != stFromUCC.stMediaInfo.enTopoly && SDP_MODE_SENDONLY != stFromUCC.stMediaInfo.enTopoly)
    {
        x_WarnLog("[PKT]discard. reason:From topoly[%d], serveId [%s].\n", 
                stFromUCC.stMediaInfo.enTopoly, stFromUCC.aucServId);
        UCHAR* pBuff = ptNode->PktBuf;
        x_WarnLog("MsgLen[%u] %x %x %x %x %x %x %x %x.", ptNode->BufLen,
                pBuff[0], pBuff[1], pBuff[2], pBuff[3], pBuff[4], pBuff[5], pBuff[6], pBuff[7]);
        return XPOC_ERR_SUCCESS;
    }

    if(XPOC_ERR_SUCCESS != XMdeCallCtxOpr(XDB_OPR_QRY, NULL, 0, &stFromUCC.ulCallCtxId, &stCC))
    {
        x_ErrorLog("Qry CallCtx fail.");
        return XPOC_ERR_FAIL;
    }

    // added by shm, 如果是组呼或者半双工,判断话权。 防止话权超时后,释放消息丢失,导致两人或多人同时讲话的情况
    /*if ((Serv_AudioHalfPri == stCC.servType || Serv_AudioGroup == stCC.servType)
        && stFromUCC.ulCBId != stCC.ulSpeakCBId)
    {
        x_WarnLog("[PKT]discard. Floor belong to cbid[%d], not [%s(%d)]", 
                stCC.ulSpeakCBId, stFromUCC.aucServId, stFromUCC.ulCBId);
        return XPOC_ERR_SUCCESS;
    }*/
    
    /*数据分发*/
    UINT i = 0;
    for(i = 0; i < stCC.stUCCIdList.UcNum && i < MAX_UC_NUM; i++)
    {
        if(TRUE != stCC.stUCCIdList.stUCCId[i].IsUsedFg)
            continue;

        stToUCC.ulRecordIdx = stCC.stUCCIdList.stUCCId[i].ulUCallCtxId;
        ulReturn = XMdeUCallCtxOpr(XDB_OPR_QRY, NULL, 0, 0, 0, NULL, &stToUCC.ulRecordIdx, &stToUCC);
        if(XPOC_ERR_SUCCESS != ulReturn)
            continue;

        if(stFromUCC.ulRecordIdx == stToUCC.ulRecordIdx)/* 不能回发 */
        {
            continue;
        }

        if(MEDIA_VIDEO == stToUCC.enMediaType)
        {
            x_DebugLog("[TCP]media:From:UCC[%d],servId[%s],type[%d],From:mediaFdIdx[%d]-->To:UCC[%d],servId[%s],type[%d],mediaFdIdx[%d], To_topoly[%d].\n", 
                    stFromUCC.ulRecordIdx, stFromUCC.aucServId, stFromUCC.enMediaType, stFromUCC.usTcpRtpFdIdx,
                    stToUCC.ulRecordIdx, stToUCC.aucServId, stToUCC.enMediaType, stToUCC.usTcpRtpFdIdx, stToUCC.stMediaInfo.enTopoly);
        }

        /* 接收端的UCC 非 SENDRECV||RECVONLY, 不向此UCC转发媒体流 */
        if((stFromUCC.enMediaType != stToUCC.enMediaType) 
            || ((SDP_MODE_SENDRECV != stToUCC.stMediaInfo.enTopoly)&&(SDP_MODE_RECVONLY != stToUCC.stMediaInfo.enTopoly)))
        {
            x_InfoLog("[PKT]discard. reason:mediatype[from:%d,to:%d],to.topoly[%d].", 
                      stFromUCC.enMediaType, stToUCC.enMediaType, stToUCC.stMediaInfo.enTopoly);
            continue;
        }

        (void)Dp_SendRtpPkt(&stFromUCC, &stToUCC, ptNode);      
    }


    /* ---------------视频下推(转发)----------------- */
    /*  1. 需要视频源CC中记录需要下推的DstCC.
        2. DC下发的视频不转发;
        3. 不能下推给视频源方.
        4. 不能下推给DC -- 由媒体方向控制.
        5. 呼叫释放需要注意清除CC.ulTransToCCId.
        6. 此功能只考虑视频媒体.
    */

    /* 当前媒体是 视频上拉/转发的媒体源 && 当前呼叫存在下推/转发的目的方 */
    if(stFromUCC.ucTransMediaSrcFg)
    {
        for(i=0; i<CALL_MAX_NUM; i++)
        {
            if(0 != stCC.ulTransToCCId[i] && INVALID_VALUE_UINT != stCC.ulTransToCCId[i])
            {
                (void)Dp_TransRtpPkt(&stFromUCC, stCC.ulTransToCCId[i], ptNode);
            }
        }
    }

    /* record转发 */
    if(0 != stFromUCC.ulRcrdCCId && INVALID_VALUE_UINT != stFromUCC.ulRcrdCCId)
    {
        /// 
        if(stCC.servType == Serv_AudioGroup && (stCC.ulSpeakCBId == 0 || stFromUCC.ulCBId != stCC.ulSpeakCBId))
        {
            x_WarnLog("[PKT]discard. reason: stCC.ulSpeakCBId: %d, stFromUCC.ulCBId:%d.", stCC.ulSpeakCBId, stFromUCC.ulCBId);
            return XPOC_ERR_SUCCESS;
        }
        (void)Dp_RecordRtpPkt(&stFromUCC, stFromUCC.ulRcrdCCId, ptNode);
    }

    XPOC_TRACEFUNC_OUT;
    return XPOC_ERR_SUCCESS;
}

/* RTCP 消息转发到 CCE.floor module */
UINT Dp_RetransmitRtcpPkt(PktNode_S *ptNode)
{
    XPOC_TRACEFUNC_IN;
    X_NULL_POINTER_CHK_ONE_PARA(ptNode);

    UCHAR *pRtcpBuff = NULL; 
    UINT ulBufLen = 0;
    UINT ulReturn = XPOC_ERR_SUCCESS;
    RtcpCtrlMsgS RtcpCtrlMsg;

    T_MdeUCallCtxTable stFromUCC;
    T_MdeCallCtxTable stFromCC;

    ulReturn = XMdeUCallCtxOpr(XDB_OPR_QRY, NULL, 0, 0, 0, NULL, &ptNode->ulUCCId, &stFromUCC);
    if(XPOC_ERR_SUCCESS != ulReturn)
        return ulReturn;

    ulReturn = XMdeCallCtxOpr(XDB_OPR_QRY, NULL, 0, &stFromUCC.ulCallCtxId, &stFromCC);
    if(XPOC_ERR_SUCCESS != ulReturn)
        return ulReturn;

    /* 话权消息转发一份给RCE */
    if(0 != stFromUCC.ulRcrdCCId && INVALID_VALUE_UINT != stFromUCC.ulRcrdCCId)
    {
        (void)Dp_RecordRtcpPkt(&stFromUCC, stFromUCC.ulRcrdCCId, ptNode);
    }

    pRtcpBuff = ptNode->PktBuf;
    ulBufLen = ptNode->BufLen;

    memset(&RtcpCtrlMsg, 0x00, sizeof(RtcpCtrlMsgS));
    if(DP_PACKET_TYPE_APPLICATION == pRtcpBuff[1]) // 0xCallCtx:rtcp floor packet, send to FLOOR
    {
        ulReturn = RtcpxRtcpCtrlMsg(pRtcpBuff, ulBufLen, &RtcpCtrlMsg);
        if(XPOC_ERR_SUCCESS != ulReturn)
        {
            x_WarnLog("Floor RTCP initiation failed!\n");    
            return XPOC_ERR_FAIL;
        }

        if( (stFromCC.ulSpeakCBId == stFromUCC.ulCBId)
            && (D_FLOOR_RELEASE_SUBTYPE_UM == RtcpCtrlMsg.eMsgType || D_FLOOR_RELEASE_SUBTYPE_AM == RtcpCtrlMsg.eMsgType) )
        {
           stFromCC.ulSpeakCBId = 0;/* 话权方释放话权 */
        }

        stFromUCC.ulSsrc = RtcpCtrlMsg.ssrc;

        /*发送前要填充Call ID、UDN*/
        CallCtrlIdU stCallId = {0};
        XPOCCommSetCallCtrlIdU(stFromCC.ulServCCId, 0, stFromCC.servType, 0, 0, &stCallId);

        RtcpCtrlMsg.callId = stCallId.ulCtrlId;
        RtcpCtrlMsg.udn.id = 0x71;
        RtcpCtrlMsg.udn.length = strlen(stFromUCC.aucServId);
        memcpy((char*)RtcpCtrlMsg.udn.value, stFromUCC.aucServId, RtcpCtrlMsg.udn.length);
        RtcpCtrlMsg.ulName = RTCP_NAME_X;

        x_InfoLog("[FLOOR]Recv floor from [%s], type [%d], ssrc [0x%x(%u)], mde ccid [%d]", 
                stFromUCC.aucServId, RtcpCtrlMsg.eMsgType, stFromUCC.ulSsrc, stFromUCC.ulSsrc, stFromCC.ulRecordIdx);
        (void)insertPkt2RtcpSendQueue1(CCE_FLOOR_RTCP_PATH, &RtcpCtrlMsg);
    }

    XPOC_TRACEFUNC_OUT;
    return XPOC_ERR_SUCCESS;
}

UINT Dp_DispatchPkt(PktNode_S *ptNode)
{
    XPOC_TRACEFUNC_IN;
    X_NULL_POINTER_CHK_ONE_PARA(ptNode);

    if(FD_TYPE_UDP_RTP == ptNode->fdType || FD_TYPE_TCP_RTP == ptNode->fdType) /*rtp*/
    {
        Dp_DistributeRtpPkt(ptNode);
    }
    else if(FD_TYPE_UDP_RTCP == ptNode->fdType) /*rtcp*/
    {
        Dp_RetransmitRtcpPkt(ptNode);
    }

    XPOC_TRACEFUNC_OUT;
    return XPOC_ERR_SUCCESS;
}

#if 0
#endif


/*向接收队列插入新数据包*/
UINT Dp_SavePktIntoQueue(UCHAR *ptRecvBuf, SINT buffLen, MediaFdS *ptMediaFd, UINT ulSrcUCCId)
{
    XPOC_TRACEFUNC_IN;
    X_NULL_POINTER_CHK_TWO_PARA(ptRecvBuf, ptMediaFd);

    /* UDP协商包头两位是 0, 丢弃不转发. 所有头两位是0000的包都丢弃. */
    if(FD_TYPE_UDP_RTP == ptMediaFd->fdType || FD_TYPE_UDP_RTCP == ptMediaFd->fdType)
    {
        // rtcp 保活包
        USHORT usHeadPara = 0;
        UCHAR  ucHeadParaLen = 0;
        Byte_2_U16(ptRecvBuf, usHeadPara, ucHeadParaLen);/* UDP data部分头两个字节  */
        if(0 == usHeadPara)
        {
            //x_DebugLog("discard test pkt for fdType[%d].", ptMediaFd->fdType);
            return XPOC_ERR_SUCCESS;
        }
    }

    // rtp保活包,12是保活包长度
    if (FD_TYPE_UDP_RTP == ptMediaFd->fdType && 12 == buffLen)
    {
        return XPOC_ERR_SUCCESS;
    }
    
    T_ModCtxS *ptModCtx = Fplat_GetModuleCtx(XMOD_TYPE_MDE);
    MdeGlbData_S* ptGlbData = (MdeGlbData_S*)ptModCtx->pAppData;
    ThreadInfo_t *ptSendThread = NULL;
    if(FD_TYPE_UDP_RTP == ptMediaFd->fdType || FD_TYPE_UDP_RTCP == ptMediaFd->fdType)
    {
        ptSendThread = ptGlbData->stUdpAddrInfo.ptSendThread;
    }
    else if(FD_TYPE_TCP_RTP == ptMediaFd->fdType)
    {
        ptSendThread = ptGlbData->stTcpAddrInfo.ptSendThread;
    }
    if(!ptSendThread)
    {
        x_ErrorLog("fdRtp[%d] ptSendThread is NULL.\r\n", ptGlbData->stUdpAddrInfo.udpRtpFd.iSocketFd);
        return XPOC_ERR_FAIL;
    }

    PktQueue_S *ptQueue = ptSendThread->pPktQueue;
    //无锁机制保护多线程读写情况
    NodeIndex_S *pstNodeIndex = NULL;
    Rm_IdxManageGetFreeNode(&ptQueue->stIndexManageCtx, &pstNodeIndex);
    if(NULL == pstNodeIndex)
    {
        x_ErrorLog("Rm_IdxManageGetFreeNode failure. No free node to receive data.\n");
        return XPOC_ERR_INVALID_PTR;
    }
    PktNode_S *ptNode = &ptQueue->PktNode[pstNodeIndex->u32PktNodeIndex]; 

    memcpy(&ptNode->PktFrom, ptQueue, sizeof(ptNode->PktFrom));
    ptNode->BufLen = buffLen;
    memcpy(ptNode->PktBuf, ptRecvBuf, MIN(buffLen, DP_BUFFER_SIZE));
//    ptNode->pNodeData = (void*)ptMediaFd;
//    ptNode->eCodecType = (SdpCodecE)stUCC.stMediaInfo.stSdpCodec[0].enCodecName; /*not used*/
    ptNode->fdType = ptMediaFd->fdType;
    ptNode->ulUCCId = ulSrcUCCId;
    ptNode->ulPktNodeIdx = pstNodeIndex->u32PktNodeIndex;

    /* ready to send */
    Rm_IdxManageInsertToSendingList(&ptQueue->stIndexManageCtx, pstNodeIndex);

    if (ptQueue->stIndexManageCtx.stFreeList.s32Count < (g_tServerCfg.nMediaQueueSize / 10))
    {
        // 当队列使用量超过90%时,告警
        x_WarnLog("Queue high water level, total [%d], free [%d], msg type [%d]", 
                    g_tServerCfg.nMediaQueueSize, ptQueue->stIndexManageCtx.stFreeList.s32Count, ptMediaFd->fdType);
    }

    // if(FD_TYPE_TCP_RTP == ptMediaFd->fdType)
    // {
    //     x_DebugLog("[PKT][TCP][RTP]pkt recv ok.NodIdx[%d],bufflen[%d],src[FD][%d:%d].Pkt[r:%ld,s:%ld].", 
    //                ptNode->ulPktNodeIdx, buffLen, ptMediaFd->recordIdx, ptMediaFd->iSocketFd, g_TcpRcvCount, g_TcpSndCount);

    //     x_DebugLog("[PKT][TCP][RTP]pkt insrt in send queue. NodeCount:[send:%d,free:%d],Pkt[r:%ld,s:%d].", 
    //               ptQueue->stIndexManageCtx.stSendList.s32Count, ptQueue->stIndexManageCtx.stFreeList.s32Count, g_TcpRcvCount, g_TcpSndCount);
    // }
    // else if(FD_TYPE_UDP_RTP == ptMediaFd->fdType)
    // {
    //     x_DebugLog("[PKT][UDP][RTP]pkt recv ok.NodIdx[%d],bufflen[%d],src[FD][%d:%d].", 
    //                ptNode->ulPktNodeIdx, buffLen, ptMediaFd->recordIdx, ptMediaFd->iSocketFd);

    //     x_DebugLog("[PKT][UDP][RTP]pkt insrt in send queue. NodeCount:[send:%d,free:%d].", 
    //               ptQueue->stIndexManageCtx.stSendList.s32Count, ptQueue->stIndexManageCtx.stFreeList.s32Count);
    // }
    // else if(FD_TYPE_UDP_RTCP == ptMediaFd->fdType)
    // {
    //     x_DebugLog("[PKT][UDP][RTCP]pkt recv ok.NodIdx[%d],bufflen[%d],src[FD][%d:%d].", 
    //                ptNode->ulPktNodeIdx, buffLen, ptMediaFd->recordIdx, ptMediaFd->iSocketFd);

    //     x_DebugLog("[PKT][UDP][RTCP]pkt insrt in send queue. NodeCount:[send:%d,free:%d].", 
    //               ptQueue->stIndexManageCtx.stSendList.s32Count, ptQueue->stIndexManageCtx.stFreeList.s32Count);
    // }

    XPOC_TRACEFUNC_OUT;
    return buffLen;
}


/*******************************************************************************
 ** FUNCTION: Dp_HandlePktFromQueue
 *******************************************************************************
 **
 ** DESCRIPTION: This function is entry of the RTP worker threads, used to
 **              process a RTP packet data.
 **
 ******************************************************************************/
void *Dp_HandlePktFromQueue(void *ptr)
{
    XPOC_TRACEFUNC_IN;

    if(!ptr)
    {
        x_ErrorLog("input para is NULL.\n");
        return NULL;
    }

    ThreadInfo_t *ptThread = (ThreadInfo_t*)ptr;

    if(!(PROTO_UDP == ptThread->enThreadType || PROTO_TCP == ptThread->enThreadType))
    {
        x_FatalLog("Thread type[%d] is invalid, threadId[%lu].\n",ptThread->enThreadType, ptThread->ThreadId);    
        return NULL;
    }
    // x_InfoLog("[PKT] handle recv pkt, threadType[%d],threadId[%lu],queue[%p].", 
    //           ptThread->enThreadType, ptThread->ThreadId, ptThread->pPktQueue);

    PktQueue_S *ptQueue = ptThread->pPktQueue;
    NodeIndex_S *ptNodeIdx = NULL;
    PktNode_S *ptNode = NULL;

    for( ; ; )
    {
        Rm_IdxManageGetSendNode(&ptQueue->stIndexManageCtx, &ptNodeIdx);
        if(!ptNodeIdx)/* no data to send */
        {
//            x_InfoLog("[MDE][PKT] current queue is NULL, threadId[%lu],Queue[%p].", ptThread->ThreadId, ptThread->pPktQueue);//too much print
            usleep(1000);   /* FIXME: sleep 1ms is OK? */
            continue;
        }

        ptNode = &ptQueue->PktNode[ptNodeIdx->u32PktNodeIndex];

//        x_InfoLog("[MDE][PKT] handle recved pkt, Queue[%p],NodeIdx[%d],BuffLen[%d].", ptThread->pPktQueue, ptNodeIndex->u32PktNodeIndex, ptNode->BufLen);

        if(0 == ptNode->BufLen)/* NULL Packet */
        {
            x_WarnLog("node bufflen is 0.NodeIdx[%d],codecType[%d],free node.", ptNode->ulPktNodeIdx, ptNode->eCodecType);
            /* this pPktNodeIndex is free now */
            Rm_IdxManageInsertToFreeList(&ptQueue->stIndexManageCtx, ptNodeIdx);
            continue;
        }

        // if(PROTO_UDP == ptThread->enThreadType)
        // {
        //     x_DebugLog("[PKT][UDP]get from busy queue. busynum[%d],freenum[%d],recv[%ld],send[%ld]", 
        //               ptQueue->stIndexManageCtx.stSendList.s32Count, ptQueue->stIndexManageCtx.stFreeList.s32Count,
        //               g_TcpRcvCount, g_TcpSndCount);
        // }
        // else if(PROTO_TCP == ptThread->enThreadType)
        // {
        //     x_DebugLog("[PKT][TCP][RTP]pkt get from busy queue. NodeCount[send:%d,free:%d],Pkt[r:%ld,s:%d].", 
        //               ptQueue->stIndexManageCtx.stSendList.s32Count, ptQueue->stIndexManageCtx.stFreeList.s32Count, g_TcpRcvCount, g_TcpSndCount);
        // }

        (void)Dp_DispatchPkt(ptNode);

        /* this pPktNodeIndex is free now */
        ptNode->ulPktNodeIdx = 0;
        Rm_IdxManageInsertToFreeList(&ptQueue->stIndexManageCtx, ptNodeIdx);
    }

    XPOC_TRACEFUNC_OUT;
}


void *Dp_RecvUdpPacket(void *ptr)
{  
    XPOC_TRACEFUNC_IN;
    (void)ptr;

    UINT ulReturn = XPOC_ERR_SUCCESS;
    UCHAR recvbuff[DP_BUFFER_SIZE];
    MediaFdS *ptMediaFd = NULL;

    struct sockaddr_in cliaddr;
    socklen_t cliaddr_len = sizeof(struct sockaddr_in);

    struct epoll_event rcvevent[MAX_RTP_SD_NUM];

    T_ModCtxS *ptModCtx = Fplat_GetModuleCtx(XMOD_TYPE_MDE);
    MdeGlbData_S* ptGlbData = (MdeGlbData_S*)ptModCtx->pAppData;

    /* get the thread info */
    for ( ; ; )
    {
        int fdnum = epoll_wait(ptGlbData->stUdpAddrInfo.udpEpollFd, rcvevent, MAX_RTP_SD_NUM, -1);
        if(fdnum < 0)
        {
//            x_WarnLog("invalid fdnum[%d]", fdnum);
            continue;
        }

        int i = 0;
        for(i = 0; i < fdnum; i++)
        {
            if(!(rcvevent[i].events & EPOLLIN))
                continue;

            ptMediaFd = (MediaFdS*)rcvevent[i].data.ptr;
            if(!ptMediaFd)
            {
                x_WarnLog("ptUdpFd is NULL. fdnum[%d].\n", fdnum);
                continue;
            }

            SINT recvlen = recvfrom(ptMediaFd->iSocketFd, recvbuff, DP_BUFFER_SIZE, 0, (struct sockaddr *)&cliaddr, &cliaddr_len);
            if(recvlen <= 0)
            {
                x_WarnLog("Call recvfrom() failure. SockFd=%d. RecvLen=%d.\n", ptMediaFd->iSocketFd, recvlen);
                //abort();
                continue;
            }

            UINT ulUCCId = 0;
            ulReturn = mdeSaveClientAddr2UCC(recvbuff, &cliaddr, ptMediaFd, &ulUCCId);
            if(XPOC_ERR_SUCCESS != ulReturn)
            {
                continue;
            }

            (void)Dp_SavePktIntoQueue(recvbuff, recvlen, ptMediaFd, ulUCCId);
        }
    }

    XPOC_TRACEFUNC_OUT;
    return NULL;
}


void *Dp_RecvTcpPacket(void *ptr)
{  
    XPOC_TRACEFUNC_IN;
    (void)ptr;

    UINT ulReturn = XPOC_ERR_SUCCESS;
    UINT ulUCCId = 0;

    UCHAR recvbuff[DP_BUFFER_SIZE];
    char *ptStrIp = NULL;
    USHORT usPort = 0;

    struct sockaddr_in cliaddr;
    socklen_t cliaddr_len = sizeof(struct sockaddr_in);
    struct epoll_event rcvevent[MAX_RTP_SD_NUM];

    T_ModCtxS *ptModCtx = Fplat_GetModuleCtx(XMOD_TYPE_MDE);
    MdeGlbData_S* ptGlbData = (MdeGlbData_S*)ptModCtx->pAppData;

    /* 从实际效果上看, 网卡上抓到包了,但是server貌似没有收到。考虑是socket的建立和使用在不同线程导致. 
       因此把 socket 建立和连接放在同一个线程中 */
    mdeInitTcpSocket(ptModCtx);

    /* get the thread info */
    int i = 0;
    for ( ; ; )
    {
        int fdnum = epoll_wait(ptGlbData->stTcpAddrInfo.tcpEpollFd, rcvevent, MAX_RTP_SD_NUM, -1);
        if(fdnum <= 0)
        {
//            x_WarnLog("invalid fdnum[%d]", fdnum);
            continue;
        }

        for(i = 0; i < fdnum; i++)
        {
            memset(recvbuff, 0, DP_BUFFER_SIZE);

            MediaFdS *ptMediaFd = (MediaFdS *)rcvevent[i].data.ptr;
            if(!ptMediaFd)
            {
                x_InfoLog("ptTcpFd is NULL. fdnum[%d].\n", fdnum);
                continue;
            }

            int sockfd = ptMediaFd->iSocketFd;
            if(sockfd == ptGlbData->stTcpAddrInfo.tcpListenFd.iSocketFd)/* pkt 来自listenFd, 说明当前是connfd, 执行accept过程 */
            {
                memset(&cliaddr, 0, sizeof(struct sockaddr_in));
                int connfd = accept(sockfd, (struct sockaddr *)&cliaddr, &cliaddr_len);

                /* set NONBLOCK */  
                int flags = fcntl(connfd, F_GETFL, 0);  
                fcntl(connfd, F_SETFL, flags | O_NONBLOCK);  

                ptStrIp = inet_ntoa(cliaddr.sin_addr);
                usPort = ntohs(cliaddr.sin_port);

                MediaFdS *ptTcpRtpFd = NULL;
                if (XPOC_ERR_SUCCESS != mdeAllocTcpFd(ptModCtx, &ptTcpRtpFd))
                {
                    close(connfd);
                    x_WarnLog("[TV][X][FD]Alloc TcpFd failed, tcpFdNum[%d], connfd[%d], client[%s:%d].", 
                            ptGlbData->stTcpAddrInfo.tcpFdNum, connfd, ptStrIp, usPort);
                    continue;
                }
                ptTcpRtpFd->usPort = ptGlbData->stTcpAddrInfo.tcpListenFd.usPort;
                ptTcpRtpFd->iSocketFd = connfd;

                x_InfoLog("[VIDEO][RTP][TCP]listenfd[%d] is triggered, connfd[%d] is accepted from client[%s:%d].\n", 
                        sockfd, connfd, ptStrIp, usPort);

                /* TCP连接作为服务端只有一个端口29000, client连接上来时还无法区分具体用户。
                    需要等待后续的协商包中携带ssrc来确认该连接与用户的对应关系。
                    当然一般情况下,可以根据用户发起连接的client_ip+port 来区分用户.但由于公网穿越时可能会改变client端的ip+port。
                    因此此时还不能确认client端的用户, 等后续协商包中的ssrc来区分用户.
                */
                /* TCP Media的client address, 本应该保存在UCC中, 但此时还没有确定具体的UCC, 因此先保存在 TcpRtpFd中. 
                   后续 UCC 根据usTcpRtpFdIdx确定 tcpRtpFd来找到具体的client address. 
                   在 recv(...)/recvfrom(...)时有可能得不到准确的client addr. */
                ptTcpRtpFd->ulCliIp = ntohl(cliaddr.sin_addr.s_addr);
                ptTcpRtpFd->rtpCliPort = ntohs(cliaddr.sin_port);

                F_epollAddFd(ptGlbData->stTcpAddrInfo.tcpEpollFd, connfd, (void*)ptTcpRtpFd, "connfd");
            }
            else
            {
                if(!(rcvevent[i].events & EPOLLIN))
                    continue;

                SINT recvlen = recv(sockfd, recvbuff, DP_BUFFER_SIZE, 0);
                if(0 == recvlen) /* client has closed this connect. server should close this connect too. */
                {
//                    x_InfoLog("Call recvfrom() failure. SockFd=%d. RecvLen=%d.\n", sockfd, recvlen);
                    //abort();
                    UCHAR ubUsedFg = FALSE;
                    pthread_mutex_lock(&ptMediaFd->mtxSocket);
                    if (INVALID_VALUE_UINT != ptMediaFd->iSocketFd)
                    {
                        (void)F_epollDelFd(ptGlbData->stTcpAddrInfo.tcpEpollFd, ptMediaFd->iSocketFd, "tcprtpfd");
                        close(ptMediaFd->iSocketFd);
                        ptMediaFd->iSocketFd = INVALID_VALUE_UINT;
                        
                        ubUsedFg = ptGlbData->stTcpAddrInfo.tcpRtpFdList[ptMediaFd->recordIdx].ubUsedFg;
                        if(ubUsedFg)
                        {
                            ptGlbData->stTcpAddrInfo.tcpRtpFdList[ptMediaFd->recordIdx].iSocketFd = INVALID_VALUE_UINT;
                            ptGlbData->stTcpAddrInfo.tcpRtpFdList[ptMediaFd->recordIdx].ulUCCId  = 0;
                            ptGlbData->stTcpAddrInfo.tcpRtpFdList[ptMediaFd->recordIdx].ulCliIp = 0;
                            ptGlbData->stTcpAddrInfo.tcpRtpFdList[ptMediaFd->recordIdx].rtpCliPort  = 0;
                            ptGlbData->stTcpAddrInfo.tcpRtpFdList[ptMediaFd->recordIdx].rtcpCliPort = 0;
                            ptGlbData->stTcpAddrInfo.tcpRtpFdList[ptMediaFd->recordIdx].ubUsedFg = FALSE;
                            ptGlbData->stTcpAddrInfo.tcpFdNum --;
                        }
                    }
                    pthread_mutex_unlock(&ptMediaFd->mtxSocket);
                    x_InfoLog("[X]close tcp socket. tcpRtpFdList[%d]:tcprtpfd[%d], ubUsedFg[%d], tcpFdNum[%d].\n", 
                        ptMediaFd->recordIdx, sockfd, ubUsedFg, ptGlbData->stTcpAddrInfo.tcpFdNum);
                    continue;
                }
                else if(recvlen < 0) /* 此时可能是 暂无数据可读 || 被中断 || 数据读错误. 统一处理为等待下一次读 */
                {
                    x_WarnLog("Call recvfrom() failure. SockFd=%d. RecvLen=%d.\n", sockfd, recvlen);
                    continue;
                }

                if(g_TcpRcvCount >= INVALID_VALUE_UINT)
                    g_TcpRcvCount = 1;
                else
                    g_TcpRcvCount++;

                if(0 == ptMediaFd->ulUCCId || INVALID_VALUE_UINT == ptMediaFd->ulUCCId)
                {
                    /* decode RTP for ssrc. get UCC by ssrc. */
                    UINT ulSsrc = 0;
                    UCHAR  ucSsrcLen = 0;
                    /* 8->10  TCP data部分增加了长度位, 偏移多两个字节.
                       20210313: 为支持横竖屏切换, 终端发来的rtp报文头部又扩展了7bytes. */
//                    Byte_2_U32(recvbuff+10+7, ulSsrc, ucSsrcLen);
                    Byte_2_U32(recvbuff+10, ulSsrc, ucSsrcLen); //for test

//                    ptStrIp = inet_ntoa(cliaddr.sin_addr);
//                    usPort = ntohs(cliaddr.sin_port);
                    x_InfoLog("[TV]recv buff from sockfd[%d].ssrc[0x%x--%d],recvlen[%d].", sockfd, ulSsrc, ulSsrc, recvlen);

                    T_MdeUCallCtxTable stUCallCtx;
                    memset(&stUCallCtx, 0, sizeof(T_MdeUCallCtxTable));
                    ulReturn = XMdeUCallCtxOpr(XDB_OPR_QRY, NULL, 0, 0, 0, &ulSsrc, NULL, &stUCallCtx);
                    if(XPOC_ERR_SUCCESS != ulReturn)
                    {
                        continue;
                    }
                    stUCallCtx.usTcpRtpFdIdx = ptMediaFd->recordIdx;
                    stUCallCtx.stMediaInfo.ulIpAddr = ptMediaFd->ulCliIp;
                    stUCallCtx.stMediaInfo.rtpPort = ptMediaFd->rtpCliPort;
                    stUCallCtx.stMediaInfo.rtcpPort = ptMediaFd->rtcpCliPort;
                    (void)XMdeUCallCtxOpr(XDB_OPR_MOD, NULL, 0, 0, 0, NULL, &stUCallCtx.ulRecordIdx, &stUCallCtx);

                    /* save UCCId 2 tcp mediafd. ptMediaFd == ptGlbData->stTcpAddrInfo.tcpRtpFdList[ptMediaFd->recordIdx] */
                    ptMediaFd->ulUCCId = stUCallCtx.ulRecordIdx;
                }
                ulUCCId = ptMediaFd->ulUCCId;

                ulReturn = mdeSaveClientAddr2UCC(recvbuff, &cliaddr, ptMediaFd, &ulUCCId);
                if(XPOC_ERR_SUCCESS != ulReturn)
                {
                    continue;
                }

                /* 对于tcp, 此时的cliaddr 没有意义. ---对于RCE有意义 */
                (void)Dp_SavePktIntoQueue(recvbuff, recvlen, ptMediaFd, ulUCCId);
            }
        }
    }

    XPOC_TRACEFUNC_OUT;
    return NULL;
}


#if 0
#endif

/*
//     该函数测试 : 使用epoll 同时控制 tcp/udp 的消息接收
//*/
//int testTcpUdpSocketInEpoll(void)
//{
//#define MAXLEN          1024
//#define MAX_OPEN_FD     1024
//#define SERV_UDP_PORT       29010
//#define SERV_TCP_PORT       29020
//    
//    int  listenfd, connfd, udpfd, epfd, ret, i, bufflen;
//    char buf[MAXLEN];
//    struct sockaddr_in cliaddr, servaddr;
//    socklen_t clilen = sizeof(cliaddr);
//    struct epoll_event tmpevent,epevents[MAX_OPEN_FD];
//    int opt = 1;

//    // create tcp listen socket
//    listenfd = socket(AF_INET, SOCK_STREAM, 0);
//    setsockopt( listenfd, SOL_SOCKET, SO_REUSEADDR, (const void *)&opt, sizeof(opt) ); 

//    servaddr.sin_family = AF_INET;
//    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
//    servaddr.sin_port = htons(SERV_TCP_PORT);
//    bind(listenfd,(struct sockaddr*)&servaddr,sizeof(servaddr));
//    listen(listenfd, 20);

//    // create udp socket
//    udpfd = socket(AF_INET, SOCK_DGRAM, 0);
//    servaddr.sin_family = AF_INET;
//    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
//    servaddr.sin_port = htons(SERV_UDP_PORT);
//    setsockopt( udpfd, SOL_SOCKET, SO_REUSEADDR, (const void *)&opt, sizeof(opt) ); 
//    bind(udpfd,(struct sockaddr*)&servaddr,sizeof(servaddr));


//    // create epoll fd
//    epfd = epoll_create(MAX_OPEN_FD);

//    // add listenfd, udpfd to efd
//    tmpevent.events = EPOLLIN;
//    tmpevent.data.fd = listenfd;
//    ret = epoll_ctl(epfd, EPOLL_CTL_ADD, listenfd, &tmpevent);

//    tmpevent.events = EPOLLIN;
//    tmpevent.data.fd = udpfd;
//    ret = epoll_ctl(epfd, EPOLL_CTL_ADD, udpfd, &tmpevent);


//    // 循环等待
//    for (;;)
//    {
//        // 返回已就绪的epoll_event,-1表示阻塞,没有就绪的epoll_event,将一直等待
//        int nready = epoll_wait(epfd, epevents, MAX_OPEN_FD, -1);
//        for ( i = 0; i < nready; ++i)
//        {
//            int sockfd = epevents[i].data.fd;

//            // 如果是新的连接,需要把新的socket添加到efd中
//            if(sockfd == listenfd)
//            {
//                connfd = accept(listenfd,(struct sockaddr*)&cliaddr,&clilen);
//                tmpevent.events = EPOLLIN;
//                tmpevent.data.fd = connfd;
//                ret = epoll_ctl(epfd, EPOLL_CTL_ADD, connfd, &tmpevent);
//                printf("listenfd[%d], connfd[%d].\n", listenfd, connfd);
//            }
//            // 否则,读取数据
//            else if(sockfd == udpfd) // recvfrom udp socket
//            {
//                memset(buf, 0, MAXLEN);
//                bufflen = recvfrom(udpfd, buf, MAXLEN, 0, (struct sockaddr *)&cliaddr, &clilen);
//                printf("recv from udp client: len[%d],buf[%s]\n", bufflen, buf);
//            }
//            else if(sockfd == connfd)// recvfrom tcp socket
//            {
//                memset(buf, 0, MAXLEN);
//                bufflen = recvfrom(sockfd, buf, MAXLEN, 0, (struct sockaddr *)&cliaddr, &clilen);
//                printf("recv from tcp client: len[%d],buf[%s]\n", bufflen, buf);

//                // 客户端关闭连接
//                if(bufflen == 0)
//                {
//                    ret =epoll_ctl(epfd, EPOLL_CTL_DEL, connfd, NULL);
//                    close(connfd);
//                    printf("client[%d] closed\n", i);
//                }
//                else
//                {
//                    for ( j = 0; j < bytes; ++j)
//                    {
//                        buf[j] = toupper(buf[j]);
//                    }
//                    // 向客户端发送数据
//                    sendto(connfd, buf, bufflen, 0, (struct sockaddr*)&cliaddr, clilen);
//                    printf("send to client: %s\n", buf);
//                }
//            }
//        }
//    }

//    return ret;
//}




mdeMrProc.c


#include "XpocCommDefine.h"
#include "XpocCommFunc.h"
#include "XpocCommMsgStruct.h"

#include "rtcp_mod_encode.h"
#include "rtcp_mod_decode.h"
#include "rtcp_mod_proc.h"

#include "mdeMrCommFunc.h"
#include "mdeMrResourceFunc.h"
#include "mdeMrMediaFunc.h"
#include "mdeMrSignalFunc.h"
#include "mdeMrProc.h"

#include "ConfFuncApi.h"

UINT g_UdpRcvCount = 0;
UINT g_UdpSndCount = 0;
UINT g_TcpRcvCount = 0;
UINT g_TcpSndCount = 0;

extern MYSQL *mysql_connect;
extern T_ModuleCBCtx g_MdeCBCtxSTable[];
extern char glb_neLocIp[STR_LEN32];
extern char glb_UdeIp[STR_LEN32];
extern char glb_AceRmtIp[STR_LEN32];
extern char glb_AceRmtPsi[STR_LEN64];

extern T_TableCfg  g_tTableCfg;
extern T_ServerCfg g_tServerCfg;

extern UINT glb_MdeRmtVideoPort;  
extern UINT glb_MdeRmtRtpPort;    
extern UINT glb_MdeRmtRtcpPort;    

pthread_rwlock_t  g_lockRtcpKeepalive;
MediaKeepaliveInfoList g_RtcpKeepaliveList;

pthread_rwlock_t  g_lockRtpKeepalive;
MediaKeepaliveInfoList g_RtpKeepaliveList;

static UINT mdeLoadCfg(T_ModCtxS *ptModCtx);
static void mdeHandleRecvFloorMsg(RtcpCtrlMsgS *pRtcpCtrlMsg, void *pUserData);

static UINT mdeLoadCfg(T_ModCtxS *ptModCtx)
{
    XPOC_TRACEFUNC_IN;
    X_NULL_POINTER_CHK_ONE_PARA(ptModCtx);

    MdeGlbData_S* ptGlbData = (MdeGlbData_S*)ptModCtx->pAppData;
    MdeCfgInfoS *ptCfg = &ptGlbData->stCfgInfo;

    ptCfg->rtpWorkerThreadNum = 2;
    ptCfg->udpRtpPort   = 29010;
    ptCfg->udpRtcpPort  = 29011;
    ptCfg->tcpPort      = 29000;

    ptCfg->udpRmtRtpPort  = glb_MdeRmtRtpPort;
    ptCfg->udpRmtRtcpPort = glb_MdeRmtRtcpPort;
    ptCfg->tcpRmtPort     = glb_MdeRmtVideoPort;

    x_InfoLog("mdeCfgInfo:threadNum[%d],udpRtpPort[%d],udpRtcpPort[%d],tcpPort[%d].",
              ptCfg->rtpWorkerThreadNum, ptCfg->udpRtpPort, ptCfg->udpRtcpPort, ptCfg->tcpPort);

    ptCfg->ulLocalIp = ntohl(inet_addr(glb_neLocIp));
    ptCfg->ulAceRmtIp = ntohl(inet_addr(glb_AceRmtIp));

    x_InfoLog("mdeCfgInfo:mde[%s--0x%x],ude[%s],acermt[%s--0x%x],rmtpsi[%s].\n", 
              glb_neLocIp, ptCfg->ulLocalIp, glb_UdeIp, glb_AceRmtIp, ptCfg->ulAceRmtIp, glb_AceRmtPsi);

    XPOC_TRACEFUNC_OUT;
    return XPOC_ERR_SUCCESS;
}


/* mde网元接收到来自CCE的rtcp消息, 直接放入UCallCtx发送队列,发送给具体的终端 */
static void mdeHandleRecvFloorMsg(RtcpCtrlMsgS *pRtcpCtrlMsg, void *pUserData)
{
    XPOC_TRACEFUNC_IN;

    (void)pUserData;

    UINT ulReturn = XPOC_ERR_SUCCESS;
    T_MdeCallCtxTable stCallCtx;
    T_MdeUCallCtxTable stUCallCtx;
    CallCtrlIdU stCallId = {0};

    memset(&stCallCtx, 0, sizeof(T_MdeCallCtxTable));
    memset(&stUCallCtx, 0, sizeof(T_MdeUCallCtxTable));

    if(0 == pRtcpCtrlMsg->callId || 0 == pRtcpCtrlMsg->udn.length)
    {
        x_WarnLog("recv invalid rtcp floor msg, callid[0x%x],udnlen[%d].\n", pRtcpCtrlMsg->callId, pRtcpCtrlMsg->udn.length);
        return;
    }

    stCallId.ulCtrlId = pRtcpCtrlMsg->callId;
    UINT ulServCCId = 0;
    UCHAR servType = 0;
    XPOCCommGetCallCtrlIdU(&stCallId, &ulServCCId, NULL, &servType, NULL, NULL);

    T_ModCtxS *ptModCtx = Fplat_GetModuleCtx(XMOD_TYPE_MDE);
    MdeGlbData_S* ptGlbData = (MdeGlbData_S*)ptModCtx->pAppData;

    ulReturn = XMdeCallCtxOpr(XDB_OPR_QRY, &ulServCCId, servType, NULL, &stCallCtx);
    if(XPOC_ERR_SUCCESS != ulReturn)
    {
        x_WarnLog("Send floor fail, cce ccid[%d],servType[%d].\n", ptModCtx, ptGlbData, ulServCCId, servType);
        return;
    }
    
    ulReturn = XMdeUCallCtxOpr(XDB_OPR_QRY, (char*)pRtcpCtrlMsg->udn.value, ulServCCId, MEDIA_AUDIO, servType, NULL, NULL, &stUCallCtx);
    if(XPOC_ERR_SUCCESS != ulReturn)
    {
        x_WarnLog("Send floor to [%s] fail, cce ccid[%d],servType[%d].\n", (char*)pRtcpCtrlMsg->udn.value, ulServCCId, servType);
        return;
    }

    if(D_FLOOR_GRANTED_SUBTYPE_UM == pRtcpCtrlMsg->eMsgType || D_FLOOR_GRANTED_SUBTYPE_AM == pRtcpCtrlMsg->eMsgType)
    {
        stCallCtx.ulSpeakCBId = stUCallCtx.ulCBId;//记录话权方
    }
    else if(D_FLOOR_IDLE_SUBTYPE_UM == pRtcpCtrlMsg->eMsgType || D_FLOOR_IDLE_SUBTYPE_AM == pRtcpCtrlMsg->eMsgType)
    {
        stCallCtx.ulSpeakCBId = 0; //话权空闲清空话权方
    }

    ///
    ulReturn = XMdeCallCtxOpr(XDB_OPR_MOD, NULL, 0, &stCallCtx.ulRecordIdx, &stCallCtx);
    if(XPOC_ERR_SUCCESS != ulReturn)
    {
        // 转发语音流时,对stCallCtx.ulSpeakCBId做了判定,此处出错一次后,可能会导致在一个话权时长内,无法转发语音流的问题
        x_ErrorLog("Mod call ctx fail, ctx id [%d], audio maybe exception.", stCallCtx.ulRecordIdx);
        
        return;
    }

    struct sockaddr_in  addrTo;
    memset(&addrTo, 0, sizeof(struct sockaddr_in));
    addrTo.sin_family = AF_INET;
    /*send client rtcp port(audio port +1) by liuqun*/
    addrTo.sin_port = htons(stUCallCtx.stMediaInfo.rtcpPort);  
    addrTo.sin_addr.s_addr = htonl(stUCallCtx.stMediaInfo.ulIpAddr); 

    char *ptStrIp = inet_ntoa(addrTo.sin_addr);
    USHORT usPort = ntohs(addrTo.sin_port);

    /*非预建立回话相关,收到CF来的话权指示, 向PX发送,将RTCP Name改成RTCP_NAME_CX,由PX转发至终端*/
    if(RTCP_NAME_TCF == pRtcpCtrlMsg->ulName)
    {
        pRtcpCtrlMsg->ulName = RTCP_NAME_MCPT;
    } 

    pRtcpCtrlMsg->ssrc = stUCallCtx.ulSsrc;
    UCHAR aucRtcpPacket[RTCP_PACK_DATA_LEN] = {0};
    UINT ulRtcpDataLen = sizeof(aucRtcpPacket);
    if(XPOC_ERR_SUCCESS != RtcpCtrlMsg2RtcpByte(pRtcpCtrlMsg, aucRtcpPacket, &ulRtcpDataLen))
    {
        x_WarnLog("FloorRtcpCtrlMsgW failed!!!ssrc=[0x%x(%u)],MsgType=[%d],callid=[%d],udn=[%s]\n",
                    pRtcpCtrlMsg->ssrc,pRtcpCtrlMsg->ssrc,pRtcpCtrlMsg->eMsgType, stUCallCtx.ulServCCId, stUCallCtx.aucServId);
        return;
    }

    //公网穿越导致server发出的话权消息终端收不到,暂定发两次.
    UCHAR i = 0;
    SINT ret = -1;
    SINT iRtcpFd = ptGlbData->stUdpAddrInfo.udpRtcpFd.iSocketFd;
    
    x_InfoLog("[FLOOR] will send floor to [%s]-[%d], ssrc [0x%x(%u)], addr [%s:%d], cce ccid [%d], mde ccid [%d], ucc id [%d].", 
                pRtcpCtrlMsg->udn.value, pRtcpCtrlMsg->eMsgType, stUCallCtx.ulSsrc, stUCallCtx.ulSsrc, ptStrIp, usPort, 
                stUCallCtx.ulServCCId, stCallCtx.ulRecordIdx, stUCallCtx.ulRecordIdx);

    for(i=0; i<2; i++)
    {
        ret = sendto(iRtcpFd, aucRtcpPacket, ulRtcpDataLen, 0, &addrTo, sizeof(struct sockaddr_in));
        if(ret <= 0)
        {
            x_WarnLog("[FLOOR] send floor to [%d]-[%s]-[%s:%d] failed, errno [%d]-[%s], floor type [%d], cce ccid [%d], mde ccid [%d], ucc id [%d].", 
                    i, pRtcpCtrlMsg->udn.value, ptStrIp, usPort, errno, strerror(errno), pRtcpCtrlMsg->eMsgType, 
                    stUCallCtx.ulServCCId, stCallCtx.ulRecordIdx, stUCallCtx.ulRecordIdx);
        }
    }

    /* 话权消息转发一份给RCE */
    if(0 != stUCallCtx.ulRcrdCCId && INVALID_VALUE_UINT != stUCallCtx.ulRcrdCCId)
    {
        PktNode_S stPktNode;
        memset(&stPktNode, 0, sizeof(PktNode_S));
        stPktNode.BufLen = ulRtcpDataLen;
        memcpy(&stPktNode.PktBuf, aucRtcpPacket, stPktNode.BufLen);
        
        (void)Dp_RecordRtcpPkt(&stUCallCtx, stUCallCtx.ulRcrdCCId, &stPktNode);
    }

    XPOC_TRACEFUNC_OUT;
    return;
}

static UINT mdeGetCBConfig(T_ModCtxS *ptModCtx)
{
    XPOC_TRACEFUNC_IN;
    X_NULL_POINTER_CHK_ONE_PARA(ptModCtx);

    UINT ulReturn = XPOC_ERR_SUCCESS;

    g_MdeCBCtxSTable[X_CB_TRANSACTION].ulMaxCtrNum        = g_tServerCfg.uConcurrCallMaxNum * 2;
    g_MdeCBCtxSTable[X_CB_TRANSACTION].ulMinBufInPool     = g_tServerCfg.uConcurrCallMaxNum * 2;
    g_MdeCBCtxSTable[X_CB_TRANSACTION].ulMaxBufInPool     = g_tServerCfg.uConcurrCallMaxNum * 2;
    g_MdeCBCtxSTable[X_CB_TRANSACTION].ulExtStepBufInPool = 200;

    ulReturn = XPOCCBMemoryCreate(ptModCtx, 
                                    X_CB_TRANSACTION,
                                    g_MdeCBCtxSTable[X_CB_TRANSACTION].ulMaxBufInPool,
                                    sizeof(MdeCtrlBlockS), &ulReturn);
    if(FAILURE == ulReturn)
    {
        x_ErrorLog("Create Transaction CB Memory Error.\n");
        ulReturn = XPOC_ERR_ALLOC_CB;
    }

    XPOC_TRACEFUNC_OUT;
    return ulReturn;
}

#if 0
#endif

UINT mdeModFirstStart(void *pMsg)
{
    XPOC_TRACEFUNC_IN;

    T_ModCtxS* ptModCtx = (T_ModCtxS*)pMsg;

    MdeGlbData_S* ptGlbData = (MdeGlbData_S*)Fplat_Malloc(sizeof(MdeGlbData_S), 0);
    if(NULL == ptGlbData)
    {
        x_WarnLog("SP module global data alloc failed.\n");
        return XPOC_ERR_FAIL;
    }
    memset(ptGlbData, 0, sizeof(MdeGlbData_S));
    ptModCtx->pAppData = (void *)ptGlbData;

    /* connfd 有可能分配的是0, 会造成消息转发到非预期的fd上. 因此fd不能初始化为0. */
    ptGlbData->stUdpAddrInfo.udpRtpFd.iSocketFd = INVALID_VALUE_UINT;
    ptGlbData->stUdpAddrInfo.udpRtcpFd.iSocketFd = INVALID_VALUE_UINT;

    UINT i = 0;
    for(i=0; i<MAX_RTP_SOCKET_NUM; i++)
    {
        ptGlbData->stTcpAddrInfo.tcpRtpFdList[i].iSocketFd = INVALID_VALUE_UINT;
        pthread_mutex_init(&ptGlbData->stTcpAddrInfo.tcpRtpFdList[i].mtxSocket, NULL);
    }
    ptGlbData->stTcpAddrInfo.tcpListenFd.iSocketFd = INVALID_VALUE_UINT;

    // keepavlive缓存消息相关
    pthread_rwlock_init(&g_lockRtcpKeepalive, NULL);
    memset(&g_RtcpKeepaliveList, 0, sizeof(MediaKeepaliveInfoList));

    pthread_rwlock_init(&g_lockRtpKeepalive, NULL);
    memset(&g_RtpKeepaliveList, 0, sizeof(MediaKeepaliveInfoList));

    XPOC_TRACEFUNC_OUT;
    return XPOC_ERR_SUCCESS;
}

UINT mdeModFinishStart(void *ptModuleCtx)
{
    XPOC_TRACEFUNC_IN;
    X_NULL_POINTER_CHK_ONE_PARA(ptModuleCtx);

    T_ModCtxS *ptModCtx = (T_ModCtxS*)ptModuleCtx;

    if(XPOC_ERR_SUCCESS != mdeLoadCfg(ptModCtx))
    {
        x_WarnLog("mdeLoadCfg failed.\n");
        return XPOC_ERR_FAIL;
    }

    if(XPOC_ERR_SUCCESS != mdeGetCBConfig(ptModCtx))
    {
        x_WarnLog("mdeGetCBConfig failed.\n");
        return XPOC_ERR_FAIL;
    }

    if(XPOC_ERR_SUCCESS != mdeInitMediaSocket(ptModCtx))
    {
        x_ErrorLog("mdeInitMediaSocket failed.\n");
        return XPOC_ERR_FAIL;
    }

    if(XPOC_ERR_SUCCESS != mdeInitDpThread(ptModCtx))
    {
        x_WarnLog("mdeInitDpThread failed!\n");
        return XPOC_ERR_FAIL;
    }

    RtcpIpAddrSt stRtcpLocAddr;
    memset(&stRtcpLocAddr, 0, sizeof(RtcpIpAddrSt));
    stRtcpLocAddr.ulIpInt = ntohl(inet_addr(glb_neLocIp));
    stRtcpLocAddr.usPort = MDE_FLOOR_RTCP_PORT;
    strcpy(stRtcpLocAddr.acLocalPath, MDE_FLOOR_RTCP_PATH);
    strcpy(stRtcpLocAddr.acEndPointPath, CCE_FLOOR_RTCP_PATH);
    //if(XPOC_ERR_SUCCESS != rtcpInitFloorThread(&stRtcpLocAddr, mdeHandleRecvFloorMsg, NULL))
    if(XPOC_ERR_SUCCESS != rtcpInitFloorThread1(&stRtcpLocAddr, mdeHandleRecvFloorMsg, NULL))
    {
        x_WarnLog("Rtcp init failed!\n");
        return XPOC_ERR_FAIL;
    }

    T_ProcTimeCtx* ptTimerCtxt = (T_ProcTimeCtx*)malloc(sizeof(T_ProcTimeCtx));
    ptTimerCtxt->TimerType = TIMER_CLEAR_UDP_KEEPALIVE;
    XPOCStartTimer(CLEAR_UDP_KEEPALIVE_TM_INTERVAL * 60, 0, ptTimerCtxt, TIME_FLAG_POOL); // 5分钟检查一次 

    // added by shm, 发送上线通知短信
    // SdsWarnning stSds;
    // memset(&stSds, 0, sizeof(stSds));
    // strcpy(stSds.acParam1, "AR_XPOC_SERVER");
    // strcpy(stSds.acParam2, g_ModuleName[ptModCtx->ucModuleId]);
    // sprintf(stSds.acParam3, "Online notification -- mde has started.");
    // sedSdsWarnReq2Http(&stSds);

    XPOC_TRACEFUNC_OUT;
    return XPOC_ERR_SUCCESS;
}

UINT mdeModMsgProc(T_ModCtxS* ptModCtx, T_PlatHead* ptPlatHdr, T_ModMsgHead* ptXPOCHeader)
{
    UINT ulRetCode = XPOC_ERR_SUCCESS;

    XPOC_TRACEFUNC_IN;
    X_NULL_POINTER_CHK_THREE_PARA(ptModCtx, ptPlatHdr, ptXPOCHeader);

    if(EV_SIP_INVITE_IND == ptXPOCHeader->ulMsgId)
    {
        ulRetCode = mdeHandleSipInvite(ptModCtx,ptXPOCHeader);
    }
    else if(EV_SIP_REINVITE_IND == ptXPOCHeader->ulMsgId)
    {
        ulRetCode = mdeHandleSipReInvite(ptModCtx,ptXPOCHeader);
    }
    else if(EV_SIP_BYE_IND == ptXPOCHeader->ulMsgId)
    {
        ulRetCode = mdeHandleSipBye(ptModCtx,ptXPOCHeader);
    }
    else if(EV_SIP_ACK_IND == ptXPOCHeader->ulMsgId)
    {
        ;//ulRetCode = mdeHandleSipAck(ptModCtx,ptXPOCHeader);
    }
    else
    {
        x_WarnLog("recv unexpected msgId[0x%x].\n",ptXPOCHeader->ulMsgId);
    }

    /// free osip ev
    if(EV_SIP_INVITE_IND == ptXPOCHeader->ulMsgId
        || EV_SIP_REINVITE_IND == ptXPOCHeader->ulMsgId
        || EV_SIP_BYE_IND == ptXPOCHeader->ulMsgId
        || EV_SIP_ACK_IND == ptXPOCHeader->ulMsgId)
    {
        SipMsgIndS *ptSipMsgInd = (SipMsgIndS*)ptXPOCHeader->stMsgBody;
        if (ptSipMsgInd)
        {
            eXosip_event_free(ptSipMsgInd->ulSipHandle);
        }
    }
    
    XPOC_TRACEFUNC_OUT;
    return ulRetCode;
}

UINT mdeModTimerProc(void *contextdata, void *timerdata)
{
    XPOC_TRACEFUNC_IN;
    X_NULL_POINTER_CHK_TWO_PARA(timerdata, contextdata);

    T_ProcTimeCtx *pTimerCtx = (T_ProcTimeCtx*)timerdata;

    switch (pTimerCtx->TimerType)
    {
        case TIMER_EVENT_NULL:  // 事件与超时同时发生时会走这里
            break;
        case TIMER_CLEAR_UDP_KEEPALIVE:
            clearMediaKeepaliveInfo();
        break;
        default:
            x_InfoLog("unexpected TimeType[%d].\n",pTimerCtx->TimerType);
            break;
    }

    XPOC_TRACEFUNC_OUT;
    return XPOC_ERR_SUCCESS;
}

mdeMrResourceFunc.c

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <netinet/tcp.h>  /* for TCP_XXX defines */

#include "XpocSocketFunc.h"

#include "mdeMrCommFunc.h"
#include "mdeMrSignalFunc.h"
#include "mdeMrMediaFunc.h"
#include "mdeMrResourceFunc.h"

extern ThreadInfo_t g_threadDpSendRtp[MAX_NUM_OF_DP_THREAD];
extern UINT g_UdpRcvCount;
extern UINT g_UdpSndCount;
extern UINT g_TcpSndCount;
extern UINT g_TcpRcvCount;

#if 0
#endif

/* UDP连接服务侧RTP/RTCP分别只启动一个socketfd等待客户端连接 */
UINT mdeInitUdpSocket(T_ModCtxS *ptModCtx)
{
    XPOC_TRACEFUNC_IN;
    X_NULL_POINTER_CHK_ONE_PARA(ptModCtx);

    MdeGlbData_S* ptGlbData = (MdeGlbData_S*)ptModCtx->pAppData;

    /* create UDP port */
    USHORT usRtpPort = ptGlbData->stCfgInfo.udpRtpPort;
    USHORT usRtcpPort = ptGlbData->stCfgInfo.udpRtcpPort;
    SINT iRtpSocketFd = F_createUdpSocket(ptGlbData->stCfgInfo.ulLocalIp, usRtpPort);
    SINT iRtcpSocketFd = F_createUdpSocket(ptGlbData->stCfgInfo.ulLocalIp, usRtcpPort);
    if(-1 == iRtpSocketFd || -1 == iRtcpSocketFd)
    {
        struct in_addr stRtpIp;
        struct in_addr stRtcpIp;
        stRtpIp.s_addr  = ptGlbData->stCfgInfo.ulLocalIp;
        stRtcpIp.s_addr = ptGlbData->stCfgInfo.ulLocalIp;

        x_ErrorLog("[X][UDP]udp socket create failed. rtp.addr[%s:%d].rtcp.addr[%s:%d].\n", 
                    inet_ntoa(stRtpIp), usRtpPort, inet_ntoa(stRtcpIp), usRtcpPort);
        return XPOC_ERR_FAIL;
    }

    MediaFdS *ptRtpFd = &ptGlbData->stUdpAddrInfo.udpRtpFd;
    MediaFdS *ptRtcpFd = &ptGlbData->stUdpAddrInfo.udpRtcpFd;
    ptGlbData->stUdpAddrInfo.ptSendThread = &g_threadDpSendRtp[0];

    ptRtpFd->fdType = FD_TYPE_UDP_RTP;
    ptRtpFd->usPort = usRtpPort;
    ptRtpFd->iSocketFd = iRtpSocketFd;

    ptRtcpFd->fdType = FD_TYPE_UDP_RTCP;
    ptRtcpFd->usPort = usRtcpPort;
    ptRtcpFd->iSocketFd = iRtcpSocketFd;

    (void)F_epollCreate(MAX_RTP_SD_NUM, &ptGlbData->stUdpAddrInfo.udpEpollFd, "udp");
    (void)F_epollAddFd(ptGlbData->stUdpAddrInfo.udpEpollFd, ptRtpFd->iSocketFd, (void*)ptRtpFd, "udpRtpFd");
    (void)F_epollAddFd(ptGlbData->stUdpAddrInfo.udpEpollFd, ptRtcpFd->iSocketFd, (void*)ptRtcpFd, "udpRtcpFd");

    x_InfoLog("[X][UDP][Socket] create ok. epollId[%d],rtpFd[%d],rtcpFd[%d].\n", 
              ptGlbData->stUdpAddrInfo.udpEpollFd, ptRtpFd->iSocketFd, ptRtcpFd->iSocketFd);

    XPOC_TRACEFUNC_OUT;
    return XPOC_ERR_SUCCESS;
}

UINT mdeInitTcpSocket(T_ModCtxS *ptModCtx)
{
    XPOC_TRACEFUNC_IN;
    X_NULL_POINTER_CHK_ONE_PARA(ptModCtx);

    MdeGlbData_S* ptGlbData = (MdeGlbData_S*)ptModCtx->pAppData;

    /* create TCP port */
    SINT iSockFd = -1;
    iSockFd = F_createTcpSocket(ptGlbData->stCfgInfo.ulLocalIp, ptGlbData->stCfgInfo.tcpPort);
    if(iSockFd < 0)
    {
        return XPOC_ERR_FAIL;
    }

    MediaFdS *ptListenFd = &ptGlbData->stTcpAddrInfo.tcpListenFd;
    ptListenFd->recordIdx = 0;
    ptListenFd->fdType = FD_TYPE_TCP_RTP;
    ptListenFd->usPort = ptGlbData->stCfgInfo.tcpPort;
    ptListenFd->iSocketFd = iSockFd;
    ptListenFd->ulUCCId = 0;
    ptGlbData->stTcpAddrInfo.ptSendThread = &g_threadDpSendRtp[1];

    (void)F_epollCreate(MAX_RTP_SD_NUM, &ptGlbData->stTcpAddrInfo.tcpEpollFd, "tcp");
    (void)F_epollAddFd(ptGlbData->stTcpAddrInfo.tcpEpollFd, ptListenFd->iSocketFd, (void*)ptListenFd, "listenfd");

    x_InfoLog("[X][TCP][Socket] create ok. epollid[%d],listenfd[%d],tcpfdnum[%d].\n", 
            ptGlbData->stTcpAddrInfo.tcpEpollFd, iSockFd, ptGlbData->stTcpAddrInfo.tcpFdNum);

    XPOC_TRACEFUNC_OUT;
    return XPOC_ERR_SUCCESS;
}

UINT mdeInitMediaSocket(T_ModCtxS *ptModCtx)
{
    XPOC_TRACEFUNC_IN;
    X_NULL_POINTER_CHK_ONE_PARA(ptModCtx);

    mdeInitUdpSocket(ptModCtx);

    /* 现在TCP socket 创建和listen在不同的线程中,据说有可能会有丢包现象. 
        后需可根据实际测试结果把此过程放到mdeInitDpThread的tcp接收线程中执行 */
//    mdeInitTcpSocket(ptModCtx);

    XPOC_TRACEFUNC_OUT;
    return XPOC_ERR_SUCCESS;
}

UINT mdeFreeTcpSocket(USHORT usRtpFdIdx)
{
    T_ModCtxS *ptModCtx = Fplat_GetModuleCtx(XMOD_TYPE_MDE);
    MdeGlbData_S* ptGlbData = (MdeGlbData_S*)ptModCtx->pAppData;

    SINT iRtpFd   = ptGlbData->stTcpAddrInfo.tcpRtpFdList[usRtpFdIdx].iSocketFd;
    SINT iEpollFd = ptGlbData->stTcpAddrInfo.tcpEpollFd;
    UCHAR ubUsedFg = ptGlbData->stTcpAddrInfo.tcpRtpFdList[usRtpFdIdx].ubUsedFg;

    pthread_mutex_lock(&ptGlbData->stTcpAddrInfo.tcpRtpFdList[usRtpFdIdx].mtxSocket);
    if (INVALID_VALUE_UINT != ptGlbData->stTcpAddrInfo.tcpRtpFdList[usRtpFdIdx].iSocketFd)
    {
        (void)F_epollDelFd(iEpollFd, ptGlbData->stTcpAddrInfo.tcpRtpFdList[usRtpFdIdx].iSocketFd, "tcprtpfd");
        shutdown(ptGlbData->stTcpAddrInfo.tcpRtpFdList[usRtpFdIdx].iSocketFd, SHUT_RDWR);
        close(ptGlbData->stTcpAddrInfo.tcpRtpFdList[usRtpFdIdx].iSocketFd);
        ptGlbData->stTcpAddrInfo.tcpRtpFdList[usRtpFdIdx].iSocketFd = INVALID_VALUE_UINT;
        ptGlbData->stTcpAddrInfo.tcpRtpFdList[usRtpFdIdx].ulUCCId  = 0;
        ptGlbData->stTcpAddrInfo.tcpRtpFdList[usRtpFdIdx].ulCliIp = 0;
        ptGlbData->stTcpAddrInfo.tcpRtpFdList[usRtpFdIdx].rtpCliPort  = 0;
        ptGlbData->stTcpAddrInfo.tcpRtpFdList[usRtpFdIdx].rtcpCliPort = 0;
        ptGlbData->stTcpAddrInfo.tcpRtpFdList[usRtpFdIdx].ubUsedFg = FALSE;
        ptGlbData->stTcpAddrInfo.tcpFdNum--;
    }
    x_InfoLog("[X]close tcp socket. tcpRtpFdList[%d]:tcprtpfd[%d], ubUsedFg[%d], tcpFdNum[%d].\n", 
                    usRtpFdIdx, iRtpFd, ubUsedFg, ptGlbData->stTcpAddrInfo.tcpFdNum);
    pthread_mutex_unlock(&ptGlbData->stTcpAddrInfo.tcpRtpFdList[usRtpFdIdx].mtxSocket);

    g_TcpSndCount = 0;
    g_TcpRcvCount = 0;

    return XPOC_ERR_SUCCESS;
}


UINT mdeCloseMediaSocket(T_MdeUCallCtxTable *ptUCC)
{
    XPOC_TRACEFUNC_IN;
    X_NULL_POINTER_CHK_ONE_PARA(ptUCC);

    if(MEDIA_AUDIO == ptUCC->enMediaType)
    {
//        mdeFreeUdpSocket(ptUCC->usRtpFdIdx, ptUCC->usRtcpFdIdx);
    }
    else if(MEDIA_VIDEO == ptUCC->enMediaType)
    {
        mdeFreeTcpSocket(ptUCC->usTcpRtpFdIdx);
    }

    XPOC_TRACEFUNC_OUT;
    return XPOC_ERR_SUCCESS;
}


#if 0
#endif


UINT mdeGetCB(T_ModMsgHead* ptXPocHdr, UINT *ptCBId, MdeCtrlBlockS **pptCB)
{
    XPOC_TRACEFUNC_IN;
    X_NULL_POINTER_CHK_THREE_PARA(ptCBId, ptXPocHdr, ptXPocHdr->stMsgBody);

    UINT  ulReturn = XPOC_ERR_SUCCESS;

    /* get CB */
    if(EV_SIP_INVITE_IND == ptXPocHdr->ulMsgId)
    {
        *pptCB = (MdeCtrlBlockS*)XPOCAllocCtrlBlock(ptCBId);
        if(NULL == *pptCB)
        {
            x_WarnLog(" Call Alloc CB failed.\n");
            return XPOC_ERR_LOCATE_CB;
        }
        memset(*pptCB, 0, sizeof(MdeCtrlBlockS));
        (*pptCB)->ulCBId = *ptCBId;
    }
    else if(EV_SIP_BYE_IND == ptXPocHdr->ulMsgId
        || EV_SIP_REINVITE_IND == ptXPocHdr->ulMsgId)
    {
        *pptCB = XPOCLocateCtrlBlock(*ptCBId);
        if(NULL == *pptCB)
        {
            return XPOC_ERR_LOCATE_CB;
        }
    }
    else
    {
        x_WarnLog("unexpected MsgId[0x%x].\n", ptXPocHdr->ulMsgId);
        return XPOC_ERR_LOCATE_CB;
    }

    (*pptCB)->ulCurrMsgId = ptXPocHdr->ulMsgId;

    XPOC_TRACEFUNC_OUT;
    return ulReturn;
}

UINT mdeGetCallCtx(T_ModCtxS *ptModCtx, UINT ulCCId, T_MdeCallCtxTable *ptCC)
{
    XPOC_TRACEFUNC_IN;
    X_NULL_POINTER_CHK_ONE_PARA(ptModCtx);

    UINT ulReturn = XPOC_ERR_SUCCESS;

    if(0 == ulCCId || INVALID_VALUE_UINT == ulCCId)
    {
        ulReturn = XMdeCallCtxOpr(XDB_OPR_ADD, &ptCC->ulServCCId, ptCC->servType, NULL, ptCC);
    }
    else
    {
        ulReturn = XMdeCallCtxOpr(XDB_OPR_QRY, NULL, 0, &ulCCId, ptCC);
    }
   

    XPOC_TRACEFUNC_OUT;
    return ulReturn;
}

UINT mdeAddCBToCC(T_MdeCallCtxTable *ptCC, UINT ulCBId)
{
    XPOC_TRACEFUNC_IN;
    X_NULL_POINTER_CHK_ONE_PARA(ptCC);

    UINT ulReturn= XPOC_ERR_SUCCESS;

    do
    {
        if(ptCC->stCBIdList.ulCBNum < MAX_CB_NUM)
        {
            ptCC->stCBIdList.ulCBId[ptCC->stCBIdList.ulCBNum] = ulCBId;
            ptCC->stCBIdList.ulCBNum++;
        }
        else
        {
            x_WarnLog("Sp Count Num > Max Num,usTempSpNum[%u], ulCBId=%d.\n", ptCC->stCBIdList.ulCBNum, ulCBId);
            ulReturn = XPOC_ERR_FAIL;
            break;
        }

        ulReturn = XMdeCallCtxOpr(XDB_OPR_MOD, NULL, 0, &ptCC->ulRecordIdx, ptCC);
    } while(0);

    if(XPOC_ERR_SUCCESS != ulReturn)
    {
        x_WarnLog("Add CB[%d] 2 CC[%d] failed. CC.CBNum[%d].\n", ulCBId, ptCC->ulRecordIdx, ptCC->stCBIdList.ulCBNum);
    }
    else
    {
        x_InfoLog("Add CB[%d] 2 CC[%d] OK. CC.CBNum[%d].\n", ulCBId, ptCC->ulRecordIdx, ptCC->stCBIdList.ulCBNum);  
    }

    XPOC_TRACEFUNC_OUT;
    return ulReturn;
}

UINT mdeDelCBFromCC(T_MdeCallCtxTable *ptCC, UINT ulCBId)
{
    XPOC_TRACEFUNC_IN;
    X_NULL_POINTER_CHK_ONE_PARA(ptCC);

    UINT ulReturn = XPOC_ERR_SUCCESS;

    do
    {
        USHORT usCBNum = ptCC->stCBIdList.ulCBNum;
        if(usCBNum > MAX_CB_NUM)
        {
            x_WarnLog("Wrong CBNum[%u]:maxLimit[%u],CBId[%u],callId[%u].", usCBNum, MAX_CB_NUM, ulCBId, ptCC->ulServCCId);
            ulReturn = XPOC_ERR_FAIL;
            break;
        }
        
        UINT i=0, j=0;
        for(i = 0; i < usCBNum; i++)
        {
            if(ulCBId == ptCC->stCBIdList.ulCBId[i])
            {
                for(j = i; j<usCBNum && j<MAX_CB_NUM-1; j++)
                {
                    ptCC->stCBIdList.ulCBId[j] = ptCC->stCBIdList.ulCBId[j+1];
                }
                ptCC->stCBIdList.ulCBId[j] = 0;
                ptCC->stCBIdList.ulCBNum--;
                break;
            }
        }
        
        ulReturn = XMdeCallCtxOpr(XDB_OPR_MOD, NULL, 0, &ptCC->ulRecordIdx, ptCC);
    } while(0);

    if(XPOC_ERR_SUCCESS != ulReturn)
    {
        x_WarnLog("Del CB[%d] from CC[%d] failed. CC.CBNum[%d].\n", ulCBId, ptCC->ulRecordIdx, ptCC->stCBIdList.ulCBNum);  
    }
    else
    {
        x_InfoLog("Del CB[%d] from CC[%d] OK. CC.CBNum[%d].\n", ulCBId, ptCC->ulRecordIdx, ptCC->stCBIdList.ulCBNum);  
    }

    XPOC_TRACEFUNC_OUT;
    return XPOC_ERR_SUCCESS;
}

/* 在视频转发(组)的媒体源CC中删除当前转发CCId */
UINT mdeDelTransCCFromSrcCC(UINT ulSrcCCId, UINT ulCCId)
{
    XPOC_TRACEFUNC_IN;

    UINT ulReturn = XPOC_ERR_SUCCESS;

    T_MdeCallCtxTable stSrcCC;
    stSrcCC.ulRecordIdx = ulSrcCCId;
    ulReturn = XMdeCallCtxOpr(XDB_OPR_QRY, NULL, 0, &stSrcCC.ulRecordIdx, &stSrcCC);
    if(XPOC_ERR_SUCCESS != ulReturn)
    {
        return ulReturn;
    }

    UCHAR i = 0;
    UCHAR j = 0;
    for(i=0; i<CALL_MAX_NUM; i++)
    {
        if(stSrcCC.ulTransToCCId[i] == ulCCId)
        {
            stSrcCC.ulTransToCCId[i] = 0;
            for(j=i; j<CALL_MAX_NUM-1; j++) /* 紧凑排列 */
            {
                if(0 == stSrcCC.ulTransToCCId[j+1])
                    break;
                else
                    stSrcCC.ulTransToCCId[j] = stSrcCC.ulTransToCCId[j+1];
            }
        }
    }

    ulReturn = XMdeCallCtxOpr(XDB_OPR_MOD, NULL, 0, &stSrcCC.ulRecordIdx, &stSrcCC);

    XPOC_TRACEFUNC_OUT;
    return XPOC_ERR_SUCCESS;
}


UINT mdeAttachUCCToCC(T_MdeCallCtxTable *ptCC, T_MdeUCallCtxTable *ptUCC)
{
    XPOC_TRACEFUNC_IN;
    X_NULL_POINTER_CHK_TWO_PARA(ptCC, ptUCC);

    UINT ulReturn = XPOC_ERR_SUCCESS;

    do
    {
        if(ptCC->stUCCIdList.UcNum >= MAX_UC_NUM)
        {
            x_WarnLog("UCCNum[%d] is overflow for CC[%d].", ptCC->stUCCIdList.UcNum, ptCC->ulRecordIdx);
            ulReturn = XPOC_ERR_FAIL;
            break;
        }

        ptCC->stUCCIdList.stUCCId[ptCC->stUCCIdList.UcNum].IsUsedFg = TRUE;
        ptCC->stUCCIdList.stUCCId[ptCC->stUCCIdList.UcNum].ulUCallCtxId = ptUCC->ulRecordIdx;
        ptCC->stUCCIdList.UcNum++;

        ulReturn = XMdeCallCtxOpr(XDB_OPR_MOD, NULL, 0, &ptCC->ulRecordIdx, ptCC);
    } while(0);

    if(XPOC_ERR_SUCCESS != ulReturn)
    {
        x_WarnLog("Attach UCC[%d] 2 CC[%d] failed. CC.UCCNum[%d].\n",
                ptUCC->ulRecordIdx, ptCC->ulRecordIdx, ptCC->stUCCIdList.UcNum);  
    }
    else
    {
        x_InfoLog("Attach UCC[%d] 2 CC[%d] OK. CC.UCCNum[%d].\n",
                ptUCC->ulRecordIdx, ptCC->ulRecordIdx, ptCC->stUCCIdList.UcNum);  
    }

    XPOC_TRACEFUNC_OUT;
    return ulReturn;
}

UINT mdeDetachUCCFromCC(UINT ulCallCtxId, UINT ulUCallCtxId)
{
    XPOC_TRACEFUNC_IN;

    UINT ulReturn = XPOC_ERR_FAIL;
    T_MdeCallCtxTable stCallCtx;
    T_MdeUCallCtxTable stUCallCtx;
    memset(&stCallCtx, 0, sizeof(T_MdeCallCtxTable));
    memset(&stUCallCtx, 0, sizeof(T_MdeUCallCtxTable));

    do
    {
        ulReturn = XMdeCallCtxOpr(XDB_OPR_QRY, NULL, 0, &ulCallCtxId, &stCallCtx);
        if(XPOC_ERR_SUCCESS != ulReturn)
        {
            break;
        }
        ulReturn = XMdeUCallCtxOpr(XDB_OPR_QRY, NULL, 0, 0, 0, NULL, &ulUCallCtxId, &stUCallCtx);
        if(XPOC_ERR_SUCCESS != ulReturn)
        {
            break;
        }
        
        USHORT i=0, j=0;
        for(i = 0; i < stCallCtx.stUCCIdList.UcNum; i++)
        {
            if(stCallCtx.stUCCIdList.stUCCId[i].IsUsedFg
                && ulUCallCtxId == stCallCtx.stUCCIdList.stUCCId[i].ulUCallCtxId)
            {
                for(j=i; j<stCallCtx.stUCCIdList.UcNum-1; j++)  /* 对数组作紧凑排列 */
                {
                    memcpy(&stCallCtx.stUCCIdList.stUCCId[j], &stCallCtx.stUCCIdList.stUCCId[j+1], sizeof(UCCId_S));
                }
                memset(&stCallCtx.stUCCIdList.stUCCId[j], 0, sizeof(UCCId_S));
                stCallCtx.stUCCIdList.UcNum--;
                ulReturn = XMdeCallCtxOpr(XDB_OPR_MOD, NULL, 0, &stCallCtx.ulRecordIdx, &stCallCtx);
                break;
            }
        }
    } while(0);

    if(XPOC_ERR_SUCCESS != ulReturn)
    {
        x_WarnLog("Detach UCC[%d] 2 CC[%d] failed. CC.UCCNum[%d].\n", 
                stUCallCtx.ulRecordIdx, stCallCtx.ulRecordIdx, stCallCtx.stUCCIdList.UcNum);
    }
    else
    {
        x_InfoLog("Detach UCC[%d] 2 CC[%d] OK. CC.UCCNum[%d].\n",
                stUCallCtx.ulRecordIdx, stCallCtx.ulRecordIdx, stCallCtx.stUCCIdList.UcNum);
    }

    XPOC_TRACEFUNC_OUT;
    return XPOC_ERR_SUCCESS;    
}

/* 该接口释放一个呼叫的所有媒体类资源. 一般仅在CC吊死时使用 */
UINT mdeFreeAllResource(UINT ulServCCId, UCHAR servType)
{
    XPOC_TRACEFUNC_IN;
    UINT ulReturn = XPOC_ERR_SUCCESS;

    T_MdeCallCtxTable stCC;
    memset(&stCC, 0, sizeof(T_MdeCallCtxTable));
    ulReturn = XMdeCallCtxOpr(XDB_OPR_QRY, &ulServCCId, servType, NULL, &stCC);
    if(XPOC_ERR_SUCCESS != ulReturn)
    {
        return ulReturn;
    }

    //free CB
    UINT i = 0;
    for(i=0; i<stCC.stCBIdList.ulCBNum; i++)
    {
        (void)XPOCFreeCtrlBlock(stCC.stCBIdList.ulCBId[i], XPOC_ERR_FREE_CB);
    }

    //close tcp.connFd
    //free (udp.rtpFd && udp.rtcpFd) || tcp.connFd from epoll_ctl
    //free UCC
    T_MdeUCallCtxTable stUCC;
    for(i=0; i<stCC.stUCCIdList.UcNum; i++)
    {
        memset(&stUCC, 0, sizeof(T_MdeUCallCtxTable));
        stUCC.ulRecordIdx = stCC.stUCCIdList.stUCCId[i].ulUCallCtxId;
        ulReturn = XMdeUCallCtxOpr(XDB_OPR_QRY, NULL, 0, 0, 0, NULL, &stUCC.ulRecordIdx, &stUCC);
        if(XPOC_ERR_SUCCESS != ulReturn)
        {
            continue;
        }
        
        (void)mdeCloseMediaSocket(&stUCC);

        ulReturn = XMdeUCallCtxOpr(XDB_OPR_DEL, NULL, 0, 0, 0, NULL, &stUCC.ulRecordIdx, &stUCC);
        if(XPOC_ERR_SUCCESS != ulReturn)
        {
            continue;
        }
    }

    //free CC
    ulReturn = XMdeCallCtxOpr(XDB_OPR_DEL, NULL, 0, &stCC.ulRecordIdx, &stCC);

    XPOC_TRACEFUNC_OUT;
    return ulReturn;
}

UINT mdeAllocTcpFd(T_ModCtxS *ptModCtx, MediaFdS **pptTcpFd)
{
    XPOC_TRACEFUNC_IN;
    X_NULL_POINTER_CHK_ONE_PARA(ptModCtx);

    MdeGlbData_S* ptGlbData = (MdeGlbData_S*)ptModCtx->pAppData;

    int i = 0;
    for(i=1; i<MAX_RTP_SOCKET_NUM; i++)
    {
        if(!ptGlbData->stTcpAddrInfo.tcpRtpFdList[i].ubUsedFg)
        {
            ptGlbData->stTcpAddrInfo.tcpRtpFdList[i].recordIdx = i;
            ptGlbData->stTcpAddrInfo.tcpRtpFdList[i].ubUsedFg = TRUE;
            ptGlbData->stTcpAddrInfo.tcpRtpFdList[i].fdType = FD_TYPE_TCP_RTP;
            
            ptGlbData->stTcpAddrInfo.tcpFdNum++;

            x_InfoLog("[TV][X][FD]Alloc TcpFd OK. tcpRtpFd[%d:%d],tcpFdNum[%d],UCCId[%d].", 
                    i, ptGlbData->stTcpAddrInfo.tcpRtpFdList[i].iSocketFd, ptGlbData->stTcpAddrInfo.tcpFdNum,
                    ptGlbData->stTcpAddrInfo.tcpRtpFdList[i].ulUCCId);

            *pptTcpFd = &ptGlbData->stTcpAddrInfo.tcpRtpFdList[i];
            return XPOC_ERR_SUCCESS;
        }
    }

    XPOC_TRACEFUNC_OUT;
    return XPOC_ERR_FAIL;
}





mdeMrSignalFunc.c


#include <math.h>
#include "mdeMrCommFunc.h"
#include "mdeMrResourceFunc.h"
#include "mdeMrSignalFunc.h"

#if 0
#endif

extern UINT g_TcpSndCount;
extern UINT g_TcpRcvCount;

UINT mdeGetMediaCcidFromInvite(osip_message_t *ptOsipMsg, UINT *ptCCId)
{
    UINT ccid = 0;

    XPOC_TRACEFUNC_IN;
    X_NULL_POINTER_CHK_TWO_PARA(ptOsipMsg, ptCCId);

    osip_list_t *ptList = &(ptOsipMsg->req_uri->url_params);
    osip_uri_param_t *ptTmp = NULL;
    __node_t *ptNode = ptList->node;
    UCHAR uiRouteIdx = 0;
    
    while((NULL != ptList->node) && (uiRouteIdx<ptList->nb_elt))
    {
        ptTmp = (osip_uri_param_t *)ptNode->element;
        if(NULL != strstr((char*)ptTmp->gname, (char*)"ccid"))
        {
            ccid = atoi(ptTmp->gvalue);
            break;
        }
        else
        {
            ptNode = ptNode->next;
            uiRouteIdx++;
        }
    }

    *ptCCId = ccid;

    XPOC_TRACEFUNC_OUT;
    return XPOC_ERR_SUCCESS;
}

/*从请求行获取OnliceCallId,注: 用户号码不再这里取,改而从From头域中获取*/
UINT mdeGetOnlineServCallCtrlId(SipMsgIndS *ptSipMsg, UINT *ptCallCtrlId)
{
    char szUserNumber[256] = {0};
    char *pUserInfo = NULL;
    char szTmp[256] = {0};
    UINT ulUserNumber = 0;
    
    XPOC_TRACEFUNC_IN;
        
    /*用于媒体面统计,获取用户号码*/
    if(ptSipMsg && 0 == strcmp("sip", ptSipMsg->ulSipHandle->request->req_uri->scheme))
    {
        pUserInfo = (char*)ptSipMsg->ulSipHandle->request->req_uri->username;
        //conf=1470897193-820000
        if(pUserInfo)
        {            
            sscanf(pUserInfo, "conf=%[^-]-%s", szTmp, szUserNumber);
            ulUserNumber = (UINT)atoi((const char *)(szUserNumber));
            *ptCallCtrlId = (UINT)atoll((const char *)(szTmp));
        }
    }

    CallCtrlIdU CCId = {0};
    CCId.ulCtrlId = *ptCallCtrlId;
    x_InfoLog("[X]mdeGetOnlineServCallCtrlId:callId[0x%x,ccid[%d],legId[%d],servType[%d],workMode[%d],platType[%d]],userNum[%d].", 
            *ptCallCtrlId, CCId.stCtrlInfoS.ulCallCtxId, CCId.stCtrlInfoS.uiCtrlLegId, CCId.stCtrlInfoS.ubServType, 
            CCId.stCtrlInfoS.ubWorkMode, CCId.stCtrlInfoS.ubPlatType, ulUserNumber);

    XPOC_TRACEFUNC_OUT;
    return ulUserNumber;
}

UINT mdeGetServIdFromSipHeader(osip_message_t *ptOsipMessage, char *ptServId)
{
    XPOC_TRACEFUNC_IN;
    X_NULL_POINTER_CHK_TWO_PARA(ptOsipMessage, ptServId);

    UINT ulReturn = XPOC_ERR_SUCCESS;
    osip_from_t *ptFrom = osip_message_get_from(ptOsipMessage);
    if(ptFrom)
    {
        sprintf(ptServId, "%s", ptFrom->url->username);
    }
    else
    {
        x_WarnLog("get servId from sip.from failed.");
        ulReturn = XPOC_ERR_FAIL;
    }

    XPOC_TRACEFUNC_OUT;
    return ulReturn;
}
UINT mdeGetOrigIpFromSipHeader(osip_message_t *ptOsipMessage, char *ptIp)
{
    XPOC_TRACEFUNC_IN;
    X_NULL_POINTER_CHK_TWO_PARA(ptOsipMessage, ptIp);

    UINT ulReturn = XPOC_ERR_SUCCESS;
    osip_from_t *ptFrom = osip_message_get_from(ptOsipMessage);
    if(ptFrom)
    {
        sprintf(ptIp, "%s", ptFrom->url->host);
    }
    else
    {
        x_WarnLog("get ip from sip.from failed.");
        ulReturn = XPOC_ERR_FAIL;
    }

    x_InfoLog("mdeGetOrigIpFromSipHeader:IP[%s].", ptIp);

    XPOC_TRACEFUNC_OUT;
    return ulReturn;
}


UINT MdeSetSdpAudioMedia(T_MdeUCallCtxTable *ptUCC, UINT sdpIp4, sdp_message_t *ptSdpMessage, servTypeE servType)
{
    XPOC_TRACEFUNC_IN;
    X_NULL_POINTER_CHK_TWO_PARA(ptUCC, ptSdpMessage);

    T_ModCtxS *ptModCtx = Fplat_GetModuleCtx(XMOD_TYPE_MDE);
    MdeGlbData_S* ptGlbData = (MdeGlbData_S*)ptModCtx->pAppData;

    sdp_media_t *ptSdpMedia = eXosip_get_audio_media(ptSdpMessage);
    if(!ptSdpMedia)
    {
        x_WarnLog("ptSdpMedia is NULL.");
        return XPOC_ERR_INVALID_PTR;
    }
    int pos = MdeGetMediaPos(ptSdpMessage, (char*)"audio");
    if(-1 == pos)
    {
        x_WarnLog("get audio media pos from sdp failed.");
        return XPOC_ERR_INVALID_PTR;
    }

    SdpCodecS *ptSdpCodec = &ptUCC->stMediaInfo.stSdpCodec[0];
    char *att_field = NULL;
    char *att_value = NULL;

    char *payload = osip_malloc(4*sizeof(UCHAR));
    sprintf(payload,"%d", ptSdpCodec->ulCodecNo);
    osip_list_add(&ptSdpMedia->m_payloads, payload, 0);

    /* 对audio增加a行: a=rtcp:40001 IN IP4 192.168.101.254 */
    char addr[STR_LEN32] = {0};
    sprintf(addr, "%d.%d.%d.%d", sdpIp4>>24&0xFF, sdpIp4>>16&0xFF, sdpIp4>>8&0xFF, sdpIp4&0xFF);

    att_field = osip_malloc(STR_LEN128*sizeof(UCHAR));
    att_value = osip_malloc(STR_LEN128*sizeof(UCHAR));
    sprintf(att_field,"rtcp");
    //  modified by shm, 解决rce用内网,终端用外网问题;此处分布式安装时,还需更改
    if(Serv_AudioRcrdPri == servType
        || Serv_VideoRcrdPri == servType
        || Serv_AudioRcrdGrp == servType
        || Serv_VideoRcrdGrp == servType)
    {
        // rce
        sprintf(att_value,"%d IN IP4 %s", ptGlbData->stCfgInfo.udpRtcpPort, addr);
    }
    else
    {
        sprintf(att_value,"%d IN IP4 %s", ptGlbData->stCfgInfo.udpRmtRtcpPort, addr);
    }
    
    sdp_message_a_attribute_add (ptSdpMessage, pos, att_field, att_value);


    att_field = osip_malloc(STR_LEN128 * sizeof(UCHAR));
    att_value = osip_malloc(STR_LEN128 * sizeof(UCHAR));
    sprintf(att_field,"rtpmap");
    sprintf(att_value,"%d %s/%d ",ptSdpCodec->ulCodecNo, ptSdpCodec->aucRtpMap, ptSdpCodec->ulClockRate);
    sdp_message_a_attribute_add (ptSdpMessage, pos, att_field, att_value);

    if(0 != strlen(ptSdpCodec->aucFrmPm))
    {
        att_field = osip_malloc(128*sizeof(UCHAR));
        att_value = osip_malloc(128*sizeof(UCHAR));
        
        sprintf(att_field,"fmtp");
        sprintf(att_value,"%s ",ptSdpCodec->aucFrmPm);
        sdp_message_a_attribute_add (ptSdpMessage, pos, att_field, att_value);
    }

    //m=audio 20400 ...
    char *sPort = osip_malloc(8*sizeof(UCHAR));

    //  modified by shm, 解决rce用内网,终端用外网问题;此处分布式安装时,还需更改
    if(Serv_AudioRcrdPri == servType
        || Serv_VideoRcrdPri == servType
        || Serv_AudioRcrdGrp == servType
        || Serv_VideoRcrdGrp == servType)
    {
        // rce
        sprintf(sPort, "%d", ptGlbData->stCfgInfo.udpRtpPort);
    }
    else
    {
        sprintf(sPort, "%d", ptGlbData->stCfgInfo.udpRmtRtpPort);
    }

    sdp_message_m_port_set(ptSdpMessage, pos, sPort);

    //a=sendrecv
    att_field = osip_malloc(16*sizeof(UCHAR));
    mdeTopoly2Str(ptUCC->stMediaInfo.enTopoly, att_field);
    sdp_message_a_attribute_add(ptSdpMessage, pos, att_field, NULL);

    XPOC_TRACEFUNC_OUT;
    return XPOC_ERR_SUCCESS; 
}

UINT MdeSetSdpVideoMedia(T_MdeUCallCtxTable *ptUCC, sdp_message_t *ptSdpMessage, servTypeE servType)
{
    XPOC_TRACEFUNC_IN;
    X_NULL_POINTER_CHK_TWO_PARA(ptUCC, ptSdpMessage);

    sdp_media_t *ptSdpMedia = eXosip_get_video_media(ptSdpMessage);
    if(!ptSdpMedia)
    {
        x_WarnLog("ptSdpMedia is NULL.");
        return XPOC_ERR_INVALID_PTR;
    }

    int pos = MdeGetMediaPos(ptSdpMessage, (char*)"video");
    if(-1 == pos)
    {
        x_WarnLog("get video media pos from sdp failed.");
        return XPOC_ERR_INVALID_PTR;
    }

    SdpCodecS *ptSdpCodec = &ptUCC->stMediaInfo.stSdpCodec[0];

    char *payload = osip_malloc(4*sizeof(UCHAR));
    sprintf(payload,"%d", ptSdpCodec->ulCodecNo);
    osip_list_add(&ptSdpMedia->m_payloads, payload, 0);

    char *att_field = osip_malloc(STR_LEN128 * sizeof(UCHAR));
    char *att_value = osip_malloc(STR_LEN128 * sizeof(UCHAR));

    sprintf(att_field,"rtpmap");
    sprintf(att_value,"%d %s/%d ",ptSdpCodec->ulCodecNo, ptSdpCodec->aucRtpMap, ptSdpCodec->ulClockRate);
    sdp_message_a_attribute_add (ptSdpMessage, pos, att_field, att_value);

    if(0 != strlen(ptSdpCodec->aucFrmPm))
    {
        att_field = osip_malloc(128*sizeof(UCHAR));
        att_value = osip_malloc(128*sizeof(UCHAR));
        
        sprintf(att_field,"fmtp");
        sprintf(att_value,"%s ",ptSdpCodec->aucFrmPm);
        sdp_message_a_attribute_add (ptSdpMessage, pos, att_field, att_value);
    }

    //m=video 29000 ...
    T_ModCtxS *ptModCtx = Fplat_GetModuleCtx(XMOD_TYPE_MDE);
    MdeGlbData_S* ptGlbData = (MdeGlbData_S*)ptModCtx->pAppData;

    char *sPort = osip_malloc(8*sizeof(UCHAR));

    //  modified by shm, 解决rce用内网,终端用外网问题;此处分布式安装时,还需更改
    if(Serv_AudioRcrdPri == servType
        || Serv_VideoRcrdPri == servType
        || Serv_AudioRcrdGrp == servType
        || Serv_VideoRcrdGrp == servType)
    {
        // rce
        sprintf(sPort, "%d", ptGlbData->stTcpAddrInfo.tcpListenFd.usPort);
    }
    else
    {
        sprintf(sPort, "%d", ptGlbData->stCfgInfo.tcpRmtPort);
    }

    sdp_message_m_port_set(ptSdpMessage, pos, sPort);

    //a=sendrecv
    att_field = osip_malloc(16*sizeof(UCHAR));
    mdeTopoly2Str(ptUCC->stMediaInfo.enTopoly, att_field);
    sdp_message_a_attribute_add(ptSdpMessage, pos, att_field, NULL);

    XPOC_TRACEFUNC_OUT;
    return XPOC_ERR_SUCCESS; 
}

UINT mdeBuildSdpCommInfo(sdp_message_t *pSendSdpMsg, UINT Ip4, UINT ulMediaNum)
{
    XPOC_TRACEFUNC_IN;
    X_NULL_POINTER_CHK_ONE_PARA(pSendSdpMsg);

    //set sdp.v
    char *v = osip_malloc(16*sizeof(UCHAR));
    sprintf(v, "0");
    sdp_message_v_version_set(pSendSdpMsg, v);

    //set sdp.o
    char *o_username = osip_malloc(32*sizeof(UCHAR));
    char *o_sess_id = osip_malloc(32*sizeof(UCHAR));
    char *sess_version = osip_malloc(32*sizeof(UCHAR));
    char *o_nettype = osip_malloc(32*sizeof(UCHAR));
    char *o_addrtype = osip_malloc(32*sizeof(UCHAR));
    sprintf(o_username, "user");
    sprintf(o_sess_id, "53655765");
    sprintf(sess_version, "2853687637");
    sprintf(o_nettype, "IN");
    sprintf(o_addrtype, "IP4");
    char *addr  = osip_malloc(16*sizeof(UCHAR));
    sprintf(addr,  "%d.%d.%d.%d", Ip4>>24&0xFF, Ip4>>16&0xFF, Ip4>>8&0xFF, Ip4&0xFF);
    sdp_message_o_origin_set(pSendSdpMsg, o_username, o_sess_id, sess_version, o_nettype, o_addrtype, addr);

    //set sdp.connection
    char *c_nettype0 = osip_malloc(32*sizeof(UCHAR));
    char *c_addrtype0 = osip_malloc(32*sizeof(UCHAR));
    sprintf(c_nettype0, "IN");
    sprintf(c_addrtype0, "IP4");
    char *addr0 = osip_malloc(16*sizeof(UCHAR));
    sprintf(addr0, "%d.%d.%d.%d", Ip4>>24&0xFF, Ip4>>16&0xFF, Ip4>>8&0xFF, Ip4&0xFF);
    sdp_message_c_connection_add(pSendSdpMsg, -1, c_nettype0, c_addrtype0, addr0, NULL, NULL);

    char *c_nettype1 = osip_malloc(16*sizeof(UCHAR));
    char *c_addrtype1 = osip_malloc(16*sizeof(UCHAR));
    sprintf(c_nettype1, "IN");
    sprintf(c_addrtype1, "IP4");
    char *addr1 = osip_malloc(16*sizeof(UCHAR));
    sprintf(addr1, "%d.%d.%d.%d", Ip4>>24&0xFF, Ip4>>16&0xFF, Ip4>>8&0xFF, Ip4&0xFF);
    sdp_message_c_connection_add(pSendSdpMsg, 0, c_nettype1, c_addrtype1, addr1, NULL, NULL);

    if(2 == ulMediaNum)
    {
        char *c_nettype2 = osip_malloc(16*sizeof(UCHAR));
        char *c_addrtype2 = osip_malloc(16*sizeof(UCHAR));
        sprintf(c_nettype2, "IN");
        sprintf(c_addrtype2, "IP4");
        char *addr2 = osip_malloc(16*sizeof(UCHAR));
        sprintf(addr2, "%d.%d.%d.%d", Ip4>>24&0xFF, Ip4>>16&0xFF, Ip4>>8&0xFF, Ip4&0xFF);
        sdp_message_c_connection_add(pSendSdpMsg, 1, c_nettype2, c_addrtype2, addr2, NULL, NULL);
    }

    XPOC_TRACEFUNC_OUT;
    return XPOC_ERR_SUCCESS;
}



UINT mdeGetMediaInfoFromReinvite(sdp_message_t *ptRecvSdpMsg, MediaDescriptionS *ptAudio, MediaDescriptionS *ptVideo)
{
    XPOC_TRACEFUNC_IN;
    X_NULL_POINTER_CHK_THREE_PARA(ptRecvSdpMsg, ptAudio, ptVideo);

    UINT ulReturn = XPOC_ERR_SUCCESS;
    UINT IPv4 = 0;

    do
    {
        sdp_message_t *ptTmpSdp = NULL;
        sdp_message_clone (ptRecvSdpMsg, &ptTmpSdp);
        if(!ptTmpSdp)
        {
            x_WarnLog("recv Reinvte without sdp, fail.");
            ulReturn = XPOC_ERR_FAIL;
            break;
        }

        sdp_media_t *ptAudioSdp = eXosip_get_audio_media(ptTmpSdp);
        if(ptAudioSdp)
        {
            char *audio_port = ptAudioSdp->m_port; //audio_port
            char *audio_ip = sdp_message_c_addr_get(ptTmpSdp, 0, 0);
            if(NULL == audio_ip)
            {
                sdp_connection_t *co = ptTmpSdp->c_connection;    
                audio_ip = co->c_addr;
            }
            IPv4 = inet_addr(audio_ip);
            ptAudio->ulIpAddr = ntohl(IPv4);
            ptAudio->rtpPort = atoi(audio_port);
            ptAudio->rtcpPort = ptAudio->rtpPort+1;
        }

        sdp_media_t *ptVideoSdp = eXosip_get_video_media(ptTmpSdp);
        if(ptVideoSdp)
        {
            char *video_port = ptVideoSdp->m_port; //video_port
            char *video_ip = sdp_message_c_addr_get(ptTmpSdp, 0, 0);
            if(NULL == video_ip)
            {
                sdp_connection_t *co = ptTmpSdp->c_connection;    
                video_ip = co->c_addr;
            }
            IPv4 = inet_addr(video_ip);
            ptVideo->ulIpAddr = ntohl(IPv4);
            ptVideo->rtpPort = atoi(video_port);
            ptVideo->rtcpPort = ptVideo->rtpPort+1;
        }
    } while(0);

    XPOC_TRACEFUNC_OUT;
    return ulReturn;
}

UINT mdeGetBodySessionFromInfo(T_ModMsgHead *ptXpocMsgHdr, MdeCtrlBlockS *ptCtrlBlock, osip_body_t **ptBody)
{
    XPOC_TRACEFUNC_IN;
    X_NULL_POINTER_CHK_THREE_PARA(ptCtrlBlock, ptXpocMsgHdr, ptXpocMsgHdr->stMsgBody);

    SipMsgIndS* ptSipMsgInd = (SipMsgIndS*)ptXpocMsgHdr->stMsgBody;
    osip_message_t *request = ptSipMsgInd->ulSipHandle->request;

    osip_body_t *ptTmpBody=NULL;
    osip_message_get_body(request,0,&ptTmpBody);
    *ptBody = ptTmpBody;

    XPOC_TRACEFUNC_OUT;
    return XPOC_ERR_SUCCESS;
}

UINT mdeGetMediaInfoFromSipInvite(T_ModMsgHead* ptXpocMsgHdr, MediaDescriptionS *ptAudio, MediaDescriptionS *ptVideo)
{
    XPOC_TRACEFUNC_IN;
    X_NULL_POINTER_CHK_THREE_PARA(ptXpocMsgHdr, ptAudio, ptVideo);

    SipMsgIndS *ptSipInd = (SipMsgIndS*)ptXpocMsgHdr->stMsgBody;
    char acCallId[64]    = {0};
    XPOCGetSipHeaderCallId(ptSipInd->ulSipHandle->request, acCallId);

    sdp_message_t *ptSdp = eXosip_get_sdp_info(ptSipInd->ulSipHandle->request);

    if(!ptSdp || !ptSdp->c_connection || !ptSdp->c_connection->c_addr)
    {
        x_WarnLog("Get sdp from invite failed, or sdp err.");
        return XPOC_ERR_FAIL;
    }

    UINT IPv4 = inet_addr(ptSdp->c_connection->c_addr);

    sdp_media_t *ptAudioMedia = eXosip_get_audio_media(ptSdp);
    if(ptAudioMedia)
    {
        ptAudio->ulIpAddr   = ntohl(IPv4);
        ptAudio->rtpPort    = atoi(ptAudioMedia->m_port);
        ptAudio->rtcpPort   = ptAudio->rtpPort + 1;
        ptAudio->bCodecNum  = 1;
        (void)MdeGetFirstMediaCodec(ptAudioMedia, &ptAudio->stSdpCodec[0]);
        (void)MdeGetMediaTopoly(ptAudioMedia, &ptAudio->enTopoly);
        (void)MdeGetMediaSsrc(ptAudioMedia, &ptAudio->ulSsrc);
        x_InfoLog("Invite call-id [%s],sdp.audio:,ssrc[0x%x(%u)],ipAddr[0x%x:%d|%d],codec[%d],topoly[%d].", 
                acCallId, ptAudio->ulSsrc, ptAudio->ulSsrc, ptAudio->ulIpAddr, ptAudio->rtpPort, ptAudio->rtcpPort, ptAudio->stSdpCodec[0].ulCodecNo, 
                ptAudio->enTopoly);
    }

    sdp_media_t *ptVideoMedia = eXosip_get_video_media(ptSdp);
    if(ptVideoMedia)
    {
        ptVideo->ulIpAddr  = ntohl(IPv4);
        ptVideo->rtpPort   = atoi(ptVideoMedia->m_port);
        ptVideo->rtcpPort  = ptVideo->rtpPort + 1;
        ptVideo->bCodecNum = 1;
        (void)MdeGetFirstMediaCodec(ptVideoMedia, &ptVideo->stSdpCodec[0]);
        (void)MdeGetMediaTopoly(ptVideoMedia, &ptVideo->enTopoly);
        (void)MdeGetMediaSsrc(ptVideoMedia, &ptVideo->ulSsrc);
        x_InfoLog("Invite call-id [%s],sdp.video:ssrc[0x%x(%u)],ipAddr[0x%x:%d],codec[%d],topoly[%d].", 
                acCallId, ptVideo->ulSsrc, ptVideo->ulSsrc, ptVideo->ulIpAddr, ptVideo->rtpPort, ptVideo->stSdpCodec[0].ulCodecNo, ptVideo->enTopoly);
    }

    if (ptSdp)
    {
        sdp_message_free(ptSdp);
        ptSdp = NULL;
    }

    //x_InfoLog("get Media from invite.CodecNum:audio[%d],video[%d].", ptAudio->bCodecNum, ptVideo->bCodecNum);

    XPOC_TRACEFUNC_OUT;
    return XPOC_ERR_SUCCESS;
}


#if 0
#endif


UINT mdeBuildSendSdpOnRecvSdp(T_MdeCallCtxTable *ptCC, sdp_media_t *ptAudioSdp, sdp_media_t *ptVideoSdp, 
                                      T_MdeUCallCtxTable *ptUCC_Audio, T_MdeUCallCtxTable *ptUCC_Video, sdp_message_t *pSendSdpMsg)
{
    XPOC_TRACEFUNC_IN;
    X_NULL_POINTER_CHK_TWO_PARA(ptCC, pSendSdpMsg);

    T_ModCtxS *ptModCtx = f_procThisContext();
    MdeGlbData_S* ptGlbData = (MdeGlbData_S*)ptModCtx->pAppData;

    x_InfoLog("input:ptAudioSdp[%p],ptVideoSdp[%p],ptUCC_Audio[%p],ptUCC_Video[%p],servType[%d],locIp[%d],AceRmtIp[%d].",
            ptAudioSdp, ptVideoSdp, ptUCC_Audio, ptUCC_Video, ptCC->servType, ptGlbData->stCfgInfo.ulLocalIp, ptGlbData->stCfgInfo.ulAceRmtIp);

    UINT sdpIPV4 = 0;
    if(Serv_AudioRcrdPri == ptCC->servType
        || Serv_VideoRcrdPri == ptCC->servType
        || Serv_AudioRcrdGrp == ptCC->servType
        || Serv_VideoRcrdGrp == ptCC->servType)
    {
        sdpIPV4 = ptGlbData->stCfgInfo.ulLocalIp;
    }
    else
    {
        sdpIPV4 = ptGlbData->stCfgInfo.ulAceRmtIp;
    }

    if(ptAudioSdp && ptUCC_Audio)
    {
        while(ptAudioSdp->a_attributes.nb_elt)
            osip_list_remove(&ptAudioSdp->a_attributes, 0);
        while(ptAudioSdp->m_payloads.nb_elt)
            osip_list_remove(&ptAudioSdp->m_payloads, 0);
        while(ptAudioSdp->c_connections.nb_elt)
            osip_list_remove(&ptAudioSdp->c_connections, 0);

        /* set audio|video media info for 200-invite */
        UCHAR i = 0;
        for(i=0; i < ptCC->MediaStreamNum; i++)
        {
            if(MEDIA_AUDIO == ptCC->MediaStreams[i].mediaType)
            {
                (void)MdeSetSdpAudioMedia(ptUCC_Audio, sdpIPV4, pSendSdpMsg, ptCC->servType);
                break;
            }
        }
    }
    if(ptVideoSdp && ptUCC_Video)
    {
        while(ptVideoSdp->a_attributes.nb_elt)
            osip_list_remove(&ptVideoSdp->a_attributes, 0);
        while(ptVideoSdp->m_payloads.nb_elt)
            osip_list_remove(&ptVideoSdp->m_payloads, 0);
        while(ptVideoSdp->c_connections.nb_elt)
            osip_list_remove(&ptVideoSdp->c_connections, 0);

        /* set audio|video media info for 200-invite */
        UCHAR i = 0;
        for(i=0; i < ptCC->MediaStreamNum; i++)
        {
            if(MEDIA_VIDEO == ptCC->MediaStreams[i].mediaType)
            {
                (void)MdeSetSdpVideoMedia(ptUCC_Video, pSendSdpMsg, ptCC->servType);
                break;
            }
        }
    }

    XPOC_TRACEFUNC_OUT;
    return XPOC_ERR_SUCCESS;   
}


/*当收到的SDP为空,在回复SDP时候,填充SDP内容*/
UINT mdeBuildSendSdpOnRecvNullSdp
(
    T_MdeCallCtxTable *ptCC,
    T_MdeUCallCtxTable *ptUCC_Audio,
    T_MdeUCallCtxTable *ptUCC_Video,
    sdp_message_t *pSendSdpMsg
)
{
    UINT i = 0,j = 0;

    XPOC_TRACEFUNC_IN;
    X_NULL_POINTER_CHK_TWO_PARA(ptCC, pSendSdpMsg);

    x_InfoLog("servType[%d],MediaStreamNum[%d],IsValidFg[%d],mediaType[%d],UCC_Audio[%p],UCC_Video[%p].\n", 
                ptCC->servType, ptCC->MediaStreamNum, ptCC->MediaStreams[0].IsValidFg, ptCC->MediaStreams[0].mediaType, 
                ptUCC_Audio, ptUCC_Video);
     
    /*如果CallCtx中SDP信息无效*/
    if(FALSE == ptCC->MediaStreams[0].IsValidFg) 
    {
        x_WarnLog("No Valid Media.\n");
        return XPOC_ERR_FAIL;
    }

    T_ModCtxS *ptModCtx = Fplat_GetModuleCtx(XMOD_TYPE_MDE);
    MdeGlbData_S* ptGlbData = (MdeGlbData_S*)ptModCtx->pAppData;
    char *att_field = NULL;
    char *att_value = NULL;

    int pos = -1;
    for(i = 0; i<ptCC->MediaStreamNum && i<MEDIA_TYPE_MAX; i++)
    {
        if(MEDIA_AUDIO == ptCC->MediaStreams[i].mediaType )
        { 
            //m行 
            char *media=osip_malloc(16*sizeof(UCHAR));
            char *proto=osip_malloc(16*sizeof(UCHAR));

            sprintf(media,"audio");
            sprintf(proto,"RTP/AVP");
            
            sdp_message_m_media_add (pSendSdpMsg, media, NULL, NULL, proto);

            sdp_media_t * audio_sdp = eXosip_get_audio_media(pSendSdpMsg);
            pos = MdeGetMediaPos(pSendSdpMsg, (char*)"audio");
            if(-1 == pos)
            {
                x_WarnLog("get audio media pos from sdp failed.\n");
                return XPOC_ERR_FAIL;
            }
            for (j = 0; j < ptCC->MediaStreams[i].bCodecNum ; j++)
            {
                char *payload = osip_malloc(4*sizeof(UCHAR));
                sprintf(payload,"%d",ptCC->MediaStreams[i].mediaCodec[j].ulCodecNo);
                osip_list_add(&audio_sdp->m_payloads,payload, j);

                /* 对audio增加a行: a=rtcp:40001 IN IP4 192.168.101.254 */
                UINT Ip4 = 0;
                if(Serv_AudioRcrdPri == ptCC->servType
                    || Serv_VideoRcrdPri == ptCC->servType
                    || Serv_AudioRcrdGrp == ptCC->servType
                    || Serv_VideoRcrdGrp == ptCC->servType)
                {
                    Ip4 = ptGlbData->stCfgInfo.ulLocalIp;
                }
                else
                {
                    Ip4 = ptGlbData->stCfgInfo.ulAceRmtIp;
                }


                char addr[STR_LEN32] = {0};
                USHORT usRtcpPort = 0;
                memset(addr, 0, STR_LEN32);

                sprintf(addr, "%d.%d.%d.%d", Ip4>>24&0xFF, Ip4>>16&0xFF, Ip4>>8&0xFF, Ip4&0xFF);
                if(ptUCC_Audio)
                {
                    //  modified by shm, 解决rce用内网,终端用外网问题;此处分布式安装时,还需更改
                    if(Serv_AudioRcrdPri == ptCC->servType
                        || Serv_VideoRcrdPri == ptCC->servType
                        || Serv_AudioRcrdGrp == ptCC->servType
                        || Serv_VideoRcrdGrp == ptCC->servType)
                    {
                        // rce
                        usRtcpPort = ptGlbData->stUdpAddrInfo.udpRtcpFd.usPort;
                    }
                    else
                    {
                        usRtcpPort = ptGlbData->stCfgInfo.udpRmtRtcpPort;
                    }
                }
                att_field = osip_malloc(128*sizeof(UCHAR));
                att_value = osip_malloc(128*sizeof(UCHAR));
                sprintf(att_field,"rtcp");
                sprintf(att_value,"%d IN IP4 %s", usRtcpPort, addr);
                sdp_message_a_attribute_add(pSendSdpMsg, pos, att_field, att_value);


                att_field = osip_malloc(128*sizeof(UCHAR));
                att_value = osip_malloc(128*sizeof(UCHAR));
                sprintf(att_field,"rtpmap");
                sprintf(att_value,"%d %s/%d ",ptCC->MediaStreams[i].mediaCodec[j].ulCodecNo,
                        ptCC->MediaStreams[i].mediaCodec[j].aucRtpMap,
                        ptCC->MediaStreams[i].mediaCodec[j].ulClockRate);
                sdp_message_a_attribute_add (pSendSdpMsg, pos, att_field, att_value);

                if(0!=strlen(ptCC->MediaStreams[i].mediaCodec[j].aucFrmPm))
                {
                    att_field = osip_malloc(128*sizeof(UCHAR));
                    att_value = osip_malloc(128*sizeof(UCHAR));
                    sprintf(att_field,"fmtp");
                    sprintf(att_value,"%s ",ptCC->MediaStreams[i].mediaCodec[j].aucFrmPm);
                    
                    sdp_message_a_attribute_add (pSendSdpMsg, pos, att_field, att_value);
                }

                //m=audio 29010 ...
                char *sPort = osip_malloc(8*sizeof(UCHAR));

                if(ptUCC_Audio)
                {
                    // modified by shm, 解决外网端口映射问题
                    //  modified by shm, 解决rce用内网,终端用外网问题;此处分布式安装时,还需更改
                    if(Serv_AudioRcrdPri == ptCC->servType
                        || Serv_VideoRcrdPri == ptCC->servType
                        || Serv_AudioRcrdGrp == ptCC->servType
                        || Serv_VideoRcrdGrp == ptCC->servType)
                    {
                        // rce
                        sprintf(sPort, "%d", ptGlbData->stCfgInfo.udpRtpPort);
                    }
                    else
                    {
                        sprintf(sPort, "%d", ptGlbData->stCfgInfo.udpRmtRtpPort);
                    }
                }
                else
                {
                    strcpy(sPort, "ERR!");
                }
                sdp_message_m_port_set(pSendSdpMsg, pos, sPort);
                
                att_field = osip_malloc(16*sizeof(UCHAR));
                mdeTopoly2Str(SDP_MODE_SENDRECV, att_field);
                sdp_message_a_attribute_add(pSendSdpMsg, pos, att_field, NULL);
            }
        }
        else if(MEDIA_VIDEO == ptCC->MediaStreams[i].mediaType )
        {
            //m行 
            char *media=osip_malloc(16*sizeof(UCHAR));
            char *proto=osip_malloc(16*sizeof(UCHAR));

            sprintf(media,"video");
            sprintf(proto,"TCP/RTP/AVP");
            
            sdp_message_m_media_add (pSendSdpMsg, media, NULL, NULL, proto);

            sdp_media_t * video_sdp = eXosip_get_video_media(pSendSdpMsg);
            pos = MdeGetMediaPos(pSendSdpMsg, (char*)"video");
            if(-1 == pos)
            {
                x_WarnLog("get video media pos from sdp failed.\n");
                return XPOC_ERR_FAIL;
            }
            for (j = 0; j < ptCC->MediaStreams[i].bCodecNum ; j++)
            {
                char *payload = osip_malloc(4*sizeof(UCHAR));
                sprintf(payload,"%d",ptCC->MediaStreams[i].mediaCodec[j].ulCodecNo);
                osip_list_add(&video_sdp->m_payloads,payload, j);

                att_field = osip_malloc(128*sizeof(UCHAR));
                att_value = osip_malloc(128*sizeof(UCHAR));

                sprintf(att_field,"rtpmap");
                sprintf(att_value,"%d %s/%d ",ptCC->MediaStreams[i].mediaCodec[j].ulCodecNo,
                        ptCC->MediaStreams[i].mediaCodec[j].aucRtpMap,
                        ptCC->MediaStreams[i].mediaCodec[j].ulClockRate);
                
                sdp_message_a_attribute_add (pSendSdpMsg, pos, att_field, att_value);

                if(0!=strlen(ptCC->MediaStreams[i].mediaCodec[j].aucFrmPm))
                {
                    att_field = osip_malloc(128*sizeof(UCHAR));
                    att_value = osip_malloc(128*sizeof(UCHAR));
                    sprintf(att_field,"fmtp");
                    sprintf(att_value,"%s ",ptCC->MediaStreams[i].mediaCodec[j].aucFrmPm);
                    
                    sdp_message_a_attribute_add (pSendSdpMsg, pos, att_field, att_value);
                }

                //m=audio 20400 ...
                char *sPort = osip_malloc(8*sizeof(UCHAR));
                if(ptUCC_Video)
                {
                    //  modified by shm, 解决rce用内网,终端用外网问题;此处分布式安装时,还需更改
                    if(Serv_AudioRcrdPri == ptCC->servType
                        || Serv_VideoRcrdPri == ptCC->servType
                        || Serv_AudioRcrdGrp == ptCC->servType
                        || Serv_VideoRcrdGrp == ptCC->servType)
                    {
                        // rce
                        sprintf(sPort, "%d", ptGlbData->stTcpAddrInfo.tcpListenFd.usPort);
                    }
                    else
                    {
                        sprintf(sPort, "%d", ptGlbData->stCfgInfo.tcpRmtPort);
                    }
                }
                else
                {
                    strcpy(sPort, "ERR");
                }
                sdp_message_m_port_set(pSendSdpMsg, pos, sPort);
                
                //a=sendrecv
                att_field = osip_malloc(16*sizeof(UCHAR));
                mdeTopoly2Str(SDP_MODE_SENDRECV, att_field);
                sdp_message_a_attribute_add(pSendSdpMsg, pos, att_field, NULL);
            }
        }
    }

    //t
    char *start = osip_malloc(4*sizeof(UCHAR));
    char *stop = osip_malloc(4*sizeof(UCHAR));
    sprintf(start, "0");
    sprintf(stop, "0");
    sdp_message_t_time_descr_add(pSendSdpMsg, start, stop);

    //s
    char *sname = osip_malloc(32*sizeof(char));
    sprintf(sname, "xpoc");
    sdp_message_s_name_set(pSendSdpMsg, sname);

    XPOC_TRACEFUNC_OUT;
    return XPOC_ERR_SUCCESS;   
}

UINT mdeBuildSdpForInviteRsp
(
    T_ModCtxS *ptModCtx,
    T_ModMsgHead *ptXpocMsgHdr,
    T_MdeCallCtxTable *ptCC,
    T_MdeUCallCtxTable *ptUCC_Audio,
    T_MdeUCallCtxTable *ptUCC_Video,
    sdp_message_t **pptSendSdp
)
{
    XPOC_TRACEFUNC_IN;
    X_NULL_POINTER_CHK_THREE_PARA(ptModCtx, ptXpocMsgHdr, ptCC);

    UINT ulReturn = XPOC_ERR_SUCCESS;
    sdp_message_t *ptSendSdpTmp = NULL;

    SipMsgIndS *ptSipMsgInd = (SipMsgIndS*)ptXpocMsgHdr->stMsgBody;
    sdp_message_t *ptRecvSdp = eXosip_get_sdp_info(ptSipMsgInd->ulSipHandle->request);

    if(NULL != ptRecvSdp)
    {
        sdp_message_clone(ptRecvSdp, &ptSendSdpTmp);
        sdp_message_free(ptRecvSdp);
        ptRecvSdp = NULL;
    }
    else
    {
        sdp_message_init(&ptSendSdpTmp);
    }
    *pptSendSdp = ptSendSdpTmp;
    sdp_media_t *ptAudioSdp = eXosip_get_audio_media(ptSendSdpTmp);
    sdp_media_t *ptVideoSdp = eXosip_get_video_media(ptSendSdpTmp);
    if((ptAudioSdp && ptUCC_Audio) || (ptVideoSdp && ptUCC_Video))
    {
        ulReturn = mdeBuildSendSdpOnRecvSdp(ptCC, ptAudioSdp, ptVideoSdp, ptUCC_Audio, ptUCC_Video, ptSendSdpTmp);
    }
    else
    {
        ulReturn = mdeBuildSendSdpOnRecvNullSdp(ptCC, ptUCC_Audio, ptUCC_Video, ptSendSdpTmp);    
    }

    MdeGlbData_S *ptGlbData = (MdeGlbData_S*)ptModCtx->pAppData;
    UINT IPV4 = 0;
    if(Serv_AudioRcrdPri == ptCC->servType
        || Serv_VideoRcrdPri == ptCC->servType
        || Serv_AudioRcrdGrp == ptCC->servType
        || Serv_VideoRcrdGrp == ptCC->servType)
    {
        IPV4 = ptGlbData->stCfgInfo.ulLocalIp;
    }
    else
    {
        IPV4 = ptGlbData->stCfgInfo.ulAceRmtIp;
    }

    (void)mdeBuildSdpCommInfo(ptSendSdpTmp, IPV4, ptCC->MediaStreamNum);

    XPOC_TRACEFUNC_OUT;
    return ulReturn;
}


#if 0
#endif

UINT mdeSendInviteRsp(SipMsgIndS *pRcvSipMsg, sdp_message_t *SdpSession, MdeCtrlBlockS *ptCB, UINT StatusCode)
{
    UINT  ulReturn = XPOC_ERR_SUCCESS;   
    char *ptNewBody = NULL;

    XPOC_TRACEFUNC_IN;
    X_NULL_POINTER_CHK_TWO_PARA(pRcvSipMsg, ptCB);

    SipMsgRspS stSipRsp;
    memset(&stSipRsp, 0, sizeof(SipMsgRspS));    

    stSipRsp.sipMsgId = EV_SIP_INVITE_REQ;
    stSipRsp.nTid = pRcvSipMsg->ulSipHandle->tid;
    stSipRsp.ulSrcCbId = ptCB->ulCBId; //pRcvSipMsg->ulDstCbId;
    stSipRsp.statusCode = StatusCode;

    stSipRsp.stSipHeader.ulServType = Service_audio;

    if(NULL !=  SdpSession)
    {
        x_DebugLog("SDP:v[%p],o_u[%p],o_s_id[%p],o_s_ver[%p],o_net[%p],o_adrtype[%p],o_adr[%p].",
            SdpSession->v_version, SdpSession->o_username,SdpSession->o_sess_id,SdpSession->o_sess_version,
            SdpSession->o_nettype,SdpSession->o_addrtype,SdpSession->o_addr);

        sdp_message_to_str(SdpSession, &ptNewBody);

        if(ptNewBody)
        {
            UINT ret = XPOCGetSipHeaderContentType(pRcvSipMsg->ulSipHandle->response, stSipRsp.stSipBodyPart.aucContentType);
            if(XPOC_ERR_SUCCESS != ret)
            {
                sprintf(stSipRsp.stSipBodyPart.aucContentType, "application/sdp");
            }
            stSipRsp.stSipBodyPart.ulXmlBodyLen = strlen(ptNewBody);
            memcpy((char*)stSipRsp.stSipBodyPart.xmlBody, ptNewBody, stSipRsp.stSipBodyPart.ulXmlBodyLen);
        }
    }
    else
    {
        x_WarnLog("pSendSdpMsg is NULL.");
    }

    if(0 != ptCB->ulCallCtxId)
    {
        stSipRsp.stSipHeader.ubSessionIdFg = TRUE;
        sprintf(stSipRsp.stSipHeader.aucSessionId, "%d", ptCB->ulCallCtxId);        
    }

    int nThread = ptCB->ulCceLegId % (SIP_THREAD_BUTT - 2) + 1;
    ulReturn = XPOCSendSipRspMsg(&stSipRsp, 0, nThread);

    if(ptNewBody)
    {
        osip_free(ptNewBody);
        sdp_message_free(SdpSession);
        ptNewBody = NULL;
        SdpSession = NULL;
    }

    XPOC_TRACEFUNC_OUT ;
    return ulReturn;
}

/*UINT MdeSendBye(void * pstMdeVoidSpCtx, UINT ulCbId)
{
    XPOC_TRACEFUNC_IN;
    X_NULL_POINTER_CHK_ONE_PARA(pstMdeVoidSpCtx);

    UINT ulRetCode = XPOC_ERR_SUCCESS;
    // SipMsgReqS stSipReq;
    // memset(&stSipReq, 0, sizeof(SipMsgReqS));

    // MdeCtrlBlockS *ptCB = (MdeCtrlBlockS*)pstMdeVoidSpCtx;

    // stSipReq.sipMsgId = EV_SIP_BYE_REQ;
    // stSipReq.ulSrcCbId = ulCbId;
    // stSipReq.ulSipHandle = ptCB->ptSpSipHandle;
    // stSipReq.stSipHeader.ulServType = Service_audio;

    // UINT ulTransId;
    // ulRetCode = XPOCSendSipReqMsg(&stSipReq, &ulTransId, SIP_THREAD_TWO);

    XPOC_TRACEFUNC_OUT;
    return ulRetCode; 

}*/


#if 0
UINT mdeSendFleetProfileReq2Http(char *ptFleetId)
{
    XPOC_TRACEFUNC_IN;
    X_NULL_POINTER_CHK_ONE_PARA(ptFleetId);

    UCHAR *ptPlatHdr = NULL;
    UINT ulMsgLen = sizeof(FleetProfileReqS);
    ptPlatHdr = (UCHAR*)XPOCAllocMsg(ulMsgLen);
    if(NULL == ptPlatHdr)
    {
        x_WarnLog ("Alloc Plat message header error, ulMsgLen[%d], fleetId[%s]!\n", ulMsgLen, ptFleetId);
        return XPOC_ERR_INVALID_PTR;
    }
    T_ModMsgHead *ptXPOCHeader = XPOCGetXpocHeaderPtr(ptPlatHdr);
    if(NULL == ptXPOCHeader)
    {
        XPOCFreeMsg(ptPlatHdr);
        return XPOC_ERR_INVALID_PTR;
    }

    //fill in the message header
    ptXPOCHeader->ulSrcCbId = 0;
    ptXPOCHeader->ulDstCbId = 0;
    ptXPOCHeader->ulMsgId = EV_JOIN_GET_FLEETPROFILE_REQ;
    ptXPOCHeader->ulMsgLen = ulMsgLen;


    FleetProfileReqS *ptReq = (FleetProfileReqS *)ptXPOCHeader->stMsgBody;
    memset(ptReq, 0, sizeof(FleetProfileReqS));
    strcpy(ptReq->aucFleetId, ptFleetId);

    SendDstInfoS stDstModInfo;
    memset(&stDstModInfo, 0, sizeof(SendDstInfoS));
    stDstModInfo.dstModId = XMOD_TYPE_HTTP;
    stDstModInfo.apiId = ptXPOCHeader->ulMsgId;
    stDstModInfo.prio = 0;
    stDstModInfo.dstPID = 0;

    //begin to send message
    UINT ulReturn = XPOCDispatchMsg(&stDstModInfo, ptPlatHdr);
    if(XPOC_ERR_SUCCESS == ulReturn)
    {
        x_InfoLog("MDE--->HTTP:EV_JOIN_GET_FLEETPROFILE_REQ OK, fleetId[%s]!\n", ptFleetId);
    }
    else
    {
        x_WarnLog("MDE--->HTTP:EV_JOIN_GET_FLEETPROFILE_REQ failed, fleetId[%s]!\n", ptFleetId);
    }

    XPOC_TRACEFUNC_OUT;
    return ulReturn;    
}
#endif

#if 0
#endif


UINT mdeHandleSipInvite(T_ModCtxS *ptModCtx, T_ModMsgHead* ptXpocMsgHdr)
{
    XPOC_TRACEFUNC_IN;
    X_NULL_POINTER_CHK_TWO_PARA(ptModCtx, ptXpocMsgHdr);

    UINT ulReturn = XPOC_ERR_SUCCESS;
    UINT ulCBId = 0;

    MdeCtrlBlockS *ptCB = NULL;
    T_MdeCallCtxTable stCC;
    T_MdeUCallCtxTable stUCC;
    
    SipMsgIndS *ptSipMsgInd = (SipMsgIndS*)ptXpocMsgHdr->stMsgBody;
    sdp_message_t *ptSendSdp = NULL;
    CallInfoS stCallInfo;

    UINT ulServCallCtrlId = 0;
    char aucServId[STR_LEN32] = {0};
    char aucOrigIp[STR_LEN32] = {0};
    UCHAR iFirstUserFg = 0;
    UCHAR iHasSdpFg = 0;
    UINT ulCCId = 0;
    CallCtrlIdU stCallId = {0};
    CallModeE enMode = Mode_Null;

    memset(&stCC, 0, sizeof(T_MdeCallCtxTable));
    memset(&stCallInfo, 0, sizeof(CallInfoS));

    T_MdeUCallCtxTable *ptUCC_Audio = NULL;
    T_MdeUCallCtxTable *ptUCC_Video = NULL;

    T_MdeUCallCtxTable stUCC_Audio;
    T_MdeUCallCtxTable stUCC_Video;
    memset(&stUCC_Audio, 0, sizeof(T_MdeUCallCtxTable));
    memset(&stUCC_Video, 0, sizeof(T_MdeUCallCtxTable));

    g_TcpSndCount = 0;
    g_TcpRcvCount = 0;

    MediaDescriptionS stAudio;
    MediaDescriptionS stVideo;
    memset(&stAudio, 0, sizeof(MediaDescriptionS));
    memset(&stVideo, 0, sizeof(MediaDescriptionS));

    /*
    MDE收到invite要做的事情:
    1. get CB
    2. alloc/get CallCtx;
    3. alloc/get UCallCtx;
    4. get media codec from src invite.sdp;
    5. save media codec para into UCallCtx;
    6. construct sdp for 200-invite;
    7. send 200-invite to CCE;
    */

    do
    {
        /* [0] get para from sip message */
        (void)mdeGetOnlineServCallCtrlId(ptSipMsgInd, &ulServCallCtrlId);
        (void)mdeGetMediaCcidFromInvite(ptSipMsgInd->ulSipHandle->request, &ulCCId);
        (void)mdeGetServIdFromSipHeader(ptSipMsgInd->ulSipHandle->request, aucServId);
        (void)mdeGetOrigIpFromSipHeader(ptSipMsgInd->ulSipHandle->request, aucOrigIp);
        (void)XPOCGetCallnfoSipHeader(ptSipMsgInd->ulSipHandle->request, &stCallInfo);/* vidgrptrans,vidpritrans,record 时使用来传递mediaCCId */
        stCallId.ulCtrlId = ulServCallCtrlId;
        stCC.ulServCCId = stCallId.stCtrlInfoS.ulCallCtxId;
        stCC.servType = stCallId.stCtrlInfoS.ubServType;
        UINT nLegId = -1;
        XPOCCommGetCallCtrlIdU(&stCallId, &stCC.ulServCCId, &nLegId, (UCHAR *)&stCC.servType, (UCHAR *)&enMode, NULL);

        char acCallId[64] = {0};
        XPOCGetSipHeaderCallId(ptSipMsgInd->ulSipHandle->request, acCallId);
        x_InfoLog("[X]recv invite, call-id[%s], servId[%s], servType[%d], servCCId[%d], srcMediaCCId[%d].", 
                    acCallId, aucServId, stCC.servType, stCC.ulServCCId, stCallInfo.ulSrcMediaCCId);

        /* 对于主动迟后接入, 作为被叫侧处理,但是在invite中携带了sdp,且需要保存信息 */
        SipMsgIndS *ptSipInd = (SipMsgIndS*)ptXpocMsgHdr->stMsgBody;
        sdp_message_t *ptSdp = eXosip_get_sdp_info(ptSipInd->ulSipHandle->request);
        iHasSdpFg = (ptSdp) ? TRUE : FALSE;

        iFirstUserFg = (0 == ulCCId) ? TRUE : FALSE;
        if (ptSdp)
        {
            sdp_message_free(ptSdp);
            ptSdp = NULL;
        }

        /* [1] alloc/get CB */
        ulReturn = mdeGetCB(ptXpocMsgHdr, &ulCBId, &ptCB);
        if(XPOC_ERR_SUCCESS != ulReturn || NULL == ptCB)
        {
            x_WarnLog("mde get CtrlBlock failed, retcode[%d],ptCB[%p].\n", ulReturn, ptCB);
            SipMsgRspS stSipRsp;
            memset(&stSipRsp, 0, sizeof(SipMsgRspS));
            stSipRsp.sipMsgId = EV_SIP_INVITE_REQ;
            stSipRsp.nTid = ptSipMsgInd->ulSipHandle->tid;
            stSipRsp.ulSrcCbId = 0;
            stSipRsp.statusCode = 403;
            stSipRsp.stSipHeader.ulServType = Service_audio;
            int nThread = nLegId % (SIP_THREAD_BUTT - 2) + 1;
            ulReturn = XPOCSendSipRspMsg(&stSipRsp, 0, nThread);
            return ulReturn;
        }
        ptCB->ulCurrMsgId = ptXpocMsgHdr->ulMsgId;
        //ptCB->ptSpSipHandle = ptSipMsgInd->ulSipHandle;
        ptCB->ulCceLegId = nLegId;

        /* [2] Alloc/get CallCtx */
        ulReturn = mdeGetCallCtx(ptModCtx, ulCCId, &stCC);
        if(XPOC_ERR_SUCCESS != ulReturn)
        {
            /* 对于invite消息,如果此时失败,说明资源被占用,释放之前呼叫的资源, 分配当前呼叫的呼叫. */
            if(EV_SIP_INVITE_IND == ptXpocMsgHdr->ulMsgId)
            {
                (void)mdeFreeAllResource(stCC.ulServCCId, stCC.servType);
                ulReturn = mdeGetCallCtx(ptModCtx, ulCCId, &stCC);
                if(XPOC_ERR_SUCCESS != ulReturn)
                    break;
            }
            else
                break;
        }

        /* 对于业务:视频转发,转发组. 需要把该业务的mediaCCId记录到媒体源的mediaCCId中.
           便于后续转发媒体源的报文给此业务用户. 当前业务只在主叫侧携带 srcMediaCCId 即可. */
        if(iFirstUserFg 
            && (0 != stCallInfo.ulSrcMediaCCId && INVALID_VALUE_UINT != stCallInfo.ulSrcMediaCCId)
            && (Serv_VideoTransPri == stCC.servType || Serv_VideoTransGrp == stCC.servType))
        {
            (void)mdeSaveTransTargetCCId2SrcCC(stCallInfo.ulSrcMediaCCId, stCC.servType, stCC.ulRecordIdx);
            stCC.ulSrcMediaCCId = stCallInfo.ulSrcMediaCCId;
        }

        /* 对于用户/组 录音录像, 把recordCCId记录到录音录像媒体源的UCC中. */
        if(iFirstUserFg 
            && (0 != stCallInfo.ulSrcMediaCCId 
                && INVALID_VALUE_UINT != stCallInfo.ulSrcMediaCCId)
            && (Serv_AudioRcrdPri == stCC.servType
                || Serv_VideoRcrdPri == stCC.servType
                || Serv_AudioRcrdGrp == stCC.servType
                || Serv_VideoRcrdGrp == stCC.servType))
        {
            (void)mdeSaveRcrdCCId2SrcUCC(stCallInfo.ulSrcMediaCCId, stCC.ulRecordIdx, stCC.servType, aucServId);
        }

        /* [3] get media codec from src invite.sdp; */
        /* 媒体协商,caller: get media from invite.sdp; callee: get media from CC.mediaStream */
        if(iFirstUserFg || iHasSdpFg)
        {
            ulReturn = mdeGetMediaInfoFromSipInvite(ptXpocMsgHdr, &stAudio, &stVideo);
            if(XPOC_ERR_SUCCESS != ulReturn)
            {
                break;
            }
            if(0 == stAudio.bCodecNum && 0 == stVideo.bCodecNum)
            {
                x_WarnLog("get media info from caller.invite.sdp failed.");
                break;
            }
            if(iFirstUserFg) /* 主动迟后时不保存到CC中 */
            {
                (void)mdeSaveMediaDescription2CC(&stAudio, &stVideo, &stCC);
            }
        }

        /* [4] get UCallCtx, open socket, attach UCC2CC for audio media and video media(if exist) */
        UCHAR i = 0;
        for(i=0; i<stCC.MediaStreamNum; i++)
        {
            memset(&stUCC, 0, sizeof(T_MdeUCallCtxTable));
            /* 主叫侧保存 mediaDescription, 被叫侧的mediaDescription等待re-invite.mediaInfo */
            if(iFirstUserFg || iHasSdpFg)
            {
                stUCC.enMediaType = stCC.MediaStreams[i].mediaType;
                (void)mdeSaveMediaDescription2UCC(&stAudio, &stVideo, &stUCC);

                /* 当前业务为 视频转发(组)的发起方, 设置标识. */
                if(Serv_VideoTransPri == stCC.servType || Serv_VideoTransGrp == stCC.servType)
                {
                    stUCC.ucTransCallerFg = TRUE;
                }
            }

            /* 视频上拉被叫方/视频回传主叫方 作为转发(组)的媒体源 */
            if(MEDIA_VIDEO ==stCC.MediaStreams[i].mediaType /* 防止意外带了音频参数 */
               && ((Serv_VideoPullPri == stCC.servType && Mode_Term ==enMode)|| (Serv_VideoPushPri == stCC.servType && Mode_Orig ==enMode)))
            {
                stUCC.ucTransMediaSrcFg = TRUE;
            }

            // alloc UCC
            memcpy(stUCC.aucServId, aucServId, strlen(aucServId));
            memcpy(stUCC.aucOrigIp, aucOrigIp, strlen(aucOrigIp));
            stUCC.ulServCCId = stCC.ulServCCId;
            stUCC.enMediaType = stCC.MediaStreams[i].mediaType;
            stUCC.servType = stCC.servType;
            stUCC.ulCBId = ptCB->ulCBId;
            stUCC.ulCallCtxId = stCC.ulRecordIdx;
            getCurrTime(stUCC.szCreateTime, sizeof(stUCC.szCreateTime)-1);/*uCallCtx创建时间*/

            /* 对于呼叫类业务, 检查其CC中是否存在rcrdCCId. 如果存在 说明当前是迟后接入的用户. 需要在此用户的UCC中保存 rcrdCCId. */
            if(!(Serv_AudioRcrdPri == stCC.servType
                    || Serv_VideoRcrdPri == stCC.servType
                    || Serv_AudioRcrdGrp == stCC.servType
                    || Serv_VideoRcrdGrp == stCC.servType)
               && (0 != stCC.ulRcrdCCId))
            {
                stUCC.ulRcrdCCId = stCC.ulRcrdCCId;
            }

            ulReturn = XMdeUCallCtxOpr(XDB_OPR_ADD, stUCC.aucServId, stUCC.ulServCCId, stUCC.enMediaType, stUCC.servType, NULL, NULL, &stUCC);
            if(XPOC_ERR_SUCCESS != ulReturn)
            {
                // modified by shm, 先删除,再增加,若还是失败,则break
                ulReturn = XMdeUCallCtxOpr(XDB_OPR_DEL, stUCC.aucServId, stUCC.ulServCCId, stUCC.enMediaType, stUCC.servType, NULL, NULL, NULL);
                if (XPOC_ERR_SUCCESS != ulReturn)
                {
                     break;
                }
                
                ulReturn = XMdeUCallCtxOpr(XDB_OPR_ADD, stUCC.aucServId, stUCC.ulServCCId, stUCC.enMediaType, stUCC.servType, NULL, NULL, &stUCC);
                if(XPOC_ERR_SUCCESS != ulReturn)
                {
                    break;
                }
               
            }

            x_InfoLog("UCC:sip call-id[%s], servId[%s],ssrc[0x%x(%u)], cce ccid[%d], mde ccid [%d], uccid[%d], cbid [%d], mediaType[%d],servType[%d],origIp[%s].", 
                      acCallId, stUCC.aucServId, stUCC.ulSsrc, stUCC.ulSsrc, stUCC.ulServCCId, stCC.ulRecordIdx, stUCC.ulRecordIdx, ptCB->ulCBId, stUCC.enMediaType, stUCC.servType, stUCC.aucOrigIp);

            // attach UCC to CC
            ulReturn = mdeAttachUCCToCC(&stCC, &stUCC);                    
            if(XPOC_ERR_SUCCESS != ulReturn)
                break;

            // update CB info.
            if(ptCB->StreamNum >= MEDIA_TYPE_MAX)
            {
                x_WarnLog("CB StreamInfoArray Index[%u] over MAX_STREAM_NUM[%u]", ptCB->StreamNum, MEDIA_TYPE_MAX);
                ptCB->StreamNum = 0;
            }
            ptCB->StreamInfoArray[ptCB->StreamNum].ulUCallCtxId = stUCC.ulRecordIdx;
            ptCB->StreamNum++;
            x_InfoLog("[CB] modify stream:num[%d],UCCId[%d-%d].", 
                      ptCB->StreamNum, ptCB->StreamInfoArray[0].ulUCallCtxId, ptCB->StreamInfoArray[1].ulUCallCtxId);

            if(MEDIA_AUDIO == stUCC.enMediaType)
            {
                memcpy(&stUCC_Audio, &stUCC, sizeof(T_MdeUCallCtxTable));
                ptUCC_Audio = &stUCC_Audio;
            }
            else if(MEDIA_VIDEO == stUCC.enMediaType)
            {
                memcpy(&stUCC_Video, &stUCC, sizeof(T_MdeUCallCtxTable));
                ptUCC_Video = &stUCC_Video;/* video UCC */
            }
        }

        if(ptCB->StreamNum != 0)
            ptCB->ulCallCtxId = stCC.ulRecordIdx;

        UINT ulReturn1 = mdeAddCBToCC(&stCC, ptCB->ulCBId);
        if(XPOC_ERR_SUCCESS != ulReturn || XPOC_ERR_SUCCESS != ulReturn1)   // modified by shm
            break;

        (void)mdeBuildSdpForInviteRsp(ptModCtx, ptXpocMsgHdr, &stCC, ptUCC_Audio, ptUCC_Video, &ptSendSdp);
    }while(0);

    SipStatusCodeE enSipStatus = (XPOC_ERR_SUCCESS == ulReturn) ? 200 : 403;
    (void)mdeSendInviteRsp(ptSipMsgInd, ptSendSdp, ptCB, enSipStatus);
    x_InfoLog("Send invite rsp, cce ccid [%d], mde ccid [%d]", stCC.ulServCCId, stCC.ulRecordIdx);
    (void)mdePrintMediaInfo(ptModCtx, stCC.ulRecordIdx);
  
    /* 失败处理,要回收资源... */
    if(XPOC_ERR_SUCCESS != ulReturn)
    {
        /* 回收控制块资源和对应的UCallCtx资源 */
        if(ptCB)
        {
            UCHAR i = 0;
            T_MdeUCallCtxTable stUCCTmp;
            for(i = 0; i < ptCB->StreamNum; i++)
            {
                ulReturn = XMdeUCallCtxOpr(XDB_OPR_QRY, NULL, 0, 0, 0, NULL, &ptCB->StreamInfoArray[i].ulUCallCtxId, &stUCCTmp);
                if(XPOC_ERR_SUCCESS != ulReturn)
                {
                    mdeCloseMediaSocket(&stUCCTmp);
                    (void)mdeDetachUCCFromCC(stCC.ulRecordIdx, ptCB->StreamInfoArray[i].ulUCallCtxId);
                    (void)XMdeUCallCtxOpr(XDB_OPR_DEL, NULL, 0, 0, 0, NULL, &stUCCTmp.ulRecordIdx, &stUCCTmp);
                }
            }
            (void)mdeDelCBFromCC(&stCC, ptCB->ulCBId);
            (void)XPOCFreeCtrlBlock(ptCB->ulCBId, 0);
        }

        /* 若是主叫的invite,还需回收CallCtx资源 */
        if(1 == iFirstUserFg)
        {
            /* 资源释放 */
            ulReturn = XMdeCallCtxOpr(XDB_OPR_DEL, NULL, 0, &stCC.ulRecordIdx, &stCC);
        }
    }
  
    XPOC_TRACEFUNC_OUT;
    return XPOC_ERR_SUCCESS;
}

UINT mdeHandleSipReInvite(T_ModCtxS *ptModCtx, T_ModMsgHead* ptXpocMsgHdr)
{
    XPOC_TRACEFUNC_IN;
    X_NULL_POINTER_CHK_TWO_PARA(ptModCtx, ptXpocMsgHdr);
    
    UINT ulReturn = XPOC_ERR_FAIL;
    UINT  ulCallCtxId = 0;
    MdeCtrlBlockS *ptCB = NULL;
    T_MdeCallCtxTable stCC;
    sdp_message_t *ptSendSdp = NULL;

    memset(&stCC, 0, sizeof(T_MdeCallCtxTable));

    /*分析SIP消息头 */
    SipMsgIndS *ptSipMsgInd = (SipMsgIndS*)ptXpocMsgHdr->stMsgBody;

    UINT ulCBId = ptSipMsgInd->ulDstCbId;
    do
    {
        /* check reinvite.sdp must exist */
        sdp_message_t *ptRecvSdp = eXosip_get_sdp_info(ptSipMsgInd->ulSipHandle->request);
        if(!ptRecvSdp) /*收到SDP为空,认为出错,因为此时(ReInvite)不可以再传输无SDP的内容信息*/
        {
            x_WarnLog("recv ReInvite.sdp is NULL, CCId[%d],CBId[%d].\n", ulCallCtxId, ulCBId);
            break;
        }
        else
        {
            sdp_message_free(ptRecvSdp);
            ptRecvSdp = NULL;
        }

        ulReturn = mdeGetCB(ptXpocMsgHdr, &ulCBId, &ptCB);
        if(XPOC_ERR_SUCCESS != ulReturn || NULL == ptCB)
        {
            x_WarnLog("mde get CtrlBlock failed, retcode[%d],ptCB[%p].\n", ulReturn, ptCB);
            SipMsgRspS stSipRsp;
            memset(&stSipRsp, 0, sizeof(SipMsgRspS));
            stSipRsp.sipMsgId = EV_SIP_INVITE_REQ;
            stSipRsp.nTid = ptSipMsgInd->ulSipHandle->tid;
            stSipRsp.ulSrcCbId = 0;
            stSipRsp.statusCode = 403;
            stSipRsp.stSipHeader.ulServType = Service_audio;
            
            CallCtrlIdU stCallId = {0};
            (void)mdeGetOnlineServCallCtrlId(ptSipMsgInd, &stCallId.ulCtrlId);
            int nLegId = stCallId.stCtrlInfoS.uiCtrlLegId;
            int nThread = nLegId % (SIP_THREAD_BUTT - 2) + 1;
            ulReturn = XPOCSendSipRspMsg(&stSipRsp, 0, nThread);
            return ulReturn;
        }

        if(0 == ptCB->StreamNum)
        {
            x_WarnLog("ReInvite has not valid MediaStream,CBId[%d],CCId[%d].\n", ulCBId, ptCB->ulCallCtxId);
            (void)XPOCFreeCtrlBlock(ptCB->ulCBId, 0);
            break;
        }

        char aucServId[64] = {0};
        char acCallId[64]  = {0};
        (void)mdeGetMediaCcidFromInvite(ptSipMsgInd->ulSipHandle->request, &ulCallCtxId);
        (void)mdeGetServIdFromSipHeader(ptSipMsgInd->ulSipHandle->request, aucServId);
        XPOCGetSipHeaderCallId(ptSipMsgInd->ulSipHandle->request, acCallId);
        x_InfoLog("[X]recv re-invite, call-id[%s], servId[%s], mde ccid[%d], cbId[%d].", acCallId, aucServId, ulCallCtxId, ulCBId);

        ulReturn = XMdeCallCtxOpr(XDB_OPR_QRY, NULL, 0, &ulCallCtxId, &stCC);
        if(XPOC_ERR_SUCCESS != ulReturn)
        {
            break;
        }

        MediaDescriptionS  stAudio;
        MediaDescriptionS  stVideo;
        memset(&stAudio, 0, sizeof(MediaDescriptionS));
        memset(&stVideo, 0, sizeof(MediaDescriptionS));
        stAudio.bCodecNum = INVALID_VALUE_UCHAR;
        stVideo.bCodecNum = INVALID_VALUE_UCHAR;
        ulReturn = mdeGetMediaInfoFromSipInvite(ptXpocMsgHdr, &stAudio, &stVideo);
        if(XPOC_ERR_SUCCESS != ulReturn)
        {
            break;
        }

        // update UCallCtx media
        T_MdeUCallCtxTable stUCC_Tmp;
        T_MdeUCallCtxTable *ptUCC_Audio = NULL;
        T_MdeUCallCtxTable *ptUCC_Video = NULL;
        T_MdeUCallCtxTable stUCC_Audio;
        T_MdeUCallCtxTable stUCC_Video;
        memset(&stUCC_Tmp, 0, sizeof(T_MdeUCallCtxTable));
        memset(&stUCC_Audio, 0, sizeof(T_MdeUCallCtxTable));
        memset(&stUCC_Video, 0, sizeof(T_MdeUCallCtxTable));

        int i = 0;
        for(i = 0; i < ptCB->StreamNum && i<MEDIA_TYPE_MAX; i++)
        {
            ulReturn = XMdeUCallCtxOpr(XDB_OPR_QRY, NULL, 0, 0, 0, NULL, &ptCB->StreamInfoArray[i].ulUCallCtxId, &stUCC_Tmp);
            if(XPOC_ERR_SUCCESS == ulReturn)
            {
                if(MEDIA_AUDIO == stUCC_Tmp.enMediaType)
                {
                    /* 此处需要打补丁.原因是: 现在被叫终端先发协商包,后发200OK. 这样会导致协商包的正确终端rtcp端口被200消息的初始rtcp端口覆盖.
                       导致UCC中保存了错误的终端初始rtcp端口. 虽然后续被叫终端会周期性发协商包,但是会造成呼叫建立后的首次话权消息没有到达被叫终端.
                       因此这里要比较原先UCC中的rtcp port,如果有效且与初始rtcp port不一致, 则仍然保存原来的 rtcp port. */
                    MediaDescriptionS stOldMedia;
                    memset(&stOldMedia, 0, sizeof(MediaDescriptionS));
                    memcpy(&stOldMedia, &stUCC_Tmp.stMediaInfo, sizeof(MediaDescriptionS));

                    stUCC_Tmp.enMediaType = MEDIA_AUDIO;
                    memcpy(&stUCC_Tmp.stMediaInfo, &stAudio, sizeof(MediaDescriptionS));
                    stUCC_Tmp.ulSsrc = stAudio.ulSsrc;

                    // begin add by shm, 从缓存的rtcp保活信息中查找ssrc对应的映射端口
                    MediaKeepaliveInfo stRtcpInfo;
                    MediaKeepaliveInfo stRtpInfo;

                    if(XPOC_ERR_SUCCESS == findRtcpNetInfo(stUCC_Tmp.ulSsrc, &stRtcpInfo, 1))
                    {
                        struct in_addr addrTmp;
                        memcpy(&addrTmp, &stRtcpInfo.ulIp, 4);
                        char *ptCliIp_ucc_str = inet_ntoa(addrTmp);
                        x_InfoLog("Update rtcp dst, ssrc : [0x%x(%u)], net addr: [%s:%d]", stUCC_Tmp.ulSsrc, stUCC_Tmp.ulSsrc, ptCliIp_ucc_str, stRtcpInfo.nPort);

                        stUCC_Tmp.stMediaInfo.ulIpAddr = stRtcpInfo.ulIp;
                        stUCC_Tmp.stMediaInfo.rtcpPort  = stRtcpInfo.nPort;
                    }
                    
                    if (XPOC_ERR_SUCCESS == findRtpNetInfo(stUCC_Tmp.ulSsrc, &stRtpInfo, 1))
                    {
                        struct in_addr addrTmp;
                        memcpy(&addrTmp, &stRtpInfo.ulIp, 4);
                        char *ptCliIp_ucc_str = inet_ntoa(addrTmp);
                        x_InfoLog("Update rtp dst, ssrc : [0x%x(%u)], net addr: [%s:%d]", stUCC_Tmp.ulSsrc, stUCC_Tmp.ulSsrc, ptCliIp_ucc_str, stRtpInfo.nPort);

                        stUCC_Tmp.stMediaInfo.ulIpAddr = stRtpInfo.ulIp;
                        stUCC_Tmp.stMediaInfo.rtpPort  = stRtpInfo.nPort;
                    }
                    // end add

                    /* 对于录音录像不能这样处理 */
                    if(!(Serv_AudioRcrdPri == stUCC_Tmp.servType 
                         || Serv_VideoRcrdPri == stUCC_Tmp.servType
                         || Serv_AudioRcrdGrp == stUCC_Tmp.servType
                         || Serv_VideoRcrdGrp == stUCC_Tmp.servType))
                    {
                        /* 比较原先UCC中的rtcp port,如果有效且与初始rtcp port不一致, 则仍然保存原来的 rtcp port. */
                        if(0 != stOldMedia.rtpPort && stOldMedia.rtpPort != stUCC_Tmp.stMediaInfo.rtpPort)
                            stUCC_Tmp.stMediaInfo.rtpPort = stOldMedia.rtpPort;
                        if(0 != stOldMedia.rtcpPort && stOldMedia.rtcpPort != stUCC_Tmp.stMediaInfo.rtcpPort)
                            stUCC_Tmp.stMediaInfo.rtcpPort = stOldMedia.rtcpPort;
                    }

                    memcpy(&stUCC_Audio, &stUCC_Tmp, sizeof(T_MdeUCallCtxTable));
                    ptUCC_Audio = &stUCC_Audio;
                }
                else if(MEDIA_VIDEO == stUCC_Tmp.enMediaType)
                {
                    stUCC_Tmp.enMediaType = MEDIA_VIDEO;
                    memcpy(&stUCC_Tmp.stMediaInfo, &stVideo, sizeof(MediaDescriptionS));
                    stUCC_Tmp.ulSsrc = stVideo.ulSsrc;
                    if(Serv_AudioRcrdPri == stUCC_Tmp.servType 
                         || Serv_VideoRcrdPri == stUCC_Tmp.servType
                         || Serv_AudioRcrdGrp == stUCC_Tmp.servType
                         || Serv_VideoRcrdGrp == stUCC_Tmp.servType)
                    {
                        stUCC_Tmp.stMediaInfo.rtpPort = 0; /* record时故意改错,目的是为了收到测试包时地址端口不一致而保存tcprtpFdId. */
                    }
                    memcpy(&stUCC_Video, &stUCC_Tmp, sizeof(T_MdeUCallCtxTable));
                    ptUCC_Video = &stUCC_Video;
                }
            }
            (void)XMdeUCallCtxOpr(XDB_OPR_MOD, NULL, 0, 0, 0, NULL, &stUCC_Tmp.ulRecordIdx, &stUCC_Tmp);
        }

        // build send sdp
        ulReturn = XPOC_ERR_SUCCESS;
        (void)mdeBuildSdpForInviteRsp(ptModCtx, ptXpocMsgHdr, &stCC, ptUCC_Audio, ptUCC_Video, &ptSendSdp);
        x_InfoLog("Send sip rsp, ServId [%s].", stUCC_Tmp.aucServId);
    } while(0);

    SipStatusCodeE enSipStatus = (XPOC_ERR_SUCCESS == ulReturn) ? 200 : 403;
    (void)mdeSendInviteRsp(ptSipMsgInd, ptSendSdp, ptCB, enSipStatus);

    XPOC_TRACEFUNC_OUT;
    return ulReturn;
}

UINT mdeHandleSipBye(T_ModCtxS *ptModCtx, T_ModMsgHead* ptXpocMsgHdr)
{
    UINT ulReturn= XPOC_ERR_SUCCESS;

    XPOC_TRACEFUNC_IN;
    X_NULL_POINTER_CHK_TWO_PARA(ptModCtx, ptXpocMsgHdr);

    SipMsgIndS *ptSipMsgInd = (SipMsgIndS*)ptXpocMsgHdr->stMsgBody;

    UINT ulCBId = ptSipMsgInd->ulDstCbId;
    UINT ulCallCtxId = 0;
    UINT ulUCallCtxId = 0;

    MdeCtrlBlockS *ptCB = NULL;
    T_MdeUCallCtxTable stUCC;
    T_MdeCallCtxTable stCC; 
    
    char aucServId[64] = {0};
    char acCallId[64]  = {0};
    (void)mdeGetServIdFromSipHeader(ptSipMsgInd->ulSipHandle->request, aucServId);
    XPOCGetSipHeaderCallId(ptSipMsgInd->ulSipHandle->request, acCallId);
    x_InfoLog("[X]recv re-invite, call-id[%s], servId[%s].", acCallId, aucServId);

    memset(&stCC, 0, sizeof(T_MdeCallCtxTable));
    memset(&stUCC, 0, sizeof(T_MdeUCallCtxTable));

    ulReturn = mdeGetCB(ptXpocMsgHdr, &ulCBId, &ptCB);
    if(XPOC_ERR_SUCCESS != ulReturn || NULL == ptCB)
    {
        x_WarnLog("mde get CtrlBlock failed, retcode[%d],ptCB[%p].\n", ulReturn, ptCB);
        return XPOC_ERR_FAIL;
    }

    ulCallCtxId = ptCB->ulCallCtxId;

    UINT i = 0;
    for(i = 0 ; i<ptCB->StreamNum && i<MEDIA_TYPE_MAX; i++)
    {
        ulUCallCtxId = ptCB->StreamInfoArray[i].ulUCallCtxId;
        ulReturn = XMdeUCallCtxOpr(XDB_OPR_QRY, NULL, 0, 0, 0, NULL, &ulUCallCtxId, &stUCC);
        if(XPOC_ERR_SUCCESS != ulReturn)
        {
            continue;
        }

        if(stUCC.usTcpRtpFdIdx < MAX_RTP_SOCKET_NUM)
        {
            ulReturn = mdeCloseMediaSocket(&stUCC);
            if(XPOC_ERR_SUCCESS != ulReturn)
            {
                x_WarnLog("Close TCP RTP socket failed. CCId=%d, UCCId=%d.\n", ulCallCtxId, ulUCallCtxId);
            }
        }

        ulReturn = mdeDetachUCCFromCC(ulCallCtxId, ulUCallCtxId);
        ulReturn = XMdeUCallCtxOpr(XDB_OPR_DEL, NULL, 0, 0, 0, NULL, &ulUCallCtxId, &stUCC);
    }

    /* 如若最后一条承载,删除CallCtx */
    ulReturn = XMdeCallCtxOpr(XDB_OPR_QRY, NULL, 0, &ulCallCtxId, &stCC);
    if(XPOC_ERR_SUCCESS != ulReturn)
    {
        return ulReturn;
    }

    ulReturn = mdeDelCBFromCC(&stCC, ulCBId);

    /* 当前是转发(组)业务, 需要在视频源mediaCC中清除当前的转发目的CC. */
    if(Serv_VideoTransPri == stCC.servType || Serv_VideoTransGrp == stCC.servType)
    {
        (void)mdeDelTransCCFromSrcCC(stCC.ulSrcMediaCCId, stCC.ulRecordIdx);
    }

    /* 当前为录音录像业务, 需要在视频源mediaUCC中清除当前的录音录像mediaCC. ----一般都是先释放呼叫,然后再释放录音录像流程, 因此不需要 */
    
    if(0 == stCC.stUCCIdList.UcNum) 
    {
        ulReturn = XMdeCallCtxOpr(XDB_OPR_DEL, NULL, 0, &stCC.ulRecordIdx, &stCC);
    }

    (void)XPOCFreeCtrlBlock(ptCB->ulCBId, ulReturn);

    (void)mdePrintMediaInfo(ptModCtx, ulCallCtxId);

    XPOC_TRACEFUNC_OUT;
    return ulReturn; 
}

#if 0
/* MDE收到ACK后获取用户对应的集团信息,供后续向CCE发送floor消息 */
UINT mdeHandleSipAck(T_ModCtxS *ptModCtx, T_ModMsgHead* ptXpocMsgHdr)
{
    UINT ulReturn= XPOC_ERR_SUCCESS;

    XPOC_TRACEFUNC_IN;
    X_NULL_POINTER_CHK_TWO_PARA(ptModCtx, ptXpocMsgHdr);

    char aucServId[STR_LEN32];
    memset(aucServId, 0, STR_LEN32);

    SipMsgIndS *ptSipMsgInd = (SipMsgIndS*)ptXpocMsgHdr->stMsgBody;
    (void)XPOCGetServIdFromSipHeaderFrom(ptSipMsgInd->ulSipHandle->request, aucServId);

    T_FleetCtxTable stFleetCtx;
    memset(&stFleetCtx, 0, sizeof(T_FleetCtxTable));
    strcpy(stFleetCtx.aucFleetId, aucServId, 4);
    ulReturn = FleetCtxOpr(XDB_OPR_QRY, stFleetCtx.aucFleetId, NULL, &stFleetCtx);
    if(XPOC_ERR_SUCCESS == ulReturn)
    {
        return ulReturn;
    }

//    (void)mdeSendFleetProfileReq2Http(stFleetCtx.aucFleetId);

    XPOC_TRACEFUNC_OUT;
    return ulReturn; 
}
#endif



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值