小计之rce

在这里插入图片描述

rceDef.h


#ifndef __RCEDEF_H__
#define __RCEDEF_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 "XpocModStruct.h"
#include "XpocProcTimer.h"
#include "eXosip2/eXosip.h"
#include "XpocCommStruct.h"
#include "XpocCommFunc.h"
#include "XpocDBInit.h"

extern char* g_rootDir;
extern char g_rceDir[128];

#define RCE_TIMER_FILES_CLEAR           1
#define RCE_TIMER_SEND_UDP_TEST_PKT_FIRST   2 /* 0.1s, 首次发送链路协商包传递SSRC, 在TCP链路建立后||UDP链路分配成功后 启动 */
#define RCE_TIMER_SEND_TCP_TEST_PKT_FIRST   3 /* 0.1s, 首次发送链路协商包传递SSRC, 在TCP链路建立后||UDP链路分配成功后 启动 */
#define RCE_TIMER_SEND_UDP_TEST_PKT         4 /* 20s, 定时发送链路协商包传递SSRC */
#define RCE_TIMER_SEND_TCP_TEST_PKT         5 /* 20s, 定时发送链路协商包传递SSRC */


typedef struct
{
    UINT ulUCallCtxId;
}StreamInfoS;

typedef struct
{
    UCHAR aucFleetId[STR_LEN32];
    UINT ulCurrMsgId;
    UINT ulCBId;
    UCHAR StreamNum;
    StreamInfoS StreamInfoArray[MEDIA_TYPE_MAX];
    //eXosip_event_t *ptSpSipHandle;   /* sip addr */
}RceCtrlBlockS;


#define RTP_HEADER_LEN (12)
#define RTP_HEADER_EXT_LEN (8)
typedef struct 
{
    /**//* byte 0 */
    UCHAR data1;
    UCHAR data2;
    USHORT seq_no;
    UINT timestamp;
    UINT ssrc;
}RTP_FIXED_HEADER;

#define MAX_RTP_SOCKET_NUM 3000
typedef struct
{
    USHORT          udpFdNum;
    MediaFdS        *udpRtpFdList;
    MediaFdS        *udpRtcpFdList;
    SINT            udpEpollFd; // udp epoll
    ThreadInfo_t    *ptSendThread;
}UdpAddrInfoS;

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

typedef struct
{
    UCHAR           rtpWorkerThreadNum;         // 2

    USHORT          udpStartPort;               // 20000
    USHORT          udpEndPort;                 // 20199

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

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

    UdpAddrInfoS    stUdpAddrInfo;
    TcpAddrInfoS    stTcpAddrInfo;
} RceGlbData_S;


#endif  /* __RCEDEF_H__ */

rceProc.h


#ifndef __RCE_PROC_H__
#define __RCE_PROC_H__

#include "ConfSQLApi.h"
#include "httpuafunc.h"
#include "httpuasrvfunc.h"
#include "rceDef.h"

#ifdef __cplusplus
extern "C" {
#endif /*__cplusplus*/

UINT rceLoadCfg(T_ModCtxS *ptModCtx);
UINT rceGetCBConfig(T_ModCtxS *ptModCtx);
void Mem_Add(char dst[260], char *a1, char*a2, char*a3);
int get_file_size(int f);//获取文件的大小 
void DownFile(struct evhttp_request *req, void *arg);

#if 0
#endif

UINT rceModFirstStart(T_ModCtxS *ptModCtx);
UINT rceModFinishStart(T_ModCtxS *ptModCtx);
UINT rceModMsgProc(T_ModCtxS *ptModCtx, T_PlatHead *ptPlatHeadInfo, T_ModMsgHead *ptXPOCHeader);
UINT rceModTimerProc(T_ModCtxS *ptModCtx, T_ProcTimeCtx *pTimerCtxt);
UINT rceSendFleetCfg2Http();
UINT rceReceiveCfgMsgFromHttp(T_ModCtxS* ptModCtx, T_ModMsgHead* ptXPocHdr);

#if 0
#endif

UINT rceHttpusFirstStart(T_ModCtxS *ptModCtx);
UINT rceHttpusFinishStart(T_ModCtxS *ptModCtx);
UINT rceHttpusMsgProc(T_ModCtxS *ptModCtx, T_PlatHead *ptPlatHeadInfo, T_ModMsgHead *ptXPOCHeader);
UINT rceHttpusTimerProc(T_ModCtxS *ptModCtx, T_ProcTimeCtx *pTimerCtxt);

typedef struct {
    struct evhttp_request *req;
    int fd;
    long chunksize;
    long long filesize;
    long long offset;
    int count;
}ChunkReqStateS ;

UINT httpSendChunk(ChunkReqStateS *crs);
void ChunkedTrickleCb(struct evhttp_connection *conn, void *arg);


#ifdef __cplusplus
}
#endif /*__cplusplus*/


#endif /* __RCE_PROC_H__ */

rceProc.c

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

#include "XpocDBInterface.h"

#include "event2/event_compat.h"
#include "event2/http_compat.h"
#include "event2/http.h"

#include "rceCommFunc.h"
#include "rceSignalFunc.h"
#include "rceMediaFunc.h"
#include "rceProc.h"
#include <dirent.h>

#define RCE_FILE_DOWNLOAD_MAX_3M    3*1024*1024
extern MYSQL *mysql_connect;
extern char glb_neLocIp[STR_LEN32];
extern int glb_RceDelTime;
extern T_ModuleCBCtx g_RceCBCtxSTable[];

extern T_TableCfg  g_tTableCfg;
extern T_ServerCfg g_tServerCfg;

UINT delUserRecord(char *aucFleetId, time_t delTime, FileTypeE enFileType);
UINT getFileType(char *filename);
UINT rceDeleteFile(char *dir, FileTypeE enFileType);

static int rceMkdirMultilevel(const char *muldir)
{
    char str[512];
    strncpy(str, muldir, 512);
    int len = strlen(str);
    if (len < 1 || str[0] != '/')
    {
        return -1;
    }

    int i;
    for(i=1; i<len; i++)
    {
        if(str[i]=='/')
        {
            str[i] = '\0';
            if(access(str,0) != 0)
            {
                mkdir(str, 0777);
            }
            str[i]='/';
        }
    }

    if(access(str,0) != 0)
    {
        mkdir(str, 0777);
    }
    return 0;
}

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

    RceGlbData_S* ptGlbData = (RceGlbData_S*)Fplat_Malloc(sizeof(RceGlbData_S), 0);
    if (NULL == ptGlbData)
    {
        x_ErrorLog("SP module global data alloc failed.\n");
        return XPOC_ERR_FAIL;
    }
    memset(ptGlbData, 0, sizeof(RceGlbData_S));
    ptModCtx->pAppData = (void *)ptGlbData;
    //申请udp tcp socket数组空间, 数组大小设为同时处于呼叫中的最大用户数
    ptGlbData->stUdpAddrInfo.udpRtpFdList = (MediaFdS *)calloc(g_tServerCfg.uConcurrCallMaxNum, sizeof(MediaFdS));
    if (NULL == ptGlbData->stUdpAddrInfo.udpRtpFdList)
    {
        x_ErrorLog("udpRtpFdList calloc failed.\n");
        return XPOC_ERR_FAIL;
    }

    ptGlbData->stUdpAddrInfo.udpRtcpFdList = (MediaFdS *)calloc(g_tServerCfg.uConcurrCallMaxNum, sizeof(MediaFdS));
    if (NULL == ptGlbData->stUdpAddrInfo.udpRtcpFdList)
    {
        x_ErrorLog("udpRtcpFdList calloc failed.\n");
        return XPOC_ERR_FAIL;
    }

    ptGlbData->stTcpAddrInfo.tcpRtpFdList = (MediaFdS *)calloc(g_tServerCfg.uConcurrCallMaxNum, sizeof(MediaFdS));
    if (NULL == ptGlbData->stTcpAddrInfo.tcpRtpFdList)
    {
        x_ErrorLog("tcpRtpFdList calloc failed.\n");
        return XPOC_ERR_FAIL;
    }

    /* connfd 有可能分配的是0, 会造成消息转发到非预期的fd上. 因此fd不能初始化为0. */
    UINT i = 0;
    for (i = 0; i < g_tServerCfg.uConcurrCallMaxNum; i++)
    {
        ptGlbData->stUdpAddrInfo.udpRtpFdList[i].iSocketFd = INVALID_VALUE_UINT;
        ptGlbData->stUdpAddrInfo.udpRtcpFdList[i].iSocketFd = INVALID_VALUE_UINT;
        ptGlbData->stTcpAddrInfo.tcpRtpFdList[i].iSocketFd = INVALID_VALUE_UINT;
    }

    XPOC_TRACEFUNC_OUT;
    return XPOC_ERR_SUCCESS;
}

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

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

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

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

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

    XPOCStartPeriodTimer(RCE_TIMER_FILES_CLEAR, CLEAR_TIME_LEN * 60, 0); // 定时检查是否清理文件,默认设置十分钟

    // 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 -- rce has started.");
    // sedSdsWarnReq2Http(&stSds);

    XPOC_TRACEFUNC_OUT;
    return XPOC_ERR_SUCCESS;
}

UINT rceModMsgProc(T_ModCtxS *ptModCtx, T_PlatHead *ptPlatHdr, T_ModMsgHead *ptXPocHdr)
{
    XPOC_TRACEFUNC_IN;
    X_NULL_POINTER_CHK_THREE_PARA(ptModCtx, ptPlatHdr, ptXPocHdr);

    UINT ulRetCode = XPOC_ERR_SUCCESS;
    if(EV_SIP_INVITE_IND == ptXPocHdr->ulMsgId)
    {
        ulRetCode = rceHandleSipInvite(ptModCtx, ptXPocHdr);
    }
    else if(EV_SIP_BYE_IND == ptXPocHdr->ulMsgId)
    {
        ulRetCode = rceHandleSipBye(ptModCtx, ptXPocHdr);
    }
    else if(EV_SIP_ACK_IND == ptXPocHdr->ulMsgId)
    {
        x_DebugLog("recv EV_SIP_ACK_IND. do nothing.");
    }
    else if (EV_RCE_RECEIVE_FLEETCONFIG == ptXPocHdr->ulMsgId) // modify by lkf
    {
        x_InfoLog("recv EV_RCE_RECEIVE_FLEETCONFIG.\n");
        (void)rceReceiveCfgMsgFromHttp(ptModCtx, ptXPocHdr);
    }
    else
    {
        x_WarnLog("recv unexpected msgId[0x%x].\n", ptXPocHdr->ulMsgId);
    }

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

    XPOC_TRACEFUNC_OUT;
    return ulRetCode;
}

UINT rceModTimerProc(T_ModCtxS *ptModCtx, T_ProcTimeCtx *pTimerCtxt)
{
    XPOC_TRACEFUNC_IN;
    X_NULL_POINTER_CHK_TWO_PARA(ptModCtx, pTimerCtxt);

    UINT ulReturn = XPOC_ERR_SUCCESS;

    // modify by lkf
    if (RCE_TIMER_FILES_CLEAR == pTimerCtxt->TimerType)
    {
        if (XPOC_ERR_SUCCESS == isClearFile(CLEAR_TIME_LEN))
        {
            (void)rceSendFleetCfg2Http();
        }
        return ulReturn;
    }
    RceGlbData_S* ptGlbData = (RceGlbData_S*)ptModCtx->pAppData;
    UINT RtpFdIndex = pTimerCtxt->uiParam2;
    MediaFdS *ptRtpFd = NULL;
    T_RceUCallCtxTable stUCC;
    UINT ulSec = 10;

    memset(&stUCC, 0, sizeof(T_RceUCallCtxTable));

//    if(Record_WORK != ptRtpFd->iRceRecordState)
//    {
//        x_WarnLog("rtpFd.recordState[%d] is not WORK.", ptRtpFd->iRceRecordState);
//        return XPOC_ERR_FAIL;
//    }

    if(RCE_TIMER_SEND_UDP_TEST_PKT_FIRST == pTimerCtxt->TimerType
        || RCE_TIMER_SEND_UDP_TEST_PKT == pTimerCtxt->TimerType)
    {
        ptRtpFd = &ptGlbData->stUdpAddrInfo.udpRtpFdList[RtpFdIndex];
    }
    else if(RCE_TIMER_SEND_TCP_TEST_PKT_FIRST == pTimerCtxt->TimerType
        || RCE_TIMER_SEND_TCP_TEST_PKT == pTimerCtxt->TimerType)
    {
        ptRtpFd = &ptGlbData->stTcpAddrInfo.tcpRtpFdList[RtpFdIndex];
    }
    if(!ptRtpFd)
    {
        x_ErrorLog("ptRtpFd is NULL.");
        return XPOC_ERR_FAIL;
    }
    stUCC.ulRecordIdx = ptRtpFd->ulUCCId;
    ulReturn = XRceUCallCtxOpr(XDB_OPR_QRY, NULL, NULL, (MediaTypeE)0, NULL, &stUCC.ulRecordIdx, &stUCC);
    if (XPOC_ERR_SUCCESS != ulReturn)
    {
        x_ErrorLog("get UCC by recordIx[%d] failed.", stUCC.ulRecordIdx);
        return ulReturn;
    }

    switch (pTimerCtxt->TimerType)
    {
        case RCE_TIMER_SEND_UDP_TEST_PKT_FIRST:
            (void)rceSendUdpTestPktWithSsrc(ptModCtx, &stUCC);

            pTimerCtxt->TimerType = RCE_TIMER_SEND_UDP_TEST_PKT;
            XPOCStartTimer(ulSec, 0, pTimerCtxt, TIME_FLAG_POOL);
            x_InfoLog("rce timer start: timeType[%d],timerId[%lld],timeLen[%d].",
                       pTimerCtxt->TimerType, pTimerCtxt->TimerId, ulSec);
            break;
        case RCE_TIMER_SEND_TCP_TEST_PKT_FIRST:
            (void)rceSendTcpTestPktWithSsrc(ptRtpFd->iSocketFd, stUCC.ulSsrc);

            pTimerCtxt->TimerType = RCE_TIMER_SEND_TCP_TEST_PKT;
            XPOCStartTimer(ulSec, 0, pTimerCtxt, TIME_FLAG_POOL);
            x_InfoLog("rce timer start: timeType[%d],timerId[%lld],timeLen[%d].",
                       pTimerCtxt->TimerType, pTimerCtxt->TimerId, ulSec);
            break;
        case RCE_TIMER_SEND_UDP_TEST_PKT:
            (void)rceSendUdpTestPktWithSsrc(ptModCtx, &stUCC);
            break;
        case RCE_TIMER_SEND_TCP_TEST_PKT:
            (void)rceSendTcpTestPktWithSsrc(ptRtpFd->iSocketFd, stUCC.ulSsrc);
            break;

        default:
            break;
    }

    XPOC_TRACEFUNC_OUT;
    return ulReturn;
}

// // modify by lkf
UINT rceSendFleetCfg2Http()
{
    XPOC_TRACEFUNC_IN;
    UINT ulReturn = XPOC_ERR_SUCCESS;
    UINT ulMsgLen = sizeof(FleetConfigReqS);
    UCHAR* ptPlatHdr = (UCHAR*)XPOCAllocMsg(ulMsgLen);
    if (NULL == ptPlatHdr)
    {
        x_ErrorLog("Alloc Plat message header error, ulMsgLen[%d]!\n", ulMsgLen);
        return XPOC_ERR_INVALID_PTR;
    }
    T_ModMsgHead* ptXPOCHeader = (T_ModMsgHead*)XPOCGetXpocHeaderPtr(ptPlatHdr);
    if (NULL == ptXPOCHeader)
    {
        XPOCFreeMsg(ptPlatHdr);
        return XPOC_ERR_INVALID_PTR;
    }

    //fill in the message header
    ptXPOCHeader->ulMsgId = EV_RCE_GET_FLEETCONFIG;
    ptXPOCHeader->ulMsgLen = ulMsgLen;
   
    FleetConfigReqS *ptReq = (FleetConfigReqS *)ptXPOCHeader->stMsgBody;
    memset(ptReq, 0, sizeof(FleetConfigReqS));
    strncpy(ptReq->aucServIP, glb_neLocIp, strlen(glb_neLocIp));
    //x_DebugLog("get all fleet config from [%s]", ptReq->aucServIP);
  
    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
    ulReturn = XPOCDispatchMsg(&stDstModInfo, ptPlatHdr);
    if (XPOC_ERR_SUCCESS == ulReturn)
    {
        x_InfoLog("RCE--->HTTP:EV_RCE_GET_FLEETCONFIG[0x%x] OK!\n", EV_RCE_GET_FLEETCONFIG);
    }
    else
    {
        x_ErrorLog("RCE--->HTTP:EV_RCE_GET_FLEETCONFIG[0x%x] failed!\n", EV_RCE_GET_FLEETCONFIG);
    }
    XPOC_TRACEFUNC_OUT;
    return ulReturn;
}

// modify by lkf, 接收http消息,打开定时器,对不同集团文件夹做处理;表数据处理也在这里执行
UINT rceReceiveCfgMsgFromHttp(T_ModCtxS* ptModCtx, T_ModMsgHead* ptXPocHdr)
{
    XPOC_TRACEFUNC_IN;
    X_NULL_POINTER_CHK_TWO_PARA(ptModCtx, ptXPocHdr);

    time_t cfgTime = getCleanTime(glb_RceDelTime); // 获取配置文件设置的对应当天时间戳
    AllFleetConfigS* ptFleetCfg = NULL;
    ptFleetCfg = (AllFleetConfigS*)ptXPocHdr->stMsgBody;
    if (access(g_rceDir, 0) == -1)
    {
        x_ErrorLog("path[%s] unexist!", g_rceDir);
        return XPOC_ERR_SUCCESS;
    }

    time_t delTime;
    UINT i;
    for(i = 0; i < (ptFleetCfg->ulFleetNum); i++)
    {
        /* 录音录像分开删除 */
        char tmp[STR_LEN256] = {0};
        sprintf(tmp, "%s/%s", g_rceDir, ptFleetCfg->stCfgList[i].aucFleetId);
        if (access(tmp, 0) == -1)
        {
            continue;
        }
        x_InfoLog("fleet[%s] prepare to delete audio before [%d] days and video before [%d] days.", ptFleetCfg->stCfgList[i].aucFleetId,
                    ptFleetCfg->stCfgList[i].ulAudioStorageTime, ptFleetCfg->stCfgList[i].ulVideoStorageTime);

        if(ptFleetCfg->stCfgList[i].ulAudioStorageTime != 0)
        {
            delTime = cfgTime - ptFleetCfg->stCfgList[i].ulAudioStorageTime * 86400;
            delUserRecord(ptFleetCfg->stCfgList[i].aucFleetId, delTime, FILE_AUDIO);
        }

        if(ptFleetCfg->stCfgList[i].ulVideoStorageTime != 0)
        {
            delTime = cfgTime - ptFleetCfg->stCfgList[i].ulVideoStorageTime * 86400;
            delUserRecord(ptFleetCfg->stCfgList[i].aucFleetId, delTime, FILE_VIDEO);
        }
    }

    XPOC_TRACEFUNC_OUT;
    return XPOC_ERR_SUCCESS;
}

#if 0
#endif

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

    //todo

    XPOC_TRACEFUNC_OUT;
    return XPOC_ERR_SUCCESS;
}


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

    struct evhttp *http_server = NULL;

    char *http_addr = glb_neLocIp;
    short http_port = RCE_HTTP_PORT;

    //初始化
    event_init();
    //启动http服务端
    http_server = evhttp_start(http_addr, http_port);
    if (http_server == NULL)
    {
        x_ErrorLog("http server start failed.");
        return XPOC_ERR_FAIL;
    }

    //设置请求超时时间(s)
    evhttp_set_timeout(http_server, HTTP_REQUEST_MAX_TIMELEN);
    //设置事件处理函数,evhttp_set_cb针对每一个事件(请求)注册一个处理函数,
    //区别于evhttp_set_gencb函数,是对所有请求设置一个统一的处理函数    
    //文件服务器
    rceMkdirMultilevel(g_rceDir);
    
    // modified by shm at 20210203
    //evhttp_set_cb(http_server, "/xpoc/dc-rce-downloadfd", DownFile, NULL);
    evhttp_set_gencb(http_server, DownFile, NULL);   

    //循环监听. //此处处理http_handler_msg
    event_dispatch();
    //实际上不会释放,代码不会运行到这一步
    evhttp_free(http_server);

    XPOC_TRACEFUNC_OUT;
    return XPOC_ERR_SUCCESS;
}

UINT rceHttpusMsgProc(T_ModCtxS *ptModCtx, T_PlatHead *ptPlatHdr, T_ModMsgHead *ptXPocHdr)
{
    XPOC_TRACEFUNC_IN;
    X_NULL_POINTER_CHK_THREE_PARA(ptModCtx, ptPlatHdr, ptXPocHdr);

    UINT ulReturn = XPOC_ERR_SUCCESS;

    XPOC_TRACEFUNC_OUT;
    return ulReturn;
}

UINT rceHttpusTimerProc(T_ModCtxS *ptModCtx, T_ProcTimeCtx *pTimerCtxt)
{
    XPOC_TRACEFUNC_IN;
    X_NULL_POINTER_CHK_TWO_PARA(ptModCtx, pTimerCtxt);

    UINT ulReturn = XPOC_ERR_SUCCESS;

    XPOC_TRACEFUNC_OUT;
    return ulReturn;
}

#if 0
#endif


void Mem_Add(char dst[260], char *a1, char*a2, char*a3)
{
    memset(dst, 0, 260);
    strcat(dst, a1);
    strcat(dst, a2);
    strcat(dst, a3);
}

//获取文件的大小 
int get_file_size(int f)
{
    struct stat st;
    fstat(f, &st);
    return st.st_size;
}

UINT httpSendChunk(ChunkReqStateS *crs)
{
    lseek(crs->fd, crs->offset, SEEK_SET);

    long length = crs->filesize - crs->offset;
    if(length > crs->chunksize)
    {
        length = crs->chunksize;
    }

    char *Buffer = (char *)malloc(length + 1);
    if(Buffer != NULL)
    {
        memset(Buffer, 0, length+1);
    }
    else
    {
        x_ErrorLog("malloc buffer fail !");
        if(crs->fd != -1)  close(crs->fd);
        return XPOC_ERR_FAIL;
    }
    
    int iread = read(crs->fd, Buffer, length);
    //x_InfoLog("[httpSendChunk] Count[%d], fileLength[%d], fileOffset[%d], iread[%d].", crs->count, crs->filesize, crs->offset, iread);
    if(iread > 0)
    {
        struct evbuffer *evb = evbuffer_new();
        evbuffer_add(evb, Buffer, iread);

        if (crs->offset < crs->filesize) 
        {
            evhttp_send_reply_chunk_with_cb(crs->req, evb, ChunkedTrickleCb, crs);
            crs->offset += iread;
            crs->count++;
        }
        else
        {
            x_InfoLog("evhttp_send_reply_end, fd[%d], fileSize[%d], iread[%d]", crs->fd, crs->filesize, iread);
            evhttp_send_reply_end(crs->req);
            if(crs->fd != -1)  close(crs->fd);
            free(crs);
            crs = NULL;
        }

        evbuffer_free(evb);
    }
    else
    {
        x_InfoLog("download file success, fd[%d], fileSize[%d], iread[%d]", crs->fd, crs->filesize, iread);
        evhttp_send_reply_end(crs->req);
        if(crs->fd != -1)  close(crs->fd);
        free(crs);
        crs = NULL;
    }

    free(Buffer);
    return XPOC_ERR_SUCCESS;
}

void ChunkedTrickleCb(struct evhttp_connection *conn, void *arg)
{
    ChunkReqStateS *state = (ChunkReqStateS *)arg;
    //x_InfoLog("[ChunkedTrickleCb] Count[%d], fileLength[%d], fileOffset[%d].", state->count, state->filesize, state->offset);
    httpSendChunk(state);
}

// modified by shm at 20210203, http请求类型修改为get,并去除json消息体,文件名在uri中携带
void DownFile(struct evhttp_request *req, void *arg)
{
    if (!req)
    {
        x_ErrorLog("http request is null.");
        return;
    }
    if ((!req->uri) || (strlen(req->uri) <= 90) || (!strstr(req->uri, "-Cur-"))) // modify by lkf, the length of uri is more than 90 and the -Cur- is contained in uri
    {
        x_ErrorLog("http uri[%s] is unexpected!", req->uri);
        return send_rsp_message(req, HTTP_EXPECTATIONFAILED);
    }
    if(strncmp(req->uri, "/xpoc/", 6))
    {
        x_ErrorLog("http uri[%s] is invalid.", req->uri);
        return send_rsp_message(req, HTTP_EXPECTATIONFAILED);
    }
    print_http_event(req, arg);     /* [/xpoc/dc-rce-downloadfd/file] */
    int fd = -1;
    char strtemp[260];
    if (EVHTTP_REQ_GET == req->type)
    {
        if (req->uri)
        {
            char intfName[128] = {0};
            char* pcFileName = req->uri+1;    // 加2是避免字符串起始的'/'
            char* pcTmp      = NULL;
            do
            {
                pcTmp = strchr(pcFileName, '/');

                if (pcTmp)
                {
                    memset(intfName, 0, 128);   // added by shm, 解决循环后pcFileName不以 '\0' 结尾的问题
                    memcpy(intfName, pcFileName, pcTmp-pcFileName);
                    pcFileName = pcTmp + 1; // 加1是避免字符串起始的'/'
                }
                else
                {
                    // modify by lkf
                    char fleetId[STR_LEN16] = {0}; // 集团号文件夹
                    char folderName[STR_LEN16] = {0}; // 日期文件夹名称
                    char filePath[STR_LEN256] = {0};
                    char* pos = strstr(pcFileName, "-Cur-");
                    if(!pos)
                    {
                        x_ErrorLog("[%s] is abnormal, [-Cur-] is not substring!", pcFileName);
                        break;
                    }
                    strncpy(fleetId, pos + 5, STR_LEN8); // 集团号是八位
                    strncpy(folderName, pos + 23, STR_LEN10); // 日期文件夹10位
                    sprintf(filePath, "%s/%s/%s", g_rceDir, fleetId, folderName);
                    Mem_Add(strtemp, filePath, "/", pcFileName);
                    x_InfoLog("down file[%s], intfName[%s].", strtemp, intfName);
                    if(strcmp(intfName, "dc-rce-downloadfd") == 0)  // 在libevent uri区分接口的话,这里实际用不上
                    {
                        // open file
                        if ((fd = open(strtemp, O_RDONLY)) >= 0)
                        {
                            int filelen = get_file_size(fd);

                            // check range
                            const char *pRange = evhttp_find_header(req->input_headers, "Range");
                            UINT rangbeg = 0, rangend = 0;
                            if(pRange)   // play
                            {
                                char *pBeg = (char*)strstr(pRange, "bytes=");
                                if(pBeg)
                                {
                                    pBeg += 6;
                                    char *pEnd = strchr(pBeg, '-');
                                    if(pEnd)
                                    {
                                        char strTmp[64] = {0};
                                        memcpy(strTmp, pBeg, pEnd-pBeg);
                                        rangbeg = atoi(strTmp);
                                        pBeg = pEnd+1;
                                        rangend = atoi(pBeg);
                                    }
                                }
                                // modified by shm, 解决大文件不能播放问题;解决大文件拖拽播放异常问题
                                struct evbuffer *retbuff = evbuffer_new();
                                if (retbuff != NULL)
                                {
                                    char contRange[64] = {0};
                                    sprintf(contRange, "bytes %d-%d/%d", rangbeg, rangend>0?rangend:filelen-1, filelen);
                                    evhttp_add_header(req->output_headers, "Content-Type", "video/mp4");
                                    evhttp_add_header(req->output_headers, "Connection", "Keep-alive");
                                    evhttp_add_header(req->output_headers, "Content-Range", contRange);

                                    struct evbuffer_file_segment *pstSeg = NULL;

                                    int nCurrFileEndPos = (rangend>0?rangend:filelen - 1) - rangbeg + 1;
                                    pstSeg = evbuffer_file_segment_new(fd, rangbeg, nCurrFileEndPos, 0);
                                    if (!pstSeg)
                                        return;
                                    int r = evbuffer_add_file_segment(retbuff, pstSeg, 0, nCurrFileEndPos);
                                    if (r == 0)
                                        evbuffer_file_segment_free(pstSeg);
                                    evhttp_send_reply(req, 206, "Partial Content", retbuff);

                                    evbuffer_free(retbuff);
                                    if (-1 != fd) close(fd);
                                    return;
                                }
                            }
                            else
                            {
                                ChunkReqStateS *state = (ChunkReqStateS *)malloc(sizeof(ChunkReqStateS));
                                if(state != NULL)
                                {
                                    memset(state, 0, sizeof(ChunkReqStateS));
                                }
                                else
                                {
                                    x_ErrorLog("ChunkReqStateS malloc fail!");
                                    if (-1 != fd) close(fd);
                                    return send_rsp_message(req, HTTP_EXPECTATIONFAILED);
                                }

                                char fileSize[STR_LEN16] = {0};
                                sprintf(fileSize, "%d", filelen);
                                evhttp_add_header(req->output_headers, "Content-Type", "application/octet-stream");
                                evhttp_add_header(req->output_headers, "Connection", "Keep-alive");
                                evhttp_add_header(req->output_headers, "Content-Length", fileSize);

                                state->req = req;
                                state->fd = fd;
                                state->chunksize = RCE_FILE_DOWNLOAD_MAX_3M;
                                state->count = 0;
                                state->filesize = filelen;
	                            state->offset = 0;
                                evhttp_send_reply_start(req, HTTP_OK, "OK");
                                httpSendChunk(state);
                                //x_InfoLog("Count[%d], fileLength[%d], fileOffset[%d].", state->count, state->filesize, state->offset);
                                x_InfoLog("Start download file[%s], fd[%d], fileSize[%d].", strtemp, fd, state->filesize);
                                return;
                            }
                        }
                        else
                        {
                            x_ErrorLog("Open [%s] failed.", strtemp);
                        }
                    }
                }
                
            } while (pcTmp);
        }
    }

    if (-1 != fd) close(fd);
    return send_rsp_message(req, HTTP_EXPECTATIONFAILED);
}

UINT rceLoadCfg(T_ModCtxS *ptModCtx)
{
    X_NULL_POINTER_CHK_ONE_PARA(ptModCtx);

    RceGlbData_S* ptGlbData = (RceGlbData_S*)ptModCtx->pAppData;
    RceCfgInfoS *ptCfg = &ptGlbData->stCfgInfo;

    ptCfg->rtpWorkerThreadNum = 2;
    ptCfg->udpStartPort = 30000;
    ptCfg->udpEndPort = ptCfg->udpStartPort + g_tServerCfg.uConcurrCallMaxNum * 2 - 1; //调整为最大呼叫人数的2倍 lxh 20210610

    x_InfoLog("glbCfgInfo:threadNum[%d],udpPort[%d--%d].",
                ptCfg->rtpWorkerThreadNum, ptCfg->udpStartPort, ptCfg->udpEndPort);

    UINT Addr = inet_addr((char*)(glb_neLocIp));
    ptCfg->ulLocalIp = ntohl(Addr);

    x_InfoLog("udp localIP[0x%x],aceRmtIp[0x%x].\n", ptCfg->ulLocalIp, ptCfg->ulAceRmtIp);

    return XPOC_ERR_SUCCESS;
}

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

    UINT ulReturn = XPOC_ERR_SUCCESS;

    g_RceCBCtxSTable[X_CB_TRANSACTION].ulMaxCtrNum    = g_tServerCfg.uConcurrCallMaxNum;
    g_RceCBCtxSTable[X_CB_TRANSACTION].ulMinBufInPool = g_tServerCfg.uConcurrCallMaxNum;
    g_RceCBCtxSTable[X_CB_TRANSACTION].ulMaxBufInPool = g_tServerCfg.uConcurrCallMaxNum;
    g_RceCBCtxSTable[X_CB_TRANSACTION].ulExtStepBufInPool = 200;

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

    XPOC_TRACEFUNC_OUT;
    return ulReturn;
}

UINT delUserRecord(char *aucFleetId, time_t delTime, FileTypeE enFileType)
{
    XPOC_TRACEFUNC_IN;

    char aucDelTime[STR_LEN32] = {0};
    char aucDelFolder[STR_LEN32] = {0};
    strftime((char*)aucDelTime, STR_LEN32, "%Y-%m-%d %H:%M:%S", localtime(&delTime));
    strftime((char*)aucDelFolder, STR_LEN32, "%Y-%m-%d", localtime(&delTime));

    H_USER_Record_Delete_S ptRecord;
    memset(&ptRecord, 0 , sizeof(H_USER_Record_Delete_S));
    strcpy(ptRecord.aucFleetId, aucFleetId);
    strcpy(ptRecord.aucDeleteTime, aucDelTime);
    if(enFileType == FILE_AUDIO)
    {
        x_InfoLog("fleet[%s] delete audio record before time[%s].", ptRecord.aucFleetId, ptRecord.aucDeleteTime);
        (void)MYSQL_UserAudioRecord_Delete(mysql_connect, &ptRecord);
    }
    else if(enFileType == FILE_VIDEO)
    {
        x_InfoLog("fleet[%s] delete video record before time[%s].", ptRecord.aucFleetId, ptRecord.aucDeleteTime);
        (void)MYSQL_UserVideoRecord_Delete(mysql_connect, &ptRecord);
    }

    char fleetPath[STR_LEN256] = {0};
    sprintf(fleetPath, "%s/%s", g_rceDir, aucFleetId);
    struct dirent* pdirent;
    DIR* d_info = NULL;
    if ((d_info = opendir(fleetPath)) == NULL) // 进入集团目录
    {
        x_ErrorLog("folder[%s] open fail.", fleetPath);
        return XPOC_ERR_SUCCESS;
    }

    while ((pdirent = readdir(d_info)) != NULL) // 读取的是类似2019-03-26文件夹
    {
        if (pdirent->d_type & DT_DIR) // 读取文件夹名
        {             
            if (strcmp(pdirent->d_name, ".") == 0 || strcmp(pdirent->d_name, "..") == 0) // 忽略. 和 .. 文件夹
            {
                continue;
            }
           
            if (strcmp(pdirent->d_name, aucDelFolder) <= 0)
            {
                char rmDir[STR_LEN256] = {0};
                sprintf(rmDir, "%s/%s", fleetPath, pdirent->d_name);
                if(enFileType == FILE_AUDIO)
                {
                    (void)rceDeleteFile(rmDir, FILE_AUDIO);
                }
                else if(enFileType == FILE_VIDEO)
                {
                    (void)rceDeleteFile(rmDir, FILE_VIDEO);
                }
            }
        }
    }
    closedir(d_info);

    XPOC_TRACEFUNC_OUT;
    return XPOC_ERR_SUCCESS;
}

//区分录音(.amr)还是录像(.mp4)文件
UINT getFileType(char *filename)
{
    if(strncmp(filename, "VideoPri", 8) == 0)
    {
        return FILE_VIDEO;
    }
    else
    {
        const char *pFile = strrchr(filename, '.');  // 判断输入的文件名最后输出.的位置
        if (pFile != NULL)
        {
            if (strcmp(pFile, ".amr") == 0)
            {
                return FILE_AUDIO;
            }
            else if (strcmp(pFile, ".mp4") == 0)
            {
                return FILE_VIDEO;
            }
        }
    }

    x_ErrorLog("do not known file type, fileName[%s].", filename);
    return 0;
}

UINT rceDeleteFile(char *dir, FileTypeE enFileType)
{
    char filename[256] = {0};
    DIR *dirp;
    struct dirent *dp;

    // 参数传递进来的目录不存在,直接返回
    if ( 0 != access(dir, F_OK) ) 
    {
        return 0;
    }

    dirp = opendir(dir);
    while ( (dp=readdir(dirp)) != NULL ) 
    {
        // 忽略 . 和 ..
        if ( (0 == strcmp(".", dp->d_name)) || (0 == strcmp("..", dp->d_name)) )
        {
            continue;
        }

        sprintf(filename, "%s/%s", dir, dp->d_name);
        if(enFileType == getFileType(filename))
        {
            XPOCRemoveFile(filename);
        }
    }
    closedir(dirp);

    if (isDirEmpty(dir) == TRUE)
    {
        (void)XPOCRemoveFile(dir);
    }

    return XPOC_ERR_SUCCESS;
}

rceResourceFunc.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 <sys/timeb.h>


#include "rceSignalFunc.h"
#include "rceMediaFunc.h"
#include "rceResourceFunc.h"

extern T_ServerCfg g_tServerCfg;
extern ThreadInfo_t g_threadDpSendRtp[MAX_NUM_OF_DP_THREAD];
static UINT rceGetSsrc(MediaTypeE enType, USHORT ulFdIndex);

#if 0
#endif


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

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

    return XPOC_ERR_SUCCESS;
}

UINT recEpollAddFd(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],[sockFd][%d],errno[%d].", epollFd, sockFd, errno);    
        return XPOC_ERR_FAIL;
    }
    
    x_InfoLog("[X][EPOLL] ctl(ADD) OK. epollFd[%d],[sockFd][%d],ext[%s].", epollFd, sockFd, ptExt);

    return XPOC_ERR_SUCCESS;
}

UINT rceEpollDelFd(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],[sockFd][%d],errno[%d].", epollFd, sockFd, errno); 
        return XPOC_ERR_FAIL;
    }

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

    return XPOC_ERR_SUCCESS;
}

#if 0
#endif

SINT rceSetTcpBuff(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_DebugLog("[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 rceCreateUdpSocketPort(UINT IpV4, USHORT Port, FDTypeE fdType)
{
    SINT sockFd = -1;
    
    /* Opening UDP sockets */ 
    if((sockFd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) 
    {
        x_ErrorLog("[UDP][Socket] open udp socket failed. port [%d], errno = %d, err [%s].\n", Port, errno, strerror(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_DebugLog("[UDP][Socket] bind udp socket OK. sockFd[%d], addr[%s:%d], fdType[%d].\n", sockFd, ptStrIp, usPort, fdType);
    
    return sockFd;
}

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

    (void)rceSetTcpBuff(sockFd);

    x_DebugLog("[TCP][Socket] tcp socket OK. sockFd[%d].\n", sockFd);

    return sockFd;
}

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

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

    /* create UDP port */
    SINT iRtpFd = -1, iRtcpFd = -1;
    USHORT usRtpPort = 0, usRtcpPort = 0;
    MediaFdS *ptRtpFd = NULL;
    MediaFdS *ptRtcpFd = NULL;

    int i = ptGlbData->stCfgInfo.udpStartPort;
    UINT j=0;
    while(i < ptGlbData->stCfgInfo.udpEndPort && j < g_tServerCfg.uConcurrCallMaxNum)
    {
        usRtpPort = i;
        iRtpFd = rceCreateUdpSocketPort(ptGlbData->stCfgInfo.ulLocalIp, i, FD_TYPE_UDP_RTP);
        if(-1 == iRtpFd)
        {
            i = i+2;
            continue;
        }
        ptRtpFd = &ptGlbData->stUdpAddrInfo.udpRtpFdList[j];
        ptRtpFd->recordIdx = j;
        ptRtpFd->fdType = FD_TYPE_UDP_RTP;
        ptRtpFd->usPort = usRtpPort;
        ptRtpFd->iSocketFd = iRtpFd;
        ptRtpFd->ulUCCId = 0;
        ptRtpFd->ulUCCIdbak = 0;
        ptRtpFd->fd_record = -1;
        ptRtpFd->iRceRecordState = Record_IDLE;
        pthread_rwlock_init(&ptRtpFd->mtxUseFg, NULL);
        pthread_rwlock_init(&ptRtpFd->mtxWrMp4, NULL);
        
        i++;

        usRtcpPort = i;
        iRtcpFd = rceCreateUdpSocketPort(ptGlbData->stCfgInfo.ulLocalIp, i, FD_TYPE_UDP_RTCP);
        if(-1 == iRtcpFd)
        {
            close(iRtpFd);
            continue;
        }
        ptRtcpFd = &ptGlbData->stUdpAddrInfo.udpRtcpFdList[j];
        ptRtcpFd->recordIdx = j;
        ptRtcpFd->fdType = FD_TYPE_UDP_RTCP;
        ptRtcpFd->usPort = usRtcpPort;
        ptRtcpFd->iSocketFd = iRtcpFd;
        ptRtcpFd->ulUCCId = 0;
        ptRtcpFd->ulUCCIdbak = 0;
        pthread_rwlock_init(&ptRtcpFd->mtxUseFg, NULL);
        pthread_rwlock_init(&ptRtcpFd->mtxWrMp4, NULL);
        ptRtcpFd->fd_record = -1;
        ptRtcpFd->iRceRecordState = Record_IDLE;
        i++;

        ptGlbData->stUdpAddrInfo.udpFdNum++;
        j++;
    }

    ptGlbData->stUdpAddrInfo.ptSendThread = &g_threadDpSendRtp[0];

    x_InfoLog("[X][UDP]udp socket create ok. fdNum[%d].\n", ptGlbData->stUdpAddrInfo.udpFdNum);

    (void)rceEpollCreate(MAX_RTP_SD_NUM, &ptGlbData->stUdpAddrInfo.udpEpollFd, (char *)"udp");

    XPOC_TRACEFUNC_OUT;
    return XPOC_ERR_SUCCESS;
}

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

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

    /* create UDP port */
    //SINT iRtpFd = -1;
    MediaFdS *ptRtpFd = NULL;

    UINT i = 0;
    for (i = 0; i < g_tServerCfg.uConcurrCallMaxNum; i++)
    {
        ptRtpFd = &ptGlbData->stTcpAddrInfo.tcpRtpFdList[i];
        if(!ptRtpFd)
        {
            x_ErrorLog("ptRtpFd is NULL for i[%d].", i);
            return XPOC_ERR_FAIL;
        }

        ptRtpFd->recordIdx = i;
        ptRtpFd->fdType = FD_TYPE_TCP_RTP;
        ptRtpFd->iSocketFd = -1;
        ptRtpFd->fd_record = -1;
        ptRtpFd->iRceRecordState = Record_IDLE;
        ptRtpFd->ulUCCId = 0;
        ptRtpFd->ulUCCIdbak = 0;
        pthread_rwlock_init(&ptRtpFd->mtxUseFg, NULL);
        pthread_rwlock_init(&ptRtpFd->mtxWrMp4, NULL);
    }

    ptGlbData->stTcpAddrInfo.tcpFdNum = g_tServerCfg.uConcurrCallMaxNum;
    ptGlbData->stTcpAddrInfo.ptSendThread = &g_threadDpSendRtp[1];

    x_InfoLog("[X][TCP]tcp socket create ok. fdNum[%d].\n", ptGlbData->stTcpAddrInfo.tcpFdNum);

    (void)rceEpollCreate(MAX_RTP_SD_NUM, &ptGlbData->stTcpAddrInfo.tcpEpollFd, (char *)"tcp");

    XPOC_TRACEFUNC_OUT;
    return XPOC_ERR_SUCCESS;
}

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

    rceInitUdpSocket(ptModCtx);

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

    XPOC_TRACEFUNC_OUT;
    return XPOC_ERR_SUCCESS;
}

UINT rceGetSsrc(MediaTypeE enType, USHORT ulFdIndex)
{
    struct timeb timer;
    ftime(&timer);
    srand(timer.time * 1000 + timer.millitm);
    int num = rand()%255;
    int tmp = (num << 16);

    unsigned int nSsrc = 0;
    if (enType == MEDIA_AUDIO)
    {
        nSsrc = 0xA0000000 + tmp + ulFdIndex;
    }
    else if (enType == MEDIA_VIDEO)
    {
        nSsrc = 0xB0000000 + tmp + ulFdIndex;
    }
    x_DebugLog("get ssrc[%u:%x], mediaType[%d], fdIndex[%d].", nSsrc, nSsrc, enType, ulFdIndex);
    return nSsrc;
}

/* 占用一个 socket */
static USHORT g_UdpSocketIndex = 0;
UINT rceAllocUdpSocket(T_ModCtxS *ptModCtx, T_RceUCallCtxTable *ptUCC)
{
    XPOC_TRACEFUNC_IN;
    X_NULL_POINTER_CHK_TWO_PARA(ptModCtx, ptUCC);

    UINT ulReturn = XPOC_ERR_SUCCESS;
    RceGlbData_S* ptGlbData = (RceGlbData_S*)ptModCtx->pAppData;
    
    USHORT i = g_UdpSocketIndex;
    UCHAR ubFirst = TRUE;
    USHORT j = 0; 
    for(j = 0; j <= ptGlbData->stUdpAddrInfo.udpFdNum; j++) //确保轮询一遍,且避免死循环
    {
        if (ubFirst == FALSE && i == g_UdpSocketIndex)
        {
            x_ErrorLog("All udpSockets are in use!");
            ulReturn = XPOC_ERR_FAIL;
            break;
        }
        ubFirst = FALSE;

        if(TRUE != ptGlbData->stUdpAddrInfo.udpRtpFdList[i].ubUsedFg)
        {
            MediaFdS *ptRtpFd = &ptGlbData->stUdpAddrInfo.udpRtpFdList[i];
            ptRtpFd->ubUsedFg = TRUE;
            ptRtpFd->ubFirstProc = TRUE;  ///
            ptRtpFd->tcpFdIdx = -1;
            ptRtpFd->ulUCCId = ptUCC->ulRecordIdx;
            ptUCC->usRtpFdIdx = i;
            ptUCC->ulSsrc = rceGetSsrc(MEDIA_AUDIO, ptUCC->usRtpFdIdx);
            ptRtpFd->iRceMediaType = ptUCC->enMediaType;
            ptRtpFd->enRcrdServType = ptUCC->tCallInfoS.enRcrdServType;
            (void)recEpollAddFd(ptGlbData->stUdpAddrInfo.udpEpollFd, ptRtpFd->iSocketFd, (void*)ptRtpFd, (char *)"rtpfd");
            x_DebugLog("[UDP]Alloc rtpFd:fd[%d:%d],UCCId[%d],addr[0x%x:%d],ptGlbData[%p],ssrc[0x%x],RcrdServType[%d].\n",
                       i, ptRtpFd->iSocketFd, ptRtpFd->ulUCCId, ptGlbData->stCfgInfo.ulLocalIp, ptRtpFd->usPort, 
                       ptGlbData, ptUCC->ulSsrc, ptRtpFd->enRcrdServType);

            MediaFdS *ptRtcpFd = &ptGlbData->stUdpAddrInfo.udpRtcpFdList[i];
            ptRtcpFd->ubUsedFg = TRUE;
            ptRtcpFd->ulUCCId = ptUCC->ulRecordIdx;
            ptRtcpFd->enRcrdServType = ptUCC->tCallInfoS.enRcrdServType;
            ptUCC->usRtcpFdIdx = i;
            (void)recEpollAddFd(ptGlbData->stUdpAddrInfo.udpEpollFd, ptRtcpFd->iSocketFd, (void*)ptRtcpFd, (char *)"rtcpfd");
            x_DebugLog("[UDP]Alloc rtcpFd:fd[%d:%d],UCCId[%d],addr[0x%x:%d],ptGlbData[%p],ssrc[0x%x],RcrdServType[%d].\n",
                       i, ptRtcpFd->iSocketFd, ptRtcpFd->ulUCCId, ptGlbData->stCfgInfo.ulLocalIp, ptRtcpFd->usPort, 
                       ptGlbData, ptUCC->ulSsrc, ptRtpFd->enRcrdServType);
            break;
        }

        i++;
        if (i == ptGlbData->stUdpAddrInfo.udpFdNum)
        {
            i = 0;
        }
    }
    g_UdpSocketIndex = (i + 1) % ptGlbData->stUdpAddrInfo.udpFdNum;
    XPOC_TRACEFUNC_OUT;
    return ulReturn;
}

UINT rceFreeUdpSocket(USHORT usRtpFdIdx, USHORT usRtcpFdIdx)
{
    T_ModCtxS *ptModCtx = (T_ModCtxS*)Fplat_GetModuleCtx(XMOD_TYPE_RCE);
    RceGlbData_S* ptGlbData = (RceGlbData_S*)ptModCtx->pAppData;

    pthread_rwlock_wrlock(&ptGlbData->stUdpAddrInfo.udpRtpFdList[usRtpFdIdx].mtxUseFg);    // added by shm

    SINT iRtpFd   = ptGlbData->stUdpAddrInfo.udpRtpFdList[usRtpFdIdx].iSocketFd;
    SINT iRtcpFd  = ptGlbData->stUdpAddrInfo.udpRtcpFdList[usRtcpFdIdx].iSocketFd;
    SINT iEpollFd = ptGlbData->stUdpAddrInfo.udpEpollFd;
    
    (void)rceEpollDelFd(iEpollFd, iRtpFd, (char *)"rtpfd");
    (void)rceEpollDelFd(iEpollFd, iRtcpFd, (char *)"rtcpfd");
    
    ptGlbData->stUdpAddrInfo.udpRtpFdList[usRtpFdIdx].ubUsedFg = FALSE;
    CloseRecordFile(&ptGlbData->stUdpAddrInfo.udpRtpFdList[usRtpFdIdx]);
    ptGlbData->stUdpAddrInfo.udpRtpFdList[usRtpFdIdx].ulUCCId  = 0;
    ptGlbData->stUdpAddrInfo.udpRtpFdList[usRtpFdIdx].ulCliIp = 0;
    ptGlbData->stUdpAddrInfo.udpRtpFdList[usRtpFdIdx].rtpCliPort  = 0;
    ptGlbData->stUdpAddrInfo.udpRtpFdList[usRtpFdIdx].rtcpCliPort = 0;
    
    ptGlbData->stUdpAddrInfo.udpRtcpFdList[usRtcpFdIdx].ubUsedFg = FALSE;
    ptGlbData->stUdpAddrInfo.udpRtcpFdList[usRtcpFdIdx].ulUCCId  = 0;
    ptGlbData->stUdpAddrInfo.udpRtcpFdList[usRtcpFdIdx].ulCliIp = 0;
    ptGlbData->stUdpAddrInfo.udpRtcpFdList[usRtcpFdIdx].rtpCliPort  = 0;
    ptGlbData->stUdpAddrInfo.udpRtcpFdList[usRtcpFdIdx].rtcpCliPort = 0;
    //ptGlbData->stUdpAddrInfo.udpFdNum --;

    XPOCStopTimer(ptGlbData->stUdpAddrInfo.udpRtpFdList[usRtpFdIdx].stTimer.TimerId);

    pthread_rwlock_unlock(&ptGlbData->stUdpAddrInfo.udpRtpFdList[usRtpFdIdx].mtxUseFg); // added by shm
    return XPOC_ERR_SUCCESS;
}

/* 占用一个 socket */
static USHORT g_TcpSocketIndex = 0;
UINT rceAllocTcpSocket(T_ModCtxS *ptModCtx, T_RceUCallCtxTable *ptUCC)
{
    XPOC_TRACEFUNC_IN;
    X_NULL_POINTER_CHK_TWO_PARA(ptModCtx, ptUCC);

    UINT ulReturn = XPOC_ERR_SUCCESS;
    RceGlbData_S* ptGlbData = (RceGlbData_S*)ptModCtx->pAppData;

    USHORT i = g_TcpSocketIndex;
    UCHAR ubFirst = TRUE;
    USHORT j = 0; 
    for(j = 0; j <= ptGlbData->stTcpAddrInfo.tcpFdNum; j++) //确保轮询一遍,且避免死循环
    {
        if (ubFirst == FALSE && i == g_TcpSocketIndex)
        {
            x_ErrorLog("All tcpSockets are in use!");
            ulReturn = XPOC_ERR_FAIL;
            break;
        }
        ubFirst = FALSE;

        if (TRUE != ptGlbData->stTcpAddrInfo.tcpRtpFdList[i].ubUsedFg)
        {
            MediaFdS *ptRtpFd = &ptGlbData->stTcpAddrInfo.tcpRtpFdList[i];
            ptRtpFd->ubUsedFg = TRUE;
            ptRtpFd->ubClosingFd = FALSE; ///
            ptRtpFd->tcpFdIdx = -1;
            ptRtpFd->ulUCCId = ptUCC->ulRecordIdx;
            ptUCC->usTcpRtpFdIdx = i;
            ptUCC->ulSsrc = rceGetSsrc(MEDIA_VIDEO, ptUCC->usTcpRtpFdIdx);
            ptRtpFd->iRceMediaType = ptUCC->enMediaType;
            ptRtpFd->iRecvBufferLen = 0;
            ptRtpFd->enRcrdServType = ptUCC->tCallInfoS.enRcrdServType;

            int num = 0;   //防止创建 tcp socket 一直失败,限定尝试5次
            while (-1 == ptRtpFd->iSocketFd)
            {
                ptRtpFd->iSocketFd = rceCreateTcpSocketPort();
                if (-1 == ptRtpFd->iSocketFd)
                {
                    x_ErrorLog("rceCreateTcpSocketPort failed : %s(errno: %d))\n", strerror(errno), errno);
                    num++;
                    if(num >= 5)
                    {
                        break;
                    }
                    continue;
                }
            }

            /*设置服务器ip*/
            struct sockaddr_in s_addr;
            memset(&s_addr, 0, sizeof(s_addr));
            s_addr.sin_family = AF_INET;
            s_addr.sin_port = htons(ptUCC->stMediaInfo.rtpPort);
            s_addr.sin_addr.s_addr = htonl(ptUCC->stMediaInfo.ulIpAddr);
            /*开始连接服务器*/
            if (connect(ptRtpFd->iSocketFd, (struct sockaddr*)&s_addr, sizeof(struct sockaddr)) == -1) {
                x_ErrorLog("[TCP]connect failed:fd[%d:%d],UCCId[%d],addr[%d-%d],ptGlbData[%p],ssrc[0x%x].\n",
                    i, ptRtpFd->iSocketFd, ptRtpFd->ulUCCId, ptUCC->stMediaInfo.ulIpAddr,
                    ptUCC->stMediaInfo.rtpPort, ptGlbData, ptUCC->ulSsrc);
                return XPOC_ERR_FAIL;
            }

            struct sockaddr_in client;
            int clientAddrLen = sizeof(client);
            if (getsockname(ptRtpFd->iSocketFd, (struct sockaddr*)&client, (socklen_t*)&clientAddrLen) != 0) 
            {
                x_ErrorLog("client get port error: %s(errno: %d))\n", strerror(errno), errno);
                return XPOC_ERR_FAIL;
            }
            ptRtpFd->usPort = ntohs(client.sin_port);
            (void)recEpollAddFd(ptGlbData->stTcpAddrInfo.tcpEpollFd, ptRtpFd->iSocketFd, (void*)ptRtpFd, (char *)"tcprtpfd");
            x_DebugLog("[TCP]Alloc TcprtpFd:fd[%d:%d],UCCId[%d],addr[0x%x:%d],ptGlbData[%p],ssrc[0x%x].\n",
                i, ptRtpFd->iSocketFd, ptRtpFd->ulUCCId, ptGlbData->stCfgInfo.ulLocalIp,
                ptRtpFd->usPort, ptGlbData, ptUCC->ulSsrc);

            break;
        }

        i++;
        if (i == ptGlbData->stTcpAddrInfo.tcpFdNum)
        {
            i = 0;
        }
    }
    g_TcpSocketIndex = (i + 1) % ptGlbData->stTcpAddrInfo.tcpFdNum;
    XPOC_TRACEFUNC_OUT;
    return ulReturn;
}

UINT rceFreeTcpSocket(USHORT usRtpFdIdx)
{
    T_ModCtxS *ptModCtx = (T_ModCtxS*)Fplat_GetModuleCtx(XMOD_TYPE_RCE);
    RceGlbData_S* ptGlbData = (RceGlbData_S*)ptModCtx->pAppData;

    MediaFdS *ptMediaFd = &ptGlbData->stTcpAddrInfo.tcpRtpFdList[usRtpFdIdx];

    pthread_rwlock_wrlock(&ptMediaFd->mtxUseFg);    // added by shm

    ptMediaFd->ubClosingFd = TRUE; ///

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

    (void)rceEpollDelFd(iEpollFd, iRtpFd, (char *)"tcprtpfd");
    close(iRtpFd); 
    x_DebugLog("[X]close tcp socket. tcprtpfd[%d].\n", iRtpFd);
    ptGlbData->stTcpAddrInfo.tcpRtpFdList[usRtpFdIdx].iSocketFd = -1;

    ptMediaFd->ubUsedFg = FALSE;
    CloseRecordFile(ptMediaFd);
    ptMediaFd->ulUCCId  = 0;
    ptMediaFd->ulCliIp = 0;
    ptMediaFd->rtpCliPort  = 0;
    ptMediaFd->rtcpCliPort = 0;
    ptMediaFd->iRecvBufferLen = 0;
    memset(ptMediaFd->szRecordFileName, 0, STR_LEN512);   // added by shm

    XPOCStopTimer(ptMediaFd->stTimer.TimerId);

    pthread_rwlock_unlock(&ptMediaFd->mtxUseFg);    // added by shm

    return XPOC_ERR_SUCCESS;
}


UINT rceCloseMediaSocket(T_RceUCallCtxTable *ptUCC)
{
    XPOC_TRACEFUNC_IN;
    X_NULL_POINTER_CHK_ONE_PARA(ptUCC);

    if(MEDIA_AUDIO == ptUCC->enMediaType)
    {
        rceFreeUdpSocket(ptUCC->usRtpFdIdx, ptUCC->usRtcpFdIdx);
    }
    else if(MEDIA_VIDEO == ptUCC->enMediaType)
    {
        rceFreeTcpSocket(ptUCC->usTcpRtpFdIdx);
    }

    XPOC_TRACEFUNC_OUT;
    return XPOC_ERR_SUCCESS;
}


#if 0
#endif


UINT rceGetCB(T_ModMsgHead* ptXPocHdr, UINT *ptCBId, RceCtrlBlockS **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 = (RceCtrlBlockS*)XPOCAllocCtrlBlock(ptCBId);
        if(NULL == *pptCB)
        {
            x_ErrorLog(" Call Alloc CB failed.\n");
            return XPOC_ERR_LOCATE_CB;
        }
        memset(*pptCB, 0, sizeof(RceCtrlBlockS));
        (*pptCB)->ulCBId = *ptCBId;
    }
    else if(EV_SIP_BYE_IND == ptXPocHdr->ulMsgId)
    {
        x_InfoLog("get cbid[%d] while recv EV_SIP_BYE_IND.\n", *ptCBId);
        *pptCB = (RceCtrlBlockS*)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;
}


/*****************  函数说明 **********************
* 函数名    : MrIdxMangePoolInit
* 功能说明  : 初始化队列索引管理上下文
*************************************************/
UINT rceRmIdxManageInitCtx(
    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;
}

/*************************************************
*****************  函数说明 **********************
* 函数名    : rceRmIdxManageGetFromHead
* 功能说明  : 从头部开始获取结点,获取后队列跳过结点(注,FIFO,从队尾插入)
*************************************************/
UINT rceRmIdxManageGetFromHead
(
    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;
}

/*************************************************
*****************  函数说明 **********************
* 函数名    : rceRmIdxManageGetFromHead
* 功能说明  : 插入结点到尾部(注,FIFO,从头部开始获取结点)
*************************************************/
UINT rceRmIdxManageInsertToTail(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: 队列索引管理函数   
/*************************************************
*****************  函数说明 **********************
* 函数名    : rceRmIdxManageGetFreeNode
* 功能说明  : 获取可以接收数据(空闲)的结点
*************************************************/
UINT rceRmIdxManageGetFreeNode(MrIdxMangeCtx_t* pManageCtx, NodeIndex_S **ppNodeIndex)
{
    X_NULL_POINTER_CHK_TWO_PARA(pManageCtx, ppNodeIndex);
    
    return rceRmIdxManageGetFromHead(&pManageCtx->stFreeList, ppNodeIndex);
}

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

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

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

/*************************************************
*****************  函数说明 **********************
* 函数名    : rceRmIdxManageInsertToFreeList
* 功能说明  : 将要发送的结点插入发送列表
*************************************************/
UINT rceRmIdxManageInsertToSendingList(MrIdxMangeCtx_t* pManageCtx, NodeIndex_S* pNodeIndex)
{
    X_NULL_POINTER_CHK_TWO_PARA(pManageCtx, pNodeIndex);
    
    return rceRmIdxManageInsertToTail(&pManageCtx->stSendList, pNodeIndex);
}

///End: 队列索引管理函数   

rceSignalFunc.c


#include <math.h>
#include "rceResourceFunc.h"
#include "rceSignalFunc.h"

#if 0
#endif
extern T_ServerCfg g_tServerCfg;

/*从请求行获取CallId*/
UINT rceGetCallIdFromSipHeader(osip_message_t *ptOsipMessage, char *ptCallId)
{
    XPOC_TRACEFUNC_IN;
    X_NULL_POINTER_CHK_TWO_PARA(ptOsipMessage, ptCallId);

    UINT ulReturn = XPOC_ERR_SUCCESS;
    osip_call_id_t *ptCall_Id = osip_message_get_call_id(ptOsipMessage);
    if (ptCall_Id)
    {
        char *tmp = NULL;
        osip_call_id_to_str(ptCall_Id, &tmp);
        sprintf(ptCallId, "%s", tmp);
        osip_free(tmp);
    }
    else
    {
        x_ErrorLog("get callId from sip.from failed.");
        ulReturn = XPOC_ERR_FAIL;
    }

    XPOC_TRACEFUNC_OUT;
    return ulReturn;
}

UINT rceGetServIdFromSipHeader(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_ErrorLog("get servId from sip.from failed.");
        ulReturn = XPOC_ERR_FAIL;
    }

    XPOC_TRACEFUNC_OUT;
    return ulReturn;
}

UINT RceSetSdpAudioMedia(T_RceUCallCtxTable *ptUCC, sdp_message_t *ptSdpMessage)
{
    XPOC_TRACEFUNC_IN;
    X_NULL_POINTER_CHK_TWO_PARA(ptUCC, ptSdpMessage);

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

    T_ModCtxS *ptModCtx = (T_ModCtxS*)Fplat_GetModuleCtx(XMOD_TYPE_RCE);
    RceGlbData_S* ptGlbData = (RceGlbData_S*)ptModCtx->pAppData;


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

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

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

//    /* 对audio增加a行: a=rtcp:40001 IN IP4 192.168.101.254 */
//    UINT sdpIp4 = ptGlbData->stCfgInfo.ulLocalIp;
//    
//    char addr[STR_LEN32] = {0};
//    memset(addr, 0, STR_LEN32);
//    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");
//    sprintf(att_value,"%d IN IP4 %s", ptGlbData->stUdpAddrInfo.udpRtcpFdList[ptUCC->usRtcpFdIdx].usPort, addr);
//    sdp_message_a_attribute_add (ptSdpMessage, pos, att_field, att_value);

    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 = (char*)osip_malloc(128*sizeof(UCHAR));
        att_value = (char*)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 = (char*)osip_malloc(8*sizeof(UCHAR));
    sprintf(sPort, "%d", ptGlbData->stUdpAddrInfo.udpRtpFdList[ptUCC->usRtpFdIdx].usPort);
    sdp_message_m_port_set(ptSdpMessage, pos, sPort);

    //a=recvonly
    att_field = (char*)osip_malloc(16*sizeof(UCHAR));
    mdeTopoly2Str(SDP_MODE_RECVONLY, att_field);
    sdp_message_a_attribute_add(ptSdpMessage, pos, att_field, NULL);

    att_field = (char*)osip_malloc(128 * sizeof(UCHAR));
    sprintf(att_field, "ssrc:%d", ptUCC->ulSsrc);
    sdp_message_a_attribute_add(ptSdpMessage, pos, att_field, NULL);

    XPOC_TRACEFUNC_OUT;
    return XPOC_ERR_SUCCESS; 
}

UINT RceSetSdpVideoMedia(T_RceUCallCtxTable *ptUCC, sdp_message_t *ptSdpMessage)
{
    XPOC_TRACEFUNC_IN;
    X_NULL_POINTER_CHK_TWO_PARA(ptUCC, ptSdpMessage);

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

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

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

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

    char *att_field = (char*)osip_malloc(STR_LEN128 * sizeof(UCHAR));
    char *att_value = (char*)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 = (char*)osip_malloc(128*sizeof(UCHAR));
        att_value = (char*)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 = (T_ModCtxS*)Fplat_GetModuleCtx(XMOD_TYPE_RCE);
    RceGlbData_S* ptGlbData = (RceGlbData_S*)ptModCtx->pAppData;

    char *sPort = (char*)osip_malloc(8*sizeof(UCHAR));
    sprintf(sPort, "%d", ptGlbData->stTcpAddrInfo.tcpRtpFdList[ptUCC->usTcpRtpFdIdx].usPort);
    sdp_message_m_port_set(ptSdpMessage, pos, sPort);

    //a=recvonly
    att_field = (char*)osip_malloc(16*sizeof(UCHAR));
    mdeTopoly2Str(SDP_MODE_RECVONLY, att_field);
    sdp_message_a_attribute_add(ptSdpMessage, pos, att_field, NULL);

    att_field = (char*)osip_malloc(128 * sizeof(UCHAR));
    sprintf(att_field, "ssrc:%d", ptUCC->ulSsrc);
    sdp_message_a_attribute_add(ptSdpMessage, pos, att_field, NULL);

    XPOC_TRACEFUNC_OUT;
    return XPOC_ERR_SUCCESS; 
}

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

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

    //set sdp.o
    char *o_username = (char*)osip_malloc(32*sizeof(UCHAR));
    char *o_sess_id = (char*)osip_malloc(32*sizeof(UCHAR));
    char *sess_version = (char*)osip_malloc(32*sizeof(UCHAR));
    char *o_nettype = (char*)osip_malloc(32*sizeof(UCHAR));
    char *o_addrtype = (char*)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  = (char*)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 = (char*)osip_malloc(32*sizeof(UCHAR));
    char *c_addrtype0 = (char*)osip_malloc(32*sizeof(UCHAR));
    sprintf(c_nettype0, "IN");
    sprintf(c_addrtype0, "IP4");
    char *addr0 = (char*)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 = (char*)osip_malloc(16*sizeof(UCHAR));
    char *c_addrtype1 = (char*)osip_malloc(16*sizeof(UCHAR));
    sprintf(c_nettype1, "IN");
    sprintf(c_addrtype1, "IP4");
    char *addr1 = (char*)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 = (char*)osip_malloc(16*sizeof(UCHAR));
        char *c_addrtype2 = (char*)osip_malloc(16*sizeof(UCHAR));
        sprintf(c_nettype2, "IN");
        sprintf(c_addrtype2, "IP4");
        char *addr2 = (char*)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 rceGetMediaInfoFromSipInvite(T_ModMsgHead* ptXpocMsgHdr, MediaDescriptionS *ptAudio, MediaDescriptionS *ptVideo)
{
    XPOC_TRACEFUNC_IN;
    X_NULL_POINTER_CHK_THREE_PARA(ptXpocMsgHdr, ptAudio, ptVideo);

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

    if(!ptSdp)
    {
        x_ErrorLog("get sdp from invite failed.");
        return XPOC_ERR_FAIL;
    }

    char aucSipCallId[STR_LEN32] = {0};
    (void)rceGetCallIdFromSipHeader(ptSipInd->ulSipHandle->request, aucSipCallId);

    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:ipAddr[0x%x:%d],codec[%d],topoly[%d],ssrc[0x%x(%u)].",  
                aucSipCallId, ptAudio->ulIpAddr, ptAudio->rtpPort, ptAudio->stSdpCodec[0].ulCodecNo, ptAudio->enTopoly, ptAudio->ulSsrc);
    }

    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:ipAddr[0x%x:%d],codec[%d],topoly[%d],ssrc[0x%x].", 
                aucSipCallId, ptVideo->ulIpAddr, ptVideo->rtpPort, ptVideo->stSdpCodec[0].ulCodecNo, ptVideo->enTopoly, ptVideo->ulSsrc);
    }

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

    XPOC_TRACEFUNC_OUT;
    return XPOC_ERR_SUCCESS;
}

UINT rceSaveMediaInfo2UCC(MediaDescriptionS *ptAudio, MediaDescriptionS *ptVideo, T_RceUCallCtxTable *ptUCC)
{
    XPOC_TRACEFUNC_IN;
    X_NULL_POINTER_CHK_ONE_PARA(ptUCC);

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

    XPOC_TRACEFUNC_OUT;
    return XPOC_ERR_SUCCESS;
}

UINT rceBuildSendSdpOnRecvSdp
(
    sdp_media_t *ptAudioSdp,
    sdp_media_t *ptVideoSdp,
    T_RceUCallCtxTable *ptUCC_Audio,
    T_RceUCallCtxTable *ptUCC_Video,
    sdp_message_t *pSendSdpMsg
)
{
    XPOC_TRACEFUNC_IN;
    X_NULL_POINTER_CHK_ONE_PARA(pSendSdpMsg);
    x_DebugLog("input:ptAudioSdp[%p],ptVideoSdp[%p],ptUCC_Audio[%p],ptUCC_Video[%p].",
                ptAudioSdp, ptVideoSdp, ptUCC_Audio, ptUCC_Video);

    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 */
        (void)RceSetSdpAudioMedia(ptUCC_Audio, pSendSdpMsg);
    }
    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 */
        (void)RceSetSdpVideoMedia(ptUCC_Video, pSendSdpMsg);
    }

    XPOC_TRACEFUNC_OUT;
    return XPOC_ERR_SUCCESS;   
}

UINT rceBuildSdpForInviteRsp
(
    T_ModCtxS *ptModCtx,
    T_ModMsgHead *ptXpocMsgHdr,
    T_RceUCallCtxTable *ptUCC_Audio,
    T_RceUCallCtxTable *ptUCC_Video,
    sdp_message_t **pptSendSdp
)
{
    XPOC_TRACEFUNC_IN;
    X_NULL_POINTER_CHK_TWO_PARA(ptModCtx, ptXpocMsgHdr);

    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(!ptRecvSdp)
    {
        x_ErrorLog("recv invite without sdp.");
        return XPOC_ERR_FAIL;
    }
    sdp_message_clone(ptRecvSdp, &ptSendSdpTmp);
    if (ptRecvSdp)
    {
        sdp_message_free(ptRecvSdp);
        ptRecvSdp = NULL;
    }

    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 = rceBuildSendSdpOnRecvSdp(ptAudioSdp, ptVideoSdp, ptUCC_Audio, ptUCC_Video, ptSendSdpTmp);
    }
    else
    {
        return XPOC_ERR_FAIL;
    }

    UINT ulMediaNum = 0;
    if (ptAudioSdp && ptUCC_Audio)
    {
        ulMediaNum++;
    }
    if (ptVideoSdp && ptUCC_Video)
    {
        ulMediaNum++;
    }

    RceGlbData_S *ptGlbData = (RceGlbData_S*)ptModCtx->pAppData;
    (void)rceBuildSdpCommInfo(ptSendSdpTmp, ptGlbData->stCfgInfo.ulLocalIp, ulMediaNum);

    *pptSendSdp = ptSendSdpTmp;

    XPOC_TRACEFUNC_OUT;
    return ulReturn;
}


#if 0
#endif

UINT rceSendInviteRsp(SipMsgIndS *pRcvSipMsg, sdp_message_t *SdpSession, RceCtrlBlockS *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_InfoLog("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);
        x_InfoLog("Invite response sdp:%s.", 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_ErrorLog("pSendSdpMsg is NULL.");
    }

    ulReturn = XPOCSendSipRspMsg(&stSipRsp, 0, SIP_THREAD_ONE);
    if (ptNewBody)
    {
        osip_free(ptNewBody);
        sdp_message_free(SdpSession);
    }

    XPOC_TRACEFUNC_OUT;
    return ulReturn;
}

UINT rceSendByeRsp(SipMsgIndS *pRcvSipMsg, RceCtrlBlockS *ptCB, UINT StatusCode)
{
    UINT  ulReturn = XPOC_ERR_SUCCESS;

    XPOC_TRACEFUNC_IN;
    X_NULL_POINTER_CHK_TWO_PARA(pRcvSipMsg, ptCB);

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

    stSipRsp.sipMsgId = EV_SIP_BYE_REQ;
    stSipRsp.nTid = pRcvSipMsg->ulSipHandle->tid;
    stSipRsp.ulSrcCbId = ptCB->ulCBId; //pRcvSipMsg->ulDstCbId;
    stSipRsp.statusCode = StatusCode;
    
    ulReturn = XPOCSendSipRspMsg(&stSipRsp, 0, SIP_THREAD_ONE);

    XPOC_TRACEFUNC_OUT;
    return ulReturn;
}

UINT rceHandleSipInvite(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;
    RceCtrlBlockS *ptCB = NULL;

    T_RceUCallCtxTable stUCC;
    
    SipMsgIndS *ptSipMsgInd = (SipMsgIndS*)ptXpocMsgHdr->stMsgBody;
    sdp_message_t *ptSendSdp = NULL;

    char aucServId[STR_LEN32] = {0};
    char aucSipCallId[STR_LEN32] = {0};

    T_RceUCallCtxTable *ptUCC_Audio = NULL;
    T_RceUCallCtxTable *ptUCC_Video = NULL;

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

    MediaDescriptionS stAudio;
    MediaDescriptionS stVideo;
    memset(&stAudio, 0, sizeof(MediaDescriptionS));
    memset(&stVideo, 0, sizeof(MediaDescriptionS));
    stAudio.bCodecNum = INVALID_VALUE_UCHAR;
    stVideo.bCodecNum = INVALID_VALUE_UCHAR;

    CallInfoS stCallInfo;

    /*
    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
    {
        memset(aucSipCallId, 0, STR_LEN32);
        (void)rceGetCallIdFromSipHeader(ptSipMsgInd->ulSipHandle->request, aucSipCallId);
        memset(aucServId, 0, STR_LEN32);
        (void)rceGetServIdFromSipHeader(ptSipMsgInd->ulSipHandle->request, aucServId);
        memset(&stCallInfo, 0, sizeof(CallInfoS));
        (void)XPOCGetCallnfoSipHeader(ptSipMsgInd->ulSipHandle->request, &stCallInfo);/* vidgrptrans,vidpritrans,record 时使用来传递mediaCCId */
        if (Serv_AudioRcrdPri != stCallInfo.enServType 
            && Serv_VideoRcrdPri != stCallInfo.enServType
            && Serv_AudioRcrdGrp != stCallInfo.enServType
            && Serv_VideoRcrdGrp != stCallInfo.enServType)
        {
            x_ErrorLog("rce get CallInfo enServType failed, call-id [%s].\n", aucSipCallId);
            ulReturn = XPOC_ERR_INVALID_MSG;
            break;
        }

        ulReturn = rceGetCB(ptXpocMsgHdr, &ulCBId, &ptCB);
        if(XPOC_ERR_SUCCESS != ulReturn || NULL == ptCB)
        {
            x_ErrorLog("rce get CtrlBlock failed, retcode[%d],ptCB[%p], sip call-id[%s].\n", ulReturn, ptCB, aucSipCallId);
            break;
        }
        ptCB->ulCurrMsgId = ptXpocMsgHdr->ulMsgId;
        //ptCB->ptSpSipHandle = ptSipMsgInd->ulSipHandle;

        (void)rceGetMediaInfoFromSipInvite(ptXpocMsgHdr, &stAudio, &stVideo);
        if (INVALID_VALUE_UCHAR == stAudio.bCodecNum && INVALID_VALUE_UCHAR == stVideo.bCodecNum)
        {
            x_WarnLog("get media info from caller.invite.sdp failed. sip call-id [%s]", aucSipCallId);
            break;
        }   
        int rtpFdIdx = -1;
        if (INVALID_VALUE_UCHAR != stAudio.bCodecNum)
        {
            memset(&stUCC, 0, sizeof(T_RceUCallCtxTable));
            stUCC.enMediaType = MEDIA_AUDIO;
            (void)rceSaveMediaInfo2UCC(&stAudio, &stVideo, &stUCC);
            // alloc UCC
            memcpy(&(stUCC.tCallInfoS), &stCallInfo, sizeof(CallInfoS));
            memcpy(stUCC.aucServId, aucServId, strlen(aucServId));
            memcpy(stUCC.aucCallId, aucSipCallId, strlen(aucSipCallId));

            ulReturn = XRceUCallCtxOpr(XDB_OPR_ADD, stUCC.aucServId, stUCC.aucCallId, stUCC.enMediaType, NULL, NULL, &stUCC);
            if (XPOC_ERR_SUCCESS != ulReturn)
            {
                x_ErrorLog("Qry UCallCtx fail.");
                break;
            }

            // open udp socket
            ulReturn = rceAllocUdpSocket(ptModCtx, &stUCC);
            if (XPOC_ERR_SUCCESS != ulReturn)
            {
                x_ErrorLog("rceAllocUdpSocket failed -- udp audio connect failed -- aucServId[%s] call-id[%s] ipAddr[0x%x:%d]", 
                        stUCC.aucServId, stUCC.aucCallId, stUCC.stMediaInfo.ulIpAddr, stUCC.stMediaInfo.rtpPort);
                (void)XRceUCallCtxOpr(XDB_OPR_DEL, NULL, 0, (MediaTypeE)0, NULL, &stUCC.ulRecordIdx, &stUCC);
                break;
            }
            ///
            rtpFdIdx = stUCC.usRtpFdIdx;

            stUCC.ulCBId = ptCB->ulCBId;
            ulReturn = XRceUCallCtxOpr(XDB_OPR_MOD, NULL, 0, (MediaTypeE)0, NULL, &stUCC.ulRecordIdx, &stUCC);
            if (XPOC_ERR_SUCCESS != ulReturn)
            {
                x_ErrorLog("Modify UCallCtx fail.");
                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++;

            memcpy(&stUCC_Audio, &stUCC, sizeof(T_RceUCallCtxTable));
            ptUCC_Audio = &stUCC_Audio;

            x_InfoLog("Audio media:sip call-id [%s], servId [%s], servType [%d], cbId [%d], ucc id [%d]", 
                        aucSipCallId, stUCC.aucServId, stCallInfo.enServType, ptCB->ulCBId, stUCC.ulRecordIdx);
        }
        if (INVALID_VALUE_UCHAR != stVideo.bCodecNum)
        {
            memset(&stUCC, 0, sizeof(T_RceUCallCtxTable));
            stUCC.enMediaType = MEDIA_VIDEO;
            (void)rceSaveMediaInfo2UCC(&stAudio, &stVideo, &stUCC);
            // alloc UCC
            memcpy(&(stUCC.tCallInfoS), &stCallInfo, sizeof(CallInfoS));
            memcpy(stUCC.aucServId, aucServId, strlen(aucServId));
            memcpy(stUCC.aucCallId, aucSipCallId, strlen(aucSipCallId));

            ulReturn = XRceUCallCtxOpr(XDB_OPR_ADD, stUCC.aucServId, stUCC.aucCallId, stUCC.enMediaType, NULL, NULL, &stUCC);
            if (XPOC_ERR_SUCCESS != ulReturn)
            {
                x_ErrorLog("Add UCallCtx fail.");
                break;
            }

            // open tcp socket
            ulReturn = rceAllocTcpSocket(ptModCtx, &stUCC);
            if (XPOC_ERR_SUCCESS != ulReturn)
            {
                x_ErrorLog("rceAllocTcpSocket failed -- tcp video connect failed -- aucServId[%s] call-id[%s] ipAddr[0x%x:%d]", 
                        stUCC.aucServId, stUCC.aucCallId, stUCC.stMediaInfo.ulIpAddr, stUCC.stMediaInfo.rtpPort);
                (void)XRceUCallCtxOpr(XDB_OPR_DEL, NULL, 0, (MediaTypeE)0, NULL, &stUCC.ulRecordIdx, &stUCC);
                break;
            }

            stUCC.ulCBId = ptCB->ulCBId;
            ulReturn = XRceUCallCtxOpr(XDB_OPR_MOD, NULL, 0, (MediaTypeE)0, NULL, &stUCC.ulRecordIdx, &stUCC);
            if (XPOC_ERR_SUCCESS != ulReturn)
            {
                x_ErrorLog("Modify UCallCtx fail.");
                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++;
            
            memcpy(&stUCC_Video, &stUCC, sizeof(T_RceUCallCtxTable));
            ptUCC_Video = &stUCC_Video;/* video UCC */

            ///
            if(rtpFdIdx != -1) {
                MediaFdS *ptRtpFd = NULL;
                RceGlbData_S* ptGlbData = (RceGlbData_S*)ptModCtx->pAppData;
                ptRtpFd = &ptGlbData->stUdpAddrInfo.udpRtpFdList[rtpFdIdx];
                ptRtpFd->tcpFdIdx = stUCC.usTcpRtpFdIdx;
            }

            x_InfoLog("Vedio media:sip call-id [%s], servId [%s], servType [%d], cbId [%d], ucc id [%d]", 
                        aucSipCallId, stUCC.aucServId, stCallInfo.enServType, ptCB->ulCBId, stUCC.ulRecordIdx);
        }

        ulReturn = rceBuildSdpForInviteRsp(ptModCtx, ptXpocMsgHdr, ptUCC_Audio, ptUCC_Video, &ptSendSdp);
        if (XPOC_ERR_SUCCESS != ulReturn)
        {
            x_ErrorLog("rceAllocTcpSocket failed -- tcp video connect failed -- aucServId[%s] call-id[%s] ipAddr[0x%x:%d]",
                stUCC.aucServId, stUCC.aucCallId, stUCC.stMediaInfo.ulIpAddr, stUCC.stMediaInfo.rtpPort);
            ptSendSdp = NULL;
        }
    }while(0);

    SipStatusCodeE enSipStatus = (SipStatusCodeE)((XPOC_ERR_SUCCESS == ulReturn) ? 200 : 403);
    (void)rceSendInviteRsp(ptSipMsgInd, ptSendSdp, ptCB, enSipStatus);
  
    /* 失败处理,要回收控制块资源和对应的UCallCtx资源 */
    if(XPOC_ERR_SUCCESS != ulReturn)
    {
        if(ptCB)
        {
            UCHAR i = 0;
            T_RceUCallCtxTable stUCCTmp;
            for(i = 0; i < ptCB->StreamNum; i++)
            {
                ulReturn = XRceUCallCtxOpr(XDB_OPR_QRY, NULL, 0, (MediaTypeE)0, NULL, &ptCB->StreamInfoArray[i].ulUCallCtxId, &stUCCTmp);
                if(XPOC_ERR_SUCCESS != ulReturn)
                {
                    rceCloseMediaSocket(&stUCCTmp);
                    (void)XRceUCallCtxOpr(XDB_OPR_DEL, NULL, 0, (MediaTypeE)0, NULL, &stUCCTmp.ulRecordIdx, &stUCCTmp);
                }
            }
            (void)XPOCFreeCtrlBlock(ptCB->ulCBId, 0);
        }
    }
    else
    {
        if((INVALID_VALUE_UCHAR != stVideo.bCodecNum) || (INVALID_VALUE_UCHAR != stAudio.bCodecNum))
        {
            MediaFdS *ptRtpFd = NULL;
            RceGlbData_S* ptGlbData = (RceGlbData_S*)ptModCtx->pAppData;

            if(INVALID_VALUE_UCHAR != stVideo.bCodecNum)
            {
                ptRtpFd = &ptGlbData->stTcpAddrInfo.tcpRtpFdList[stUCC.usTcpRtpFdIdx];
                ptRtpFd->stTimer.TimerType = RCE_TIMER_SEND_TCP_TEST_PKT_FIRST;
                ptRtpFd->stTimer.uiParam2 = stUCC.usTcpRtpFdIdx;
            }
            else if(INVALID_VALUE_UCHAR != stAudio.bCodecNum)
            {
                ptRtpFd = &ptGlbData->stUdpAddrInfo.udpRtpFdList[stUCC.usRtpFdIdx];
                ptRtpFd->stTimer.TimerType = RCE_TIMER_SEND_UDP_TEST_PKT_FIRST;
                ptRtpFd->stTimer.uiParam2 = stUCC.usRtpFdIdx;
            }

            XPOCStartTimer(0, 100000, &ptRtpFd->stTimer, TIME_FLAG_ONCE);/* 延迟 0.1s */
            x_InfoLog("rce timer start: timeType[%d],timerId[%lld],timeLen[0.1].",
                       ptRtpFd->stTimer.TimerType, ptRtpFd->stTimer.TimerId);
        }

        /* 对于audio-record, 向h-user-voice中插入一条记录 */
        if (Serv_AudioRcrdPri == stCallInfo.enServType 
            || Serv_AudioRcrdGrp == stCallInfo.enServType
            /*|| (Serv_VideoRcrdPri == stCallInfo.enServType 
                && Serv_VideoPri == stCallInfo.enRcrdServType 
                && (INVALID_VALUE_UCHAR == stVideo.bCodecNum))*/)
        {
            (void)rceAddVoiceCallInfo2DB(stUCC.ulRecordIdx);
        }

        if ((Serv_VideoRcrdPri == stCallInfo.enServType) /*&& (INVALID_VALUE_UCHAR != stVideo.bCodecNum)*/
            || Serv_VideoRcrdGrp == stCallInfo.enServType)
        {
            (void)rceAddVideoCallInfo2DB(stUCC.ulRecordIdx);
        }
    }
  
    XPOC_TRACEFUNC_OUT;
    return XPOC_ERR_SUCCESS;
}

UINT rceHandleSipBye(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 ulUCallCtxId = 0;
    char aucSipCallId[32] = {0};

    (void)rceGetCallIdFromSipHeader(ptSipMsgInd->ulSipHandle->request, aucSipCallId);
    x_InfoLog("Recv sip bye, call-id [%s], cbid [%d]", aucSipCallId, ulCBId);

    RceCtrlBlockS *ptCB = NULL;
    T_RceUCallCtxTable stUCC;
    T_RceUCallCtxTable stUCCTmp;

    do
    {
        ulReturn = rceGetCB(ptXpocMsgHdr, &ulCBId, &ptCB);
        if (XPOC_ERR_SUCCESS != ulReturn || NULL == ptCB)
        {
            x_ErrorLog("rce get CtrlBlock failed, retcode[%d],ptCB[%p].\n", ulReturn, ptCB);
            break;
        }

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

            memset(&stUCCTmp, 0, sizeof(T_RceUCallCtxTable));
            memcpy(&stUCCTmp, &stUCC, sizeof(T_RceUCallCtxTable));

            /// upd db before del
            RceGlbData_S* ptGlbData = (RceGlbData_S*)ptModCtx->pAppData;
            {
                if(MEDIA_AUDIO == stUCC.enMediaType && stUCC.usRtpFdIdx < g_tServerCfg.uConcurrCallMaxNum)
                {
                    MediaFdS *ptMediaFd = &ptGlbData->stUdpAddrInfo.udpRtpFdList[stUCC.usRtpFdIdx];
                    if(ptMediaFd->iRceRecordState == Record_WORK)
                    {
                        ///
                        if(Serv_VideoPri == stUCC.tCallInfoS.enRcrdServType && ptMediaFd->tcpFdIdx == -1) {
                            // update db
                            (void)rceAddVideoRecord2DB(ptMediaFd);
                            (void)rceUptUserVideoEndTime2DB(ptMediaFd);
                        } else {
                            // update db
                            (void)rceAddVoiceRecord2DB(&ptGlbData->stUdpAddrInfo.udpRtpFdList[stUCC.usRtpFdIdx]);

                            /* 关闭文件说明是写完一个文件, 需要把record的当前结束时间写道h_user_voice中 */
                            (void)rceUptUserVoiceEndTime2DB(&ptGlbData->stUdpAddrInfo.udpRtpFdList[stUCC.usRtpFdIdx]);
                        }
                    }
                }
                else if(MEDIA_VIDEO == stUCC.enMediaType && stUCC.usTcpRtpFdIdx < g_tServerCfg.uConcurrCallMaxNum)
                {
                    MediaFdS *ptMediaFd = &ptGlbData->stTcpAddrInfo.tcpRtpFdList[stUCCTmp.usTcpRtpFdIdx];
                    if(ptMediaFd->iRceRecordState == Record_WORK)
                    {
                        ULONGLONG dur = MP4GetDuration(ptMediaFd->pvMp4HFile);
                        //printf("-------~~~~~~~dur: %lu\n", dur);
                        // update db
                        (void)rceAddVideoRecord2DB(ptMediaFd);

                        /* 关闭文件说明是写完一个文件, 需要把record的当前结束时间写道h_user_video中 */
                        //(void)rceUptUserVideoEndTime2DB(&ptGlbData->stTcpAddrInfo.tcpRtpFdList[stUCCTmp.usTcpRtpFdIdx]);
                        (void)rceUptUserVideoEndTime2DB_2(&ptGlbData->stTcpAddrInfo.tcpRtpFdList[stUCCTmp.usTcpRtpFdIdx], dur/1000);
                    }
                }
            }
            
            ulReturn = XRceUCallCtxOpr(XDB_OPR_DEL, NULL, 0, (MediaTypeE)0, NULL, &ulUCallCtxId, &stUCC);

            if (stUCCTmp.usRtpFdIdx < g_tServerCfg.uConcurrCallMaxNum || stUCCTmp.usTcpRtpFdIdx < g_tServerCfg.uConcurrCallMaxNum)
            {
                ulReturn = rceCloseMediaSocket(&stUCCTmp);
                if (XPOC_ERR_SUCCESS != ulReturn)
                {
                    x_ErrorLog("Close RTP socket failed. UCCId=%d, SockfdIdx=%d.\n", ulUCallCtxId, stUCCTmp.usRtpFdIdx);
                }
            }

        }

        (void)XPOCFreeCtrlBlock(ptCB->ulCBId, ulReturn);
    } while (0);

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

    XPOC_TRACEFUNC_OUT;
    return ulReturn; 
}

rceMediaFunc.c

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

#include "rceResourceFunc.h"
#include "rceMediaFunc.h"
#include "rceCommFunc.h"
#include "rceProc.h"

extern T_ServerCfg g_tServerCfg;

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

#define PCM_SAMPLE_LEN  500

#define AMR_NB_HEADER_LEN   6
#define AMR_WB_HEADER_LEN   9
char audio_record_type = AUDIOTYPE_AMR_NB;

#if 0
#endif

//
static time_t GetCurrMsTime(void)
{
    time_t uMillSec = 0;
    struct timeval tv;
    gettimeofday(&tv, NULL);
    uMillSec = (time_t)tv.tv_sec * 1000;
    uMillSec += tv.tv_usec / 1000;
    return uMillSec;
}


#if 0
#endif


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

UINT rceInitDpThread(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, rceDpRecvUdpPacket, NULL);
    if(0 != ulReturn)
    {
        x_ErrorLog("[X][UDP] main recv thread create failed. mainThreadId[%lu], result[%lu].\n", threadId, ulReturn);
        return ulReturn;
    }
    x_InfoLog("[X][UDP] main recv thread create OK. mainThreadId[%lu].\n", threadId);

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


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

    UCHAR i = 0;
    for (i = 0; i < ptGlbData->stCfgInfo.rtpWorkerThreadNum; i++)
    {
        ptBuff = (PktQueue_S*)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);
        }

        /******************************************************************************/
        /* 初始化队列索引管理上下文 */
        rceRmIdxManageInitCtx(&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, rceDpHandlePktFromQueue, (void*)&g_threadDpSendRtp[i]);
        if(0 != ulReturn)
        {
            Fplat_Free(ptBuff);
            x_ErrorLog("[X]RTP worker thread create failed. thread[%d:%lu], result[%d].\n", 
                    i, g_threadDpSendRtp[i].ThreadId, ulReturn);
            return XPOC_ERR_FAIL;
        }
        else
        {
            x_InfoLog("[X]RTP worker 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;
}



UINT generateRecordFileName(MediaFdS *ptMediaFd, CallInfoS *ptCallInfo, char *ptCreatTime)
{
    XPOC_TRACEFUNC_IN;
    X_NULL_POINTER_CHK_THREE_PARA(ptMediaFd, ptCallInfo, ptCreatTime);

    if (MEDIA_VIDEO == ptMediaFd->iRceMediaType)
    {
        if (Serv_VideoPri == ptCallInfo->enRcrdServType)
        {
            snprintf(ptMediaFd->szRecordFileName, STR_LEN512, "VideoPri-MO-%s-MT-%s-Cur-%s-%s.mp4",
                    ptCallInfo->aucRcrdNum, ptCallInfo->aucRelatNum, ptCallInfo->aucSpkNum, ptCreatTime);
        }
        else if (Serv_VideoPullPri == ptCallInfo->enRcrdServType)
        {
            snprintf(ptMediaFd->szRecordFileName, STR_LEN512, "VideoPullPri-MO-%s-MT-%s-Cur-%s-%s.mp4",
                    ptCallInfo->aucRelatNum, ptCallInfo->aucRcrdNum, ptCallInfo->aucSpkNum, ptCreatTime);
        }
        else if (Serv_VideoPushPri == ptCallInfo->enRcrdServType)
        {
            snprintf(ptMediaFd->szRecordFileName, STR_LEN512, "VideoPushPri-MO-%s-MT-%s-Cur-%s-%s.mp4",
                    ptCallInfo->aucRcrdNum, ptCallInfo->aucRelatNum, ptCallInfo->aucSpkNum, ptCreatTime);
        }
        else if (Serv_VideoTransPri == ptCallInfo->enRcrdServType)
        {
            snprintf(ptMediaFd->szRecordFileName, STR_LEN512, "VideoTransPri-MO-%s-MT-%s-Cur-%s-%s.mp4",
                    ptCallInfo->aucRcrdNum, ptCallInfo->aucRelatNum, ptCallInfo->aucSpkNum, ptCreatTime);
        }
        else if (Serv_VideoPushGrp == ptCallInfo->enRcrdServType)
        {
            if(0==strlen(ptMediaFd->aucGroupSpkNum))/* 首次默认话权时会没有取到groupSpkNum */
            {
                strcpy(ptMediaFd->aucGroupSpkNum, ptCallInfo->aucOrigNum);
            }
            snprintf(ptMediaFd->szRecordFileName, STR_LEN512, "VideoPushGrp-MO-%s-Group-%s-Cur-%s-%s.mp4",
                    ptCallInfo->aucOrigNum, ptCallInfo->aucRcrdGrpNum, ptMediaFd->aucGroupSpkNum, ptCreatTime);
        }
        else if (Serv_VideoTransGrp == ptCallInfo->enRcrdServType)
        {
            if(0==strlen(ptMediaFd->aucGroupSpkNum))/* 首次默认话权时会没有取到groupSpkNum */
            {
                strcpy(ptMediaFd->aucGroupSpkNum, ptCallInfo->aucOrigNum);
            }
            snprintf(ptMediaFd->szRecordFileName, STR_LEN512, "VideoTransGrp-MO-%s-Group-%s-Cur-%s-%s.mp4",
                    ptCallInfo->aucOrigNum, ptCallInfo->aucRcrdGrpNum, ptMediaFd->aucGroupSpkNum, ptCreatTime);
        }
        else
        {
            x_ErrorLog("ptCallInfo->enRcrdServType[%d] is error for video.\n", ptCallInfo->enRcrdServType);
            return XPOC_ERR_FAIL;
        }
    }
    if (MEDIA_AUDIO == ptMediaFd->iRceMediaType)
    {
		if (AUDIOTYPE_AMR_NB == audio_record_type || AUDIOTYPE_AMR_WB == audio_record_type)
		{
            if (Serv_AudioPri == ptCallInfo->enRcrdServType)
            {
				/* snprintf(ptMediaFd->szRecordFileName, STR_LEN512, "AudioPri-MO-%s-MT-%s-Cur-%s-%s.amr",
                        ptCallInfo->aucRcrdNum, ptCallInfo->aucRelatNum, ptCallInfo->aucSpkNum, ptCreatTime);*/
                if (0 == strcmp(ptCallInfo->aucOrigNum, ptCallInfo->aucRcrdNum))
                {
				    snprintf(ptMediaFd->szRecordFileName, STR_LEN512, "AudioPri-MO-%s-MT-%s-Cur-%s-%s.amr",
                            ptCallInfo->aucOrigNum, ptCallInfo->aucRelatNum, ptCallInfo->aucSpkNum, ptCreatTime);
                }
                else
                {
                    snprintf(ptMediaFd->szRecordFileName, STR_LEN512, "AudioPri-MO-%s-MT-%s-Cur-%s-%s.amr",
                            ptCallInfo->aucOrigNum, ptCallInfo->aucRcrdNum, ptCallInfo->aucSpkNum, ptCreatTime);
                } 
            }
            else if (Serv_AudioHalfPri == ptCallInfo->enRcrdServType)
            {
                // modified by shm, 解决被叫录音文件名不正确问题*/
                //char acCalleeNum[STR_LEN32] = {0};
                if (0 == strcmp(ptCallInfo->aucOrigNum, ptCallInfo->aucRcrdNum))
                {
				    snprintf(ptMediaFd->szRecordFileName, STR_LEN512, "AudioHalfPri-MO-%s-MT-%s-Cur-%s-%s.amr",
                            ptCallInfo->aucOrigNum, ptCallInfo->aucRelatNum, ptCallInfo->aucSpkNum, ptCreatTime);
                }
                else
                {
                    snprintf(ptMediaFd->szRecordFileName, STR_LEN512, "AudioHalfPri-MO-%s-MT-%s-Cur-%s-%s.amr",
                            ptCallInfo->aucOrigNum, ptCallInfo->aucRcrdNum, ptCallInfo->aucSpkNum, ptCreatTime);
                }   
            
            }
            else if (Serv_AudioGroup == ptCallInfo->enRcrdServType)
            {
                if(0 == strlen(ptMediaFd->aucGroupSpkNum))/* 首次默认话权时会没有取到groupSpkNum */
                {
                    strcpy(ptMediaFd->aucGroupSpkNum, ptCallInfo->aucOrigNum);
                }
				snprintf(ptMediaFd->szRecordFileName, STR_LEN512, "AudioGroup-MO-%s-Group-%s-Cur-%s-%s.amr",
                        ptCallInfo->aucOrigNum, ptCallInfo->aucRcrdGrpNum, ptMediaFd->aucGroupSpkNum, ptCreatTime);
            }
		    else if (Serv_VideoPri == ptCallInfo->enRcrdServType)
            {
                snprintf(ptMediaFd->szRecordFileName, STR_LEN512, "VideoPri-MO-%s-MT-%s-Cur-%s-%s.amr",
                        ptCallInfo->aucRcrdNum, ptCallInfo->aucRelatNum, ptCallInfo->aucSpkNum, ptCreatTime);
            }
            else
            {
                x_ErrorLog("ptCallInfo->enRcrdServType[%d] is error for audio.\n", ptCallInfo->enRcrdServType);
                return XPOC_ERR_FAIL;
            }
        }
        else if( AUDIOTYPE_G711 == audio_record_type)
		{
			if (Serv_AudioPri == ptCallInfo->enRcrdServType)
			{
				snprintf(ptMediaFd->szRecordFileName, STR_LEN512, "AudioPri-MO-%s-MT-%s-Cur-%s-%s.wav",
					    ptCallInfo->aucRcrdNum, ptCallInfo->aucRelatNum, ptCallInfo->aucSpkNum, ptCreatTime);
			}
			else if (Serv_AudioHalfPri == ptCallInfo->enRcrdServType)
			{
				snprintf(ptMediaFd->szRecordFileName, STR_LEN512, "AudioHalfPri-MO-%s-MT-%s-Cur-%s-%s.wav",
					    ptCallInfo->aucRcrdNum, ptCallInfo->aucRelatNum, ptCallInfo->aucSpkNum, ptCreatTime);
			}
			else if (Serv_AudioGroup == ptCallInfo->enRcrdServType)
			{
				if (0 == strlen(ptMediaFd->aucGroupSpkNum))/* 首次默认话权时会没有取到groupSpkNum */
                {
					strcpy(ptMediaFd->aucGroupSpkNum, ptCallInfo->aucOrigNum);
                }
				snprintf(ptMediaFd->szRecordFileName, STR_LEN512, "AudioGroup-MO-%s-Group-%s-Cur-%s-%s.wav",
					    ptCallInfo->aucOrigNum, ptCallInfo->aucRcrdGrpNum, ptMediaFd->aucGroupSpkNum, ptCreatTime);
			}
            else if (Serv_VideoPri == ptCallInfo->enRcrdServType)
            {
                snprintf(ptMediaFd->szRecordFileName, STR_LEN512, "VideoPri-MO-%s-MT-%s-Cur-%s-%s.wav",
                        ptCallInfo->aucRcrdNum, ptCallInfo->aucRelatNum, ptCallInfo->aucSpkNum, ptCreatTime);
            }
			else
			{
				x_ErrorLog("ptCallInfo->enRcrdServType[%d] is error for audio.\n", ptCallInfo->enRcrdServType);
				return XPOC_ERR_FAIL;
			}
		}
    }

    x_InfoLog("generate RecordFileName[%s],servType[%d],mediaType[%d], spk num [%s], grp spk num [%s].", 
              ptMediaFd->szRecordFileName, ptCallInfo->enRcrdServType, ptMediaFd->iRceMediaType
              , ptCallInfo->aucSpkNum, ptMediaFd->aucGroupSpkNum);
    XPOC_TRACEFUNC_OUT;
    return XPOC_ERR_SUCCESS;
}


UINT AddWaveFileHeader(int fd_record, int totalAudioLen)
{
    unsigned char header[56] = {0x52,0x49,0x46,0x46,0x00,0x00,0x00,0x00,
                                0x57,0x41,0x56,0x45,0x66,0x6D,0x74,0x20,
                                0x10,0x00,0x00,0x00,0x01,0x00,0x01,0x00,
                                0x40,0x1F,0x00,0x00,0x80,0x3E,0x00,0x00,
                                0x02,0x00,0x10,0x00,0x66,0x61,0x63,0x74,
                                0x04,0x00,0x00,0x00,0x00,0xBE,0x00,0x00,
                                0x64,0x61,0x74,0x61,0x00,0x00,0x00,0x00 };

    int totalDataLen = totalAudioLen + 48;
    header[4] = (unsigned char)(totalDataLen & 0xff);
    header[5] = (unsigned char)((totalDataLen >> 8) & 0xff);
    header[6] = (unsigned char)((totalDataLen >> 16) & 0xff);
    header[7] = (unsigned char)((totalDataLen >> 24) & 0xff);

    header[52] = (unsigned char)(totalAudioLen & 0xff);
    header[53] = (unsigned char)((totalAudioLen >> 8) & 0xff);
    header[54] = (unsigned char)((totalAudioLen >> 16) & 0xff);
    header[55] = (unsigned char)((totalAudioLen >> 24) & 0xff);

    if (-1 != write(fd_record, header, 56))
    {
        return XPOC_ERR_SUCCESS;
    }
    else
    {
        return XPOC_ERR_FAIL;
    }
}

UINT AddAMRFileHeader(int fd_record, int totalAudioLen)
{
    (void)totalAudioLen;

	const char header[] = "#!AMR-WB\n";

	if (-1 != write(fd_record, header, AMR_WB_HEADER_LEN))
	{
		return XPOC_ERR_SUCCESS;
	}
	else
	{
		return XPOC_ERR_FAIL;
	}
}

// added by shm
UINT AddAMRNBFileHeader(int fd_record, int totalAudioLen)
{
    (void)totalAudioLen;

	const char header[] = "#!AMR\n";

	if (-1 != write(fd_record, header, AMR_NB_HEADER_LEN))
	{
		return XPOC_ERR_SUCCESS;
	}
	else
	{
		return XPOC_ERR_FAIL;
	}
}

UINT  UnpackRTPH264(unsigned char   *  bufIn, int  len, unsigned char **  pBufOut, int   *  pOutLen)
{
    *pOutLen = 0;
    if (len <= RTP_HEADER_LEN)
    {
        return   XPOC_ERR_FAIL;
    }

    const int hdrLen = (bufIn[0]&0x10)?RTP_HEADER_LEN+RTP_HEADER_EXT_LEN:RTP_HEADER_LEN;
    unsigned char *  src = bufIn + hdrLen;
    unsigned char  head1 = *src; // 获取第一个字节 
    unsigned char  head2 = *(src + 1); // 获取第二个字节 
    unsigned char  nal = head1 & 0x1f; // 获取FU indicator的类型域, 
    unsigned char  flag = head2 & 0xe0; // 获取FU header的前三位,判断当前是分包的开始、中间或结束 
    unsigned char  nal_fua = (head1 & 0xe0) | (head2 & 0x1f); // FU_A nal 
    UINT ulReturn = XPOC_ERR_FAIL;
    if (nal == 0x1c) // 判断NAL的类型为0x1c=28,说明是FU-A分片 
    { // fu-a 
        if (flag == 0x80) // 开始 
        {
            *pBufOut = src - 3;
            *((int *)(*pBufOut)) = 0x01000000; // zyf:大模式会有问题 
            *((char *)(*pBufOut) + 4) = nal_fua;
            *pOutLen = len - hdrLen + 3;
        }
        else   if (flag == 0x40) // 结束 
        {
            *pBufOut = src + 2;
            *pOutLen = len - hdrLen - 2;
        }
        else // 中间 
        {
            *pBufOut = src + 2;
            *pOutLen = len - hdrLen - 2;
        }
        ulReturn = XPOC_ERR_SUCCESS;
     
    }
    else // 单包数据 
    {
        *pBufOut = src - 4;
        *((int *)(*pBufOut)) = 0x01000000; // zyf:大模式会有问题 
        *pOutLen = len - hdrLen + 4;
        ulReturn = XPOC_ERR_SUCCESS;
    }
    // 本来应该在这的,因为我们现在是RTP头固定的,且是单包数据
    //     unsigned  char *  bufTmp = (unsigned  char *)bufIn;
    //     if (bufTmp[1] & 0x80)
    //     {
    //         bFinishFrame = true; // rtp mark 
    //     }
    //     else
    //     {
    //         bFinishFrame = false;
    //     }
    return  ulReturn;
}


short decode(unsigned char alaw)
{
    alaw ^= 0xD5;
    int sign = alaw & 0x80;
    int exponent = (alaw & 0x70) >> 4;
    int data = alaw & 0x0f;
    data <<= 4;
    data += 8;
    if (exponent != 0)
        data += 0x100;
    if (exponent > 1)
        data <<= (exponent - 1);

    return (short)(sign == 0 ? data : -data);
}
/** 个人理解
* bitsize 应该为16, pBuffer(pcm数据) 两个char 合成一个 short ,长度自然就是原来的一半(nBufferSize/2),
* 通过编码后short类型的数据变为char类型,让后复制给pCodecBits
*/
int g711_decode(char* pRawData, const unsigned char* pBuffer, int nBufferSize)
{
    short *out_data = (short*)pRawData;
    int i;
    for (i = 0; i < nBufferSize; i++)
    {
        out_data[i] = decode(pBuffer[i]);
    }

    //     if (g_AudioConfig.g_PCM_SoundRaiseEnabled)
    //     {
    //         RaiseVolume(pRawData, nBufferSize * 2, 1, g_AudioConfig.g_PCM_SoundRaiseVolume);
    //     }
    return nBufferSize * 2;
}


#if 0
#endif

UINT rceAddVideoCallInfo2DB(UINT ulUCCId)
{
    XPOC_TRACEFUNC_IN;

    UINT ulReturn = XPOC_ERR_SUCCESS;
    T_RceUCallCtxTable stUCC;
    memset(&stUCC, 0, sizeof(T_RceUCallCtxTable));
    ulReturn = XRceUCallCtxOpr(XDB_OPR_QRY, NULL, 0, (MediaTypeE)0, NULL, &ulUCCId, &stUCC);
    if (XPOC_ERR_SUCCESS != ulReturn)
    {
        return XPOC_ERR_FAIL;
    }

    H_User_Video_S stUserVideo;
    memset(&stUserVideo, 0, sizeof(H_User_Video_S));
    strcpy(stUserVideo.call_id, (Serv_VideoPri==stUCC.tCallInfoS.enRcrdServType)?stUCC.aucCallId:stUCC.tCallInfoS.aucCceSipCallId); 
    ulReturn = MYSQL_UserVideo_Qry_Id_By_SipCallId(mysql_connect, &stUserVideo);
    if(XPOC_ERR_SUCCESS == ulReturn)
    {
        return ulReturn;/* 已经存在了则不需要再ADD */
    }

    memcpy(stUserVideo.company_no, stUCC.tCallInfoS.aucSpkNum, 8);

    x_InfoLog("aucSpkNum[%s],aucCompanyNo[%s].", stUCC.tCallInfoS.aucSpkNum, stUserVideo.company_no);

    char biz_type[STR_LEN16]; 
    memset(biz_type, 0, STR_LEN16);
    ulReturn = rceGetBizTypeByServType(stUCC.tCallInfoS.enRcrdServType, biz_type);
    if(XPOC_ERR_SUCCESS != ulReturn)
    {
        return ulReturn;
    }
    strcpy(stUserVideo.biz_type, biz_type);

    if(Serv_AudioRcrdPri == stUCC.tCallInfoS.enServType
        || Serv_VideoRcrdPri == stUCC.tCallInfoS.enServType)
    {
        if (Serv_VideoPullPri == stUCC.tCallInfoS.enRcrdServType)    // added by shm at 20210204, 视频上拉,主叫为aucRelatNum,被叫为aucRcrdNum
        {
            strncpy(stUserVideo.from_user_no, stUCC.tCallInfoS.aucRelatNum, STR_LEN32);
            strncpy(stUserVideo.to_user_no, stUCC.tCallInfoS.aucRcrdNum, STR_LEN32);
        }
        else
        {
            strncpy(stUserVideo.from_user_no, stUCC.tCallInfoS.aucRcrdNum, STR_LEN32);
            strncpy(stUserVideo.to_user_no, stUCC.tCallInfoS.aucRelatNum, STR_LEN32);
        }
    }
    else if(Serv_AudioRcrdGrp == stUCC.tCallInfoS.enServType
        || Serv_VideoRcrdGrp == stUCC.tCallInfoS.enServType)
    {
        strncpy(stUserVideo.from_user_no, stUCC.tCallInfoS.aucOrigNum, STR_LEN32);
        strncpy(stUserVideo.to_user_no, stUCC.tCallInfoS.aucRcrdGrpNum, STR_LEN32);
    }

    ///
    //int now_ms = 0;
    struct timeval tv;
    //struct tm now_tm;
    time_t now_sec;
    gettimeofday(&tv, NULL);
    now_sec = tv.tv_sec;
    //now_ms = tv.tv_usec/1000;
    struct tm *tmInfo = localtime(&now_sec);
    strftime(stUserVideo.start_time, 20, "%Y-%m-%d %H:%M:%S", tmInfo);
    strftime(stUserVideo.end_time, 20, "%Y-%m-%d %H:%M:%S", tmInfo);

    ulReturn = MYSQL_UserVideo_Add(mysql_connect, &stUserVideo);
    if(XPOC_ERR_SUCCESS != ulReturn)
    {
        return ulReturn;
    }

    XPOC_TRACEFUNC_OUT;
    return XPOC_ERR_SUCCESS;
}

UINT rceUptUserVideoStartTime2DB(MediaFdS *ptMediaFd)
{
    XPOC_TRACEFUNC_IN;
    X_NULL_POINTER_CHK_ONE_PARA(ptMediaFd);

    UINT ulReturn = XPOC_ERR_SUCCESS;

    T_RceUCallCtxTable stUCC;
    memset(&stUCC, 0, sizeof(T_RceUCallCtxTable));
    ulReturn = XRceUCallCtxOpr(XDB_OPR_QRY, NULL, 0, (MediaTypeE)0, NULL, &ptMediaFd->ulUCCId, &stUCC);
    if (XPOC_ERR_SUCCESS != ulReturn)
    {
        x_ErrorLog("ucc data null.\n");
        return XPOC_ERR_FAIL;
    }

    H_User_Video_S stUserVideo;
    memset(&stUserVideo, 0, sizeof(H_User_Video_S));
    strcpy(stUserVideo.call_id, (Serv_VideoPri==stUCC.tCallInfoS.enRcrdServType)?stUCC.aucCallId:stUCC.tCallInfoS.aucCceSipCallId);

    ///
    int now_ms = 0;
    struct timeval tv;
    //struct tm now_tm;
    time_t now_sec;
    gettimeofday(&tv, NULL);
    now_sec = tv.tv_sec;
    now_ms = tv.tv_usec/1000;
    if(now_ms>500) now_sec += 1;
    struct tm *tmInfo = localtime(&now_sec);
    strftime(stUserVideo.start_time, 20, "%Y-%m-%d %H:%M:%S", tmInfo);

    ulReturn = MYSQL_UserVideo_Upt_StartTime(mysql_connect, &stUserVideo);
    if(XPOC_ERR_SUCCESS != ulReturn)
    {
        return ulReturn;
    }

    XPOC_TRACEFUNC_OUT;
    return XPOC_ERR_SUCCESS;
}

UINT rceUptUserVideoEndTime2DB(MediaFdS *ptMediaFd)
{
    XPOC_TRACEFUNC_IN;
    X_NULL_POINTER_CHK_ONE_PARA(ptMediaFd);

    UINT ulReturn = XPOC_ERR_SUCCESS;

    T_RceUCallCtxTable stUCC;
    memset(&stUCC, 0, sizeof(T_RceUCallCtxTable));
    ulReturn = XRceUCallCtxOpr(XDB_OPR_QRY, NULL, 0, (MediaTypeE)0, NULL, &ptMediaFd->ulUCCId, &stUCC);
    if (XPOC_ERR_SUCCESS != ulReturn)
    {
        x_ErrorLog("ucc data null.\n");
        return XPOC_ERR_FAIL;
    }

    H_User_Video_S stUserVideo;
    memset(&stUserVideo, 0, sizeof(H_User_Video_S));
    strcpy(stUserVideo.call_id, (Serv_VideoPri==stUCC.tCallInfoS.enRcrdServType)?stUCC.aucCallId:stUCC.tCallInfoS.aucCceSipCallId);

    ///
    //int now_ms = 0;
    struct timeval tv;
    //struct tm now_tm;
    time_t now_sec;
    gettimeofday(&tv, NULL);
    now_sec = tv.tv_sec;
    //now_ms = tv.tv_usec/1000;
    struct tm *tmInfo = localtime(&now_sec);
    strftime(stUserVideo.end_time, 20, "%Y-%m-%d %H:%M:%S", tmInfo);

    ulReturn = MYSQL_UserVideo_Upt_EndTime(mysql_connect, &stUserVideo);
    if(XPOC_ERR_SUCCESS != ulReturn)
    {
        return ulReturn;
    }

    XPOC_TRACEFUNC_OUT;
    return XPOC_ERR_SUCCESS;
}

UINT rceUptUserVideoStartTime2DB_2(MediaFdS *ptMediaFd, UINT dur)
{
    XPOC_TRACEFUNC_IN;
    X_NULL_POINTER_CHK_ONE_PARA(ptMediaFd);

    UINT ulReturn = XPOC_ERR_SUCCESS;

    T_RceUCallCtxTable stUCC;
    memset(&stUCC, 0, sizeof(T_RceUCallCtxTable));
    ulReturn = XRceUCallCtxOpr(XDB_OPR_QRY, NULL, 0, (MediaTypeE)0, NULL, &ptMediaFd->ulUCCId, &stUCC);
    if (XPOC_ERR_SUCCESS != ulReturn)
    {
        x_ErrorLog("ucc data null.\n");
        return XPOC_ERR_FAIL;
    }

    H_User_Video_S stUserVideo;
    memset(&stUserVideo, 0, sizeof(H_User_Video_S));
    strcpy(stUserVideo.call_id, (Serv_VideoPri==stUCC.tCallInfoS.enRcrdServType)?stUCC.aucCallId:stUCC.tCallInfoS.aucCceSipCallId);

    ///
    time_t now_sec;
    now_sec = ptMediaFd->tmRecordStartTime+dur;
    struct tm *tmInfo = localtime(&now_sec);
    strftime(stUserVideo.start_time, 20, "%Y-%m-%d %H:%M:%S", tmInfo);

    ulReturn = MYSQL_UserVideo_Upt_StartTime(mysql_connect, &stUserVideo);
    if(XPOC_ERR_SUCCESS != ulReturn)
    {
        return ulReturn;
    }

    XPOC_TRACEFUNC_OUT;
    return XPOC_ERR_SUCCESS;
}
UINT rceUptUserVideoEndTime2DB_2(MediaFdS *ptMediaFd, UINT dur)
{
    XPOC_TRACEFUNC_IN;
    X_NULL_POINTER_CHK_ONE_PARA(ptMediaFd);

    UINT ulReturn = XPOC_ERR_SUCCESS;

    T_RceUCallCtxTable stUCC;
    memset(&stUCC, 0, sizeof(T_RceUCallCtxTable));
    ulReturn = XRceUCallCtxOpr(XDB_OPR_QRY, NULL, 0, (MediaTypeE)0, NULL, &ptMediaFd->ulUCCId, &stUCC);
    if (XPOC_ERR_SUCCESS != ulReturn)
    {
        x_ErrorLog("ucc data null.\n");
        return XPOC_ERR_FAIL;
    }

    H_User_Video_S stUserVideo;
    memset(&stUserVideo, 0, sizeof(H_User_Video_S));
    strcpy(stUserVideo.call_id, (Serv_VideoPri==stUCC.tCallInfoS.enRcrdServType)?stUCC.aucCallId:stUCC.tCallInfoS.aucCceSipCallId);

    ///
    struct tm tmInfo;
    time_t now_sec;
    now_sec = ptMediaFd->tmRecordStartTime+dur;
    char createTime[32] = {0};
    localtime_r(&ptMediaFd->tmRecordStartTime, &tmInfo);
    strftime(createTime, STR_LEN32, "%Y-%m-%d-%H-%M-%S", &tmInfo);

    localtime_r(&now_sec, &tmInfo);
    strftime(stUserVideo.end_time, 20, "%Y-%m-%d %H:%M:%S", &tmInfo);
    //printf("--------%s---------stUserVideo.end_time: %s\n", createTime, stUserVideo.end_time);
    ulReturn = MYSQL_UserVideo_Upt_EndTime(mysql_connect, &stUserVideo);
    if(XPOC_ERR_SUCCESS != ulReturn)
    {
        return ulReturn;
    }

    XPOC_TRACEFUNC_OUT;
    return XPOC_ERR_SUCCESS;
}

UINT rceGetUserVideoIdBySipCallId(T_RceUCallCtxTable *ptUCC, long long *ptUserVideoId)
{
    XPOC_TRACEFUNC_IN;
    X_NULL_POINTER_CHK_TWO_PARA(ptUCC, ptUserVideoId);

    UINT ulReturn = XPOC_ERR_SUCCESS;

    H_User_Video_S stUserVideo;
    memset(&stUserVideo, 0, sizeof(H_User_Video_S));

    strcpy(stUserVideo.call_id, (Serv_VideoPri==ptUCC->tCallInfoS.enRcrdServType)?ptUCC->aucCallId:ptUCC->tCallInfoS.aucCceSipCallId); 

    ulReturn = MYSQL_UserVideo_Qry_Id_By_SipCallId(mysql_connect, &stUserVideo);
    if(XPOC_ERR_SUCCESS != ulReturn)
    {
        return ulReturn;
    }
    *ptUserVideoId = stUserVideo.id;

    XPOC_TRACEFUNC_OUT;
    return XPOC_ERR_SUCCESS;
}

UINT rceAddVideoRecord2DB(MediaFdS *ptMediaFd)
{
    XPOC_TRACEFUNC_IN;
    X_NULL_POINTER_CHK_ONE_PARA(ptMediaFd);

    UINT ulReturn = XPOC_ERR_SUCCESS;
    T_RceUCallCtxTable stUCC;
    memset(&stUCC, 0, sizeof(T_RceUCallCtxTable));
    ulReturn = XRceUCallCtxOpr(XDB_OPR_QRY, NULL, 0, (MediaTypeE)0, NULL, &ptMediaFd->ulUCCId, &stUCC);
    if (XPOC_ERR_SUCCESS != ulReturn)
    {
        x_ErrorLog("ucc data null.\n");
        return XPOC_ERR_FAIL;
    }

    char szFromUrl[STR_LEN128];
    char szToUrl[STR_LEN128];
    memset(szFromUrl, 0, STR_LEN128);
    memset(szToUrl, 0, STR_LEN128);

    if (Serv_VideoPri == stUCC.tCallInfoS.enRcrdServType
        || Serv_VideoPullPri == stUCC.tCallInfoS.enRcrdServType
        || Serv_VideoPushPri == stUCC.tCallInfoS.enRcrdServType
        || Serv_VideoTransPri == stUCC.tCallInfoS.enRcrdServType)
    {
        if (strcmp(stUCC.tCallInfoS.aucSpkNum, stUCC.tCallInfoS.aucRcrdNum) == 0)
        {
            memcpy(szFromUrl, ptMediaFd->szRecordFileName, MIN(strlen(ptMediaFd->szRecordFileName), STR_LEN128));
        }
        else if (strcmp(stUCC.tCallInfoS.aucSpkNum, stUCC.tCallInfoS.aucRelatNum) == 0)
        {
            memcpy(szToUrl, ptMediaFd->szRecordFileName, MIN(strlen(ptMediaFd->szRecordFileName), STR_LEN128));
        }
        else
        {
            x_ErrorLog("Error Serv_AudioPri speak is not  FAILED!!");
            return XPOC_ERR_FAIL;
        }
    }
    else if (Serv_VideoPushGrp == stUCC.tCallInfoS.enRcrdServType
        || Serv_VideoTransGrp == stUCC.tCallInfoS.enRcrdServType)
    {
        if (strcmp(stUCC.tCallInfoS.aucSpkNum, stUCC.tCallInfoS.aucOrigNum) == 0)
        {
            memcpy(szFromUrl, ptMediaFd->szRecordFileName, MIN(strlen(ptMediaFd->szRecordFileName), STR_LEN128));
        }
        else
        {
            x_ErrorLog("Error Serv_AudioGroup speak is not  FAILED!!");
            return XPOC_ERR_FAIL;
        }
    }
    else
    {
        x_ErrorLog("ptUCC->tCallInfoS.enRcrdServType[%d] is error for video.\n", stUCC.tCallInfoS.enRcrdServType);
        return XPOC_ERR_FAIL;
    }
    x_InfoLog("RcrdServType[%d],fromurl[%s],tourl[%s].", stUCC.tCallInfoS.enRcrdServType, szFromUrl, szToUrl);

    H_User_Video_Item_S stUserVideoItem;
    memset(&stUserVideoItem, 0, sizeof(H_User_Video_Item_S));
    memcpy(stUserVideoItem.company_no, stUCC.tCallInfoS.aucSpkNum, 8);

    /* get h_user_Video.id */
    ulReturn = rceGetUserVideoIdBySipCallId(&stUCC, &stUserVideoItem.user_video_no);
    if(XPOC_ERR_SUCCESS != ulReturn)
    {
        return ulReturn;
    }

    strncpy(stUserVideoItem.user_no, stUCC.tCallInfoS.aucSpkNum, STR_LEN32);
    strcpy(stUserVideoItem.call_id, (Serv_VideoPri==stUCC.tCallInfoS.enRcrdServType)?stUCC.aucCallId:stUCC.tCallInfoS.aucCceSipCallId);
    strcpy(stUserVideoItem.start_time, ptMediaFd->aucRecordStartTime);

    ///
    //int now_ms = 0;
    struct timeval tv;
    //struct tm now_tm;
    time_t now_sec;
    gettimeofday(&tv, NULL);
    now_sec = tv.tv_sec;
    //now_ms = tv.tv_usec/1000;
    struct tm *tmInfo = localtime(&now_sec);
    strftime(stUserVideoItem.end_time, 20, "%Y-%m-%d %H:%M:%S", tmInfo);
    strcpy(stUserVideoItem.from_video_url, szFromUrl);
    strcpy(stUserVideoItem.to_video_url, szToUrl);
    strcpy(stUserVideoItem.floor_url, "");
    strcpy(stUserVideoItem.record_time, stUserVideoItem.start_time);
    ULONGLONG dur = MP4GetDuration(ptMediaFd->pvMp4HFile) / 1000; // 录像时长,单位秒
    UINT hour = dur / 3600;
    UINT min = (dur - hour * 3600) / 60;
    UINT sec = dur - hour * 3600 - min * 60;
    char tmp[STR_LEN32] = {0};
    sprintf(tmp, "%02d:%02d:%02d", hour, min, sec);
    strcpy(stUserVideoItem.duration_time, tmp);

    ulReturn = MYSQL_UserVideoItem_Add(mysql_connect, &stUserVideoItem);
    if(XPOC_ERR_SUCCESS != ulReturn)
    {
        return ulReturn;
    }

    XPOC_TRACEFUNC_OUT;
    return XPOC_ERR_SUCCESS;
}


UINT rceAddVoiceCallInfo2DB(UINT ulUCCId)
{
    XPOC_TRACEFUNC_IN;

    UINT ulReturn = XPOC_ERR_SUCCESS;
    T_RceUCallCtxTable stUCC;
    memset(&stUCC, 0, sizeof(T_RceUCallCtxTable));
    ulReturn = XRceUCallCtxOpr(XDB_OPR_QRY, NULL, 0, (MediaTypeE)0, NULL, &ulUCCId, &stUCC);
    if (XPOC_ERR_SUCCESS != ulReturn)
    {
        return XPOC_ERR_FAIL;
    }

    H_User_Voice_S stUserVoice;
    memset(&stUserVoice, 0, sizeof(H_User_Voice_S));
    strcpy(stUserVoice.call_id, stUCC.tCallInfoS.aucCceSipCallId); 
    ulReturn = MYSQL_UserVoice_Qry_Id_By_SipCallId(mysql_connect, &stUserVoice);
    if(XPOC_ERR_SUCCESS == ulReturn)
    {
        return ulReturn;/* 已经存在了则不需要再ADD */
    }

    memcpy(stUserVoice.company_no, stUCC.tCallInfoS.aucSpkNum, 8);

    x_DebugLog("aucSpkNum[%s],aucCompanyNo[%s].", stUCC.tCallInfoS.aucSpkNum, stUserVoice.company_no);

    char biz_type[STR_LEN16]; 
    memset(biz_type, 0, STR_LEN16);
    ulReturn = rceGetBizTypeByServType(stUCC.tCallInfoS.enRcrdServType, biz_type);
    if(XPOC_ERR_SUCCESS != ulReturn)
    {
        return ulReturn;
    }
    strcpy(stUserVoice.biz_type, biz_type);

    if(Serv_AudioRcrdPri == stUCC.tCallInfoS.enServType
        || Serv_VideoRcrdPri == stUCC.tCallInfoS.enServType)
    {
        strncpy(stUserVoice.from_user_no, stUCC.tCallInfoS.aucRcrdNum, STR_LEN32);
        strncpy(stUserVoice.to_user_no, stUCC.tCallInfoS.aucRelatNum, STR_LEN32);
    }
    else if(Serv_AudioRcrdGrp == stUCC.tCallInfoS.enServType
        || Serv_VideoRcrdGrp == stUCC.tCallInfoS.enServType)
    {
        strncpy(stUserVoice.from_user_no, stUCC.tCallInfoS.aucOrigNum, STR_LEN32);
        strncpy(stUserVoice.to_user_no, stUCC.tCallInfoS.aucRcrdGrpNum, STR_LEN32);
    }

    ///
    //int now_ms = 0;
    struct timeval tv;
    //struct tm now_tm;
    time_t now_sec;
    gettimeofday(&tv, NULL);
    now_sec = tv.tv_sec;
    //now_ms = tv.tv_usec/1000;
    struct tm *tmInfo = localtime(&now_sec);
    strftime(stUserVoice.start_time, 20, "%Y-%m-%d %H:%M:%S", tmInfo);
    strftime(stUserVoice.end_time, 20, "%Y-%m-%d %H:%M:%S", tmInfo);

    ulReturn = MYSQL_UserVoice_Add(mysql_connect, &stUserVoice);
    if(XPOC_ERR_SUCCESS != ulReturn)
    {
        return ulReturn;
    }

    XPOC_TRACEFUNC_OUT;
    return XPOC_ERR_SUCCESS;
}

UINT rceUptUserVoiceEndTime2DB(MediaFdS *ptMediaFd)
{
    XPOC_TRACEFUNC_IN;
    X_NULL_POINTER_CHK_ONE_PARA(ptMediaFd);

    UINT ulReturn = XPOC_ERR_SUCCESS;

    T_RceUCallCtxTable stUCC;
    memset(&stUCC, 0, sizeof(T_RceUCallCtxTable));
    ulReturn = XRceUCallCtxOpr(XDB_OPR_QRY, NULL, 0, (MediaTypeE)0, NULL, &ptMediaFd->ulUCCId, &stUCC);
    if (XPOC_ERR_SUCCESS != ulReturn)
    {
        x_ErrorLog("ucc data null.\n");
        return XPOC_ERR_FAIL;
    }

    H_User_Voice_S stUserVoice;
    memset(&stUserVoice, 0, sizeof(H_User_Voice_S));
    strcpy(stUserVoice.call_id, stUCC.tCallInfoS.aucCceSipCallId);

    ///
    //int now_ms = 0;
    struct timeval tv;
    //struct tm now_tm;
    time_t now_sec;
    gettimeofday(&tv, NULL);
    now_sec = tv.tv_sec;
    //now_ms = tv.tv_usec/1000;
    struct tm *tmInfo = localtime(&now_sec);
    strftime(stUserVoice.end_time, 20, "%Y-%m-%d %H:%M:%S", tmInfo);

    ulReturn = MYSQL_UserVoice_Upt_EndTime(mysql_connect, &stUserVoice);
    if(XPOC_ERR_SUCCESS != ulReturn)
    {
        return ulReturn;
    }

    XPOC_TRACEFUNC_OUT;
    return XPOC_ERR_SUCCESS;
}

UINT rceGetUserVoiceIdBySipCallId(T_RceUCallCtxTable *ptUCC, long long *ptUserVoiceId)
{
    XPOC_TRACEFUNC_IN;
    X_NULL_POINTER_CHK_TWO_PARA(ptUCC, ptUserVoiceId);

    UINT ulReturn = XPOC_ERR_SUCCESS;

    H_User_Voice_S stUserVoice;
    memset(&stUserVoice, 0, sizeof(H_User_Voice_S));

    strcpy(stUserVoice.call_id, ptUCC->tCallInfoS.aucCceSipCallId); 

    ulReturn = MYSQL_UserVoice_Qry_Id_By_SipCallId(mysql_connect, &stUserVoice);
    if(XPOC_ERR_SUCCESS != ulReturn)
    {
        return ulReturn;
    }
    *ptUserVoiceId = stUserVoice.id;

    XPOC_TRACEFUNC_OUT;
    return XPOC_ERR_SUCCESS;
}

UINT rceAddVoiceRecord2DB(MediaFdS *ptMediaFd)
{
    XPOC_TRACEFUNC_IN;
    X_NULL_POINTER_CHK_ONE_PARA(ptMediaFd);

    UINT ulReturn = XPOC_ERR_SUCCESS;
    T_RceUCallCtxTable stUCC;
    memset(&stUCC, 0, sizeof(T_RceUCallCtxTable));
    ulReturn = XRceUCallCtxOpr(XDB_OPR_QRY, NULL, 0, (MediaTypeE)0, NULL, &ptMediaFd->ulUCCId, &stUCC);
    if (XPOC_ERR_SUCCESS != ulReturn)
    {
        x_ErrorLog("ucc data null.\n");
        return XPOC_ERR_FAIL;
    }

    char szFromUrl[STR_LEN128];
    char szToUrl[STR_LEN128];
    memset(szFromUrl, 0, STR_LEN128);
    memset(szToUrl, 0, STR_LEN128);

    if (Serv_AudioPri == stUCC.tCallInfoS.enRcrdServType
        || Serv_VideoPri == stUCC.tCallInfoS.enRcrdServType)
    {
        if (strcmp(stUCC.tCallInfoS.aucSpkNum, stUCC.tCallInfoS.aucRcrdNum) == 0)
        {
            memcpy(szFromUrl, ptMediaFd->szRecordFileName, MIN(strlen(ptMediaFd->szRecordFileName), STR_LEN128));
        }
        else if (strcmp(stUCC.tCallInfoS.aucSpkNum, stUCC.tCallInfoS.aucRelatNum) == 0)
        {
            memcpy(szToUrl, ptMediaFd->szRecordFileName, MIN(strlen(ptMediaFd->szRecordFileName), STR_LEN128));
        }
        else
        {
            x_ErrorLog("Error Serv_AudioPri speak is not  FAILED!!");
            return XPOC_ERR_FAIL;
        }
    }
    else if (Serv_AudioHalfPri == stUCC.tCallInfoS.enRcrdServType)
    {
        if (strcmp(stUCC.tCallInfoS.aucSpkNum, stUCC.tCallInfoS.aucRcrdNum) == 0)
        {
            memcpy(szFromUrl, ptMediaFd->szRecordFileName, MIN(strlen(ptMediaFd->szRecordFileName), STR_LEN128));
        }
        else if (strcmp(stUCC.tCallInfoS.aucSpkNum, stUCC.tCallInfoS.aucRelatNum) == 0)
        {
            memcpy(szToUrl, ptMediaFd->szRecordFileName, MIN(strlen(ptMediaFd->szRecordFileName), STR_LEN128));
        }
        else
        {
            x_ErrorLog("Error Serv_AudioPri speak is not  FAILED!!");
            return XPOC_ERR_FAIL;
        }       
    }
    else if (Serv_AudioGroup == stUCC.tCallInfoS.enRcrdServType)
    {
        memcpy(szFromUrl, ptMediaFd->szRecordFileName, MIN(strlen(ptMediaFd->szRecordFileName), STR_LEN128));
        /*if (strcmp(stUCC.tCallInfoS.aucSpkNum, stUCC.tCallInfoS.aucRcrdNum) == 0)
        {
            memcpy(szFromUrl, ptMediaFd->szRecordFileName, MIN(strlen(ptMediaFd->szRecordFileName), STR_LEN128));
        }
        else
        {
            x_ErrorLog("Error Serv_AudioGroup speak is not  FAILED!!");
            return XPOC_ERR_FAIL;
        }*/
    }
    else
    {
        x_ErrorLog("ptUCC->tCallInfoS.enRcrdServType[%d] is error for voice.\n", stUCC.tCallInfoS.enRcrdServType);
        return XPOC_ERR_FAIL;
    }
    x_InfoLog("UCCId[%d],aucSpkNum[%s],RcrdServType[%d],fromurl[%s],tourl[%s].", ptMediaFd->ulUCCId, 
                stUCC.tCallInfoS.aucSpkNum, stUCC.tCallInfoS.enRcrdServType, szFromUrl, szToUrl);

    H_User_Voice_Item_S stUserVoiceItem;
    memset(&stUserVoiceItem, 0, sizeof(H_User_Voice_Item_S));
    memcpy(stUserVoiceItem.company_no, stUCC.tCallInfoS.aucSpkNum, 8);

    /* get h_user_voice.id */
    ulReturn = rceGetUserVoiceIdBySipCallId(&stUCC, &stUserVoiceItem.user_voice_no);
    if(XPOC_ERR_SUCCESS != ulReturn)
    {
        return ulReturn;
    }

    strncpy(stUserVoiceItem.user_no, stUCC.tCallInfoS.aucSpkNum, STR_LEN32);
    strcpy(stUserVoiceItem.call_id, stUCC.tCallInfoS.aucCceSipCallId);

    strcpy(stUserVoiceItem.start_time, ptMediaFd->aucRecordStartTime);

    ///
    //int now_ms = 0;
    struct timeval tv;
    //struct tm now_tm;
    time_t now_sec;
    gettimeofday(&tv, NULL);
    now_sec = tv.tv_sec;
    //now_ms = tv.tv_usec/1000;
    struct tm *tmInfo = localtime(&now_sec);
    strftime(stUserVoiceItem.end_time, 20, "%Y-%m-%d %H:%M:%S", tmInfo);
    strcpy(stUserVoiceItem.from_voice_url, szFromUrl);
    strcpy(stUserVoiceItem.to_voice_url, szToUrl);
    strcpy(stUserVoiceItem.floor_url, "");
    strcpy(stUserVoiceItem.record_time, stUserVoiceItem.start_time);

    ulReturn = MYSQL_UserVoiceItem_Add(mysql_connect, &stUserVoiceItem);
    if(XPOC_ERR_SUCCESS != ulReturn)
    {
        return ulReturn;
    }

    XPOC_TRACEFUNC_OUT;
    return XPOC_ERR_SUCCESS;
}

#if 0
#endif

UINT CreateRecordFile(MediaFdS *ptMediaFd)
{
    XPOC_TRACEFUNC_IN;
    X_NULL_POINTER_CHK_ONE_PARA(ptMediaFd);

    //(void)CloseRecordFile(ptMediaFd);

    T_RceUCallCtxTable stUCC;
    memset(&stUCC, 0, sizeof(T_RceUCallCtxTable));
    UINT ulReturn = XRceUCallCtxOpr(XDB_OPR_QRY, NULL, 0, (MediaTypeE)0, NULL, &ptMediaFd->ulUCCId, &stUCC);
    if (XPOC_ERR_SUCCESS != ulReturn)
    {
        x_ErrorLog("ucc data null.\n");
        return XPOC_ERR_FAIL;
    }

//    /* 在存储报文到文件的时候,增加检查当前报文的ssrc与ucc.ssrc是否一致. */
//    UINT ulRtpSsrc = 0;
//    UINT ulRtpSsrcLen = 0;
//    if (MEDIA_AUDIO == ptMediaFd->iRceMediaType)
//    {
//        Byte_2_U32(ptNode->PktBuf+8, ulRtpSsrc, ulRtpSsrcLen);
//    }
//    else if (MEDIA_VIDEO == ptMediaFd->iRceMediaType)
//    {
//        Byte_2_U32(ptNode->PktBuf+10+7, ulRtpSsrc, ulRtpSsrcLen);
//    }
//    /* rtp.ssrc 与ucc.mediainfo.ssrc 不一致,说明当前的rtp不是该ucc的报文, 丢弃. */
//    x_InfoLog("ucc.ssrc[%d(0x%x)],rtp.ssrc[%d(0x%x)].",
//            stUCC.stMediaInfo.ulSsrc, stUCC.stMediaInfo.ulSsrc, ulRtpSsrc, ulRtpSsrc);
//    if((0 != stUCC.stMediaInfo.ulSsrc) && (ulRtpSsrc != stUCC.stMediaInfo.ulSsrc))
//    {
//        return XPOC_ERR_FAIL;
//    }

    ///
    //int now_ms = 0;
    struct timeval tv;
    //struct tm now_tm;
    time_t now_sec;
    gettimeofday(&tv, NULL);
    now_sec = tv.tv_sec;
    //now_ms = tv.tv_usec/1000;
    ///
    MediaFdS oldMedFds = *ptMediaFd;

    char createTime[STR_LEN512];
    time(&(ptMediaFd->tmRecordStartTime));
    memset(ptMediaFd->szRecordFileName, 0, STR_LEN512);    
    memset(createTime, 0, STR_LEN32);
    strftime(createTime, STR_LEN32, "%Y-%m-%d-%H-%M-%S", localtime(&now_sec));
    strcpy(ptMediaFd->aucRecordStartTime, createTime);

    (void)generateRecordFileName(ptMediaFd, &stUCC.tCallInfoS, createTime);

    /// avoid open the same file
    if (MEDIA_AUDIO == ptMediaFd->iRceMediaType)
    {
        if(oldMedFds.fd_record != -1 && !strcmp(oldMedFds.szRecordFileName, ptMediaFd->szRecordFileName))
        {
            XPOC_TRACEFUNC_OUT;
            return XPOC_ERR_SUCCESS;
        }
        else
        {
            (void)CloseRecordFile(&oldMedFds);
        }
    }

    // upd starttime endtime
    rceUptUserVideoStartTime2DB_2(ptMediaFd, 0);
    rceUptUserVideoEndTime2DB_2(ptMediaFd, 0);

    // modify by lkf
    char fleetIdFolder[STR_LEN10];
    memset(fleetIdFolder, 0, STR_LEN10);
    memcpy(fleetIdFolder, &stUCC.tCallInfoS.aucSpkNum, STR_LEN8); // modify by lkf, 获取8位集团号
    char fleetPath[STR_LEN256]; // modify by lkf, 临时变量,检查是否存在子文件夹
    memset(fleetPath, 0, STR_LEN256);
    sprintf(fleetPath, "%s/%s", g_rceDir, fleetIdFolder);
    if (access(fleetPath, 0) == -1) // 集团文件夹是否建立
    {
        mkdir(fleetPath, 0777); /* 如果不存在就用mkdir函数来创建 */
        x_InfoLog("Make fleet dir:[%s].", fleetPath);
    }

    char dateFolder[STR_LEN16];
    memset(dateFolder, 0, STR_LEN16);
    memcpy(dateFolder, createTime, STR_LEN10); // modify by lkf, 获取年月日
    char datePath[STR_LEN256]; // modify by lkf, 临时变量,检查是否存在子文件夹
    memset(datePath, 0, STR_LEN256);
    sprintf(datePath, "%s/%s", fleetPath, dateFolder);
    if (access(datePath, 0) == -1)
    {
        mkdir(datePath, 0777);
        x_InfoLog("Make date dir:[%s].", datePath);
    }

    /* 每次生成新文件时才确定文件名 */
    char tempPath[STR_LEN512];
    memset(tempPath, 0, STR_LEN512);
    sprintf(tempPath, "%s/%s", datePath, ptMediaFd->szRecordFileName); // modify by lkf
    
    if (MEDIA_AUDIO == ptMediaFd->iRceMediaType) //todo: why???
    {
        //printf("--------ptMediaFd->tcpFdIdx: %d\n", ptMediaFd->tcpFdIdx);
        // use video fd
        if(ptMediaFd->tcpFdIdx != -1)
        {
            ptMediaFd->ulSsrc = 0;
            ptMediaFd->waitVideoSPS = 1;
            ptMediaFd->iRceRecordState = Record_WORK;
            XPOC_TRACEFUNC_OUT;
            return XPOC_ERR_SUCCESS;
        }

        // open file
        if ((ptMediaFd->fd_record = open(tempPath, O_CREAT | O_TRUNC | O_WRONLY, S_IREAD | S_IWRITE)) < 0)
        {
            x_WarnLog("open record afile failed.errno [%d]-[%s]", errno, strerror(errno));
            ptMediaFd->fd_record = -1;
            return XPOC_ERR_FAIL;
        }
        x_InfoLog("Create audio file : %s", tempPath);

		if (AUDIOTYPE_AMR_NB == audio_record_type)
		{
			char header[AMR_NB_HEADER_LEN] = {0};
			if (-1 == write(ptMediaFd->fd_record, header, AMR_NB_HEADER_LEN))
			{
				return XPOC_ERR_FAIL;
			}
		}
		else if (AUDIOTYPE_AMR_WB == audio_record_type)
		{
			char header[AMR_WB_HEADER_LEN] = {0};
			if (-1 == write(ptMediaFd->fd_record, header, AMR_WB_HEADER_LEN))
			{
				return XPOC_ERR_FAIL;
			}
		}
		else if (AUDIOTYPE_G711 == audio_record_type)
		{
			char header[56];
			memset(header, 0, 56);
			if (-1 == write(ptMediaFd->fd_record, header, 56))
			{
				return XPOC_ERR_FAIL;
			}
		}
		else
		{
			return XPOC_ERR_FAIL;
		}
     
    }
    else if(MEDIA_VIDEO == ptMediaFd->iRceMediaType)
    {
        // open file
        ptMediaFd->pvMp4HFile = MP4Create(tempPath);
        if (ptMediaFd->pvMp4HFile == MP4_INVALID_FILE_HANDLE)
        {
            x_WarnLog("open record vfile failed.\n");
            ptMediaFd->pvMp4HFile = NULL;
            ptMediaFd->ulMp4Vid = 0;
            ptMediaFd->ulMp4Aud = 0;
            ptMediaFd->waitVideoSPS = 0;
            ptMediaFd->ulAudRemainDura = 0;
            return XPOC_ERR_FAIL;
        }
        //printf("..ptMediaFd->pvMp4HFile:%p\n", ptMediaFd->pvMp4HFile);
        x_InfoLog("Create mp4 file : %s", tempPath);
        //
        ptMediaFd->pFrameBuf = (UCHAR*)malloc(800*1024);
        ptMediaFd->ulFrameLen = 0;
        ptMediaFd->ulLastVRtpTs = 0;
        ptMediaFd->ulLastARtpTs = 0;
        ptMediaFd->ulAvgVRtpTs = 0;
        ptMediaFd->tmLastWriteSmpTime = GetCurrMsTime();
    }
    ptMediaFd->ulSsrc = 0;
	ptMediaFd->waitVideoSPS = 1;
    ptMediaFd->iRceRecordState = Record_WORK;

    XPOC_TRACEFUNC_OUT;
    return XPOC_ERR_SUCCESS;
}

UINT CloseRecordFile(MediaFdS *ptMediaFd)
{
    XPOC_TRACEFUNC_IN;
    X_NULL_POINTER_CHK_ONE_PARA(ptMediaFd);

    ptMediaFd->iRceRecordState = Record_IDLE;

    if(ptMediaFd->fd_record != -1 && ptMediaFd->iRceMediaType != 0)
        x_InfoLog("mediaFd: fd_record[%d],mediaType[%d].", ptMediaFd->fd_record, ptMediaFd->iRceMediaType);

    ///
    ptMediaFd->tcpFdIdx = -1;

    if (MEDIA_AUDIO == ptMediaFd->iRceMediaType) /*音频 */
    {
        if(-1 == ptMediaFd->fd_record)  /* 如果本来就没有文件, do nothing. */
        {
        	XPOC_TRACEFUNC_OUT;
            return XPOC_ERR_SUCCESS;
        }

        int file_len = lseek(ptMediaFd->fd_record, 0, SEEK_END);/* 返回值为文件大小+offset */
        lseek(ptMediaFd->fd_record, 0, SEEK_SET);         /* 重定位文件开始 */
		
		if (AUDIOTYPE_AMR_NB == audio_record_type)
		{
			AddAMRNBFileHeader(ptMediaFd->fd_record, file_len);
		}
		else if (AUDIOTYPE_AMR_WB == audio_record_type)
		{
			AddAMRFileHeader(ptMediaFd->fd_record, file_len);
		}
		else if (AUDIOTYPE_G711 == audio_record_type)
		{
			AddWaveFileHeader(ptMediaFd->fd_record, file_len);
		}

        // close file
        close(ptMediaFd->fd_record);
        ptMediaFd->fd_record = -1;

        // update db
        (void)rceAddVoiceRecord2DB(ptMediaFd);

        /* 关闭文件说明是写完一个文件, 需要把record的当前结束时间写道h_user_voice中 */
        (void)rceUptUserVoiceEndTime2DB(ptMediaFd);
    }
    else if (MEDIA_VIDEO == ptMediaFd->iRceMediaType)
    {
        if(NULL == ptMediaFd->pvMp4HFile)  /* 如果本来就没有文件, do nothing. */
        {
            return XPOC_ERR_SUCCESS;
        }

        // close file
        //printf("ptMediaFd->pvMp4HFile:%p\n", ptMediaFd->pvMp4HFile);
        ULONGLONG dur = MP4GetDuration(ptMediaFd->pvMp4HFile);
        if(ptMediaFd->ulMp4Aud != MP4_INVALID_TRACK_ID) {
            ULONGLONG adur = MP4GetTrackDuration(ptMediaFd->pvMp4HFile, ptMediaFd->ulMp4Aud);
            if(adur+500 < dur) {  // 500 ms
                WriteSilentDataToMp4File(ptMediaFd, dur-adur);
            }
        }
        //printf("................dur: %lu\n", dur);
        MP4Close(ptMediaFd->pvMp4HFile);
        ptMediaFd->pvMp4HFile = NULL;
        ptMediaFd->ulMp4Vid = 0;
        ptMediaFd->ulMp4Aud = 0;
        //
        if(ptMediaFd->pFrameBuf)
        {
            free(ptMediaFd->pFrameBuf);
            ptMediaFd->pFrameBuf = NULL;
        }
        ptMediaFd->ulFrameLen = 0;
        ptMediaFd->ulLastVRtpTs = 0;
        ptMediaFd->ulAvgVRtpTs = 0;
        ptMediaFd->tmLastWriteSmpTime = 0;

        // update db
        (void)rceAddVideoRecord2DB(ptMediaFd);

        /* 关闭文件说明是写完一个文件, 需要把record的当前结束时间写道h_user_video中 */
        //(void)rceUptUserVideoEndTime2DB(ptMediaFd);
        (void)rceUptUserVideoEndTime2DB_2(ptMediaFd, dur/1000);
    }

    XPOC_TRACEFUNC_OUT;
    return XPOC_ERR_SUCCESS;
}

UINT WriteG711AudioDataToFile(int fd_record, char*pdata, int len)
{
    char AudioData_PCM_sample[PCM_SAMPLE_LEN];
    memset(AudioData_PCM_sample, 0, PCM_SAMPLE_LEN);
    int voicelen = g711_decode(AudioData_PCM_sample, (unsigned char*)pdata, len);
    
    if (-1 != write(fd_record, AudioData_PCM_sample, voicelen))
    {
        return XPOC_ERR_SUCCESS;
    }
    return XPOC_ERR_FAIL;
}

//AMR-WB 帧格式长度
static const int amr_frame_sizes[] = { 17, 23, 32, 36, 40, 46, 50, 58, 60, 5 };
#define MAX_FRAME_TYPE 9
int amrwb_read_bytes(int nMode)
{
	if ((nMode >= 0) && (nMode < MAX_FRAME_TYPE))
	{
		return amr_frame_sizes[nMode];
	}
	return 0;
}

UINT WriteAMRWBAudioDataToFile(int fd_record, char*pdata, int len)
{
	int FT = ((pdata[0] & 0x07) << 1) | ((pdata[1] & 0x80) >> 7);
	int nAmrSize = amrwb_read_bytes(FT);
	if (len != nAmrSize + 1 ) return XPOC_ERR_FAIL;
	int i = 0;

	//移位
    //添加文件帧头,位分布如下,其中P固定为0, Q置为1
    /*  | 0 | 1 2 3 4 | 5 6 7 |
        | P |    FT   | Q P P | */
	pdata[0] = ((FT & 0X0F) << 3) | 0x04;
	for (i = 1; i < nAmrSize; i++) // AMR rtp负载格式转换成文件格式
	{
		pdata[i] = ((pdata[i] << 2) & 0xFC) | ((pdata[i + 1] >> 6) & 0x03);
	}
	pdata[nAmrSize] = (pdata[nAmrSize] << 2) & 0xFC;
	
	if (-1 != write(fd_record, pdata, nAmrSize+1))
	{
		return XPOC_ERR_SUCCESS;
	}
	return XPOC_ERR_FAIL;
}

//AMR-NB 帧格式长度
static const int amr_nb_frame_sizes[] = { 12, 13, 15, 17, 19, 20, 26, 31, 5, 0, 0, 0, 0, 0, 0, 0};
static const int amrnb_framelenbits[9] = { 95, 103, 118, 134, 148, 159, 204, 244, 39 };

int amrnb_read_bytes(int nMode)
{
	if ((nMode >= 0) && (nMode < MAX_FRAME_TYPE))
	{
		return amr_nb_frame_sizes[nMode];
	}
	return 0;
}

UINT WriteAMRNBAudioDataToFile(int fd_record, char*pdata, int len)
{
	int FT = ((pdata[0] & 0x07) << 1) | ((pdata[1] & 0x80) >> 7);
    int nAmrSize = amrnb_read_bytes(FT);
	if (len != nAmrSize + 1 ) return XPOC_ERR_FAIL;
	int i = 0;

	//移位
    //添加文件帧头,位分布如下,其中P固定为0, Q置为1
    /*  | 0 | 1 2 3 4 | 5 6 7 |
        | P |    FT   | Q P P | */
	pdata[0] = ((FT & 0X0F) << 3) | 0x04;
	for (i = 1; i < nAmrSize; i++) // AMR rtp负载格式转换成文件格式
	{
		pdata[i] = ((pdata[i] << 2) & 0xFC) | ((pdata[i + 1] >> 6) & 0x03);
	}
	pdata[nAmrSize] = (pdata[nAmrSize] << 2) & 0xFC;
	
	if (-1 != write(fd_record, pdata, nAmrSize+1))
	{
		return XPOC_ERR_SUCCESS;
	}
	return XPOC_ERR_FAIL;
}

UCHAR unpackByteField(UCHAR *pBitString, int* pBitPosition, int byBitLength)
{
	int iByteIndex;     /*特定字段所在的字节位置*/
	int iBitIndex;      /*该字段在字节中的比特起始位*/
	unsigned short wTempWord = 0;

	if(NULL == pBitString  || byBitLength > 8)
	{
		return 0;
	}

	iByteIndex    = *pBitPosition / 8;
	iBitIndex     = *pBitPosition % 8;
	*pBitPosition += byBitLength;

	wTempWord = *(pBitString + iByteIndex);
	wTempWord <<= 8;
	wTempWord += *(pBitString + iByteIndex + 1);
	wTempWord <<= iBitIndex;
	wTempWord >>= (16 - byBitLength);
	return (UCHAR)(wTempWord);
}

#define AMR_FRAME_QUALITY_BAD      0
#define AMR_FRAME_QUALITY_GOOD     1 
#define AMR_TOC_FRAME_QUALITY_MASK 0x01
#define VOICE_FRAME_PACK_MAX	   12  //最大12帧数据,实际上只是用了10帧

// add by shm,多帧传输格式数据
/* rfc3267中多帧样例:
   0                   1                   2                   3
   0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   | CMR=1 |1| FT=0  |1|1| FT=9  |1|1| FT=15 |1|0| FT=1  |1|d(0)   |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                                                               |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                                                               |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                                                               |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                                                         d(131)|
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |g(0)                                                           |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |          g(39)|h(0)                                           |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                                                               |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                                                               |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                                                               |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                                           h(176)|P|P|P|P|P|P|P|
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
/* typedef struct
{
    int nFT;    
    int nFrameSize;

}T_AmrNbMf; */
#if 0
UINT WriteAMRNBMFAudioDataToFile(int fd_record, UCHAR* pdata, int len) 
{
    if (pdata == NULL)
		return XPOC_ERR_FAIL;

    UCHAR acTOC[VOICE_FRAME_PACK_MAX] = {0};
	/* 解码偏移量 */
	int wPos         = 0;
	int wBitPosition = 0;

	//get CMR
	unpackByteField(pdata, &wBitPosition, 4);	

	//判断尾帧
	int tocPos = wBitPosition;
	int nFrameCount = 0;
	UCHAR bHasNextFT = 1; // 是否有下一个ft头
	while (bHasNextFT)
	{
		acTOC[nFrameCount] = unpackByteField(pdata, &wBitPosition, 6);
		bHasNextFT = (UCHAR)(acTOC[nFrameCount] >> 5);

		if (++nFrameCount >= VOICE_FRAME_PACK_MAX)
		{
			bHasNextFT = 0;
		}
	}

	for (int i = 0; i < nFrameCount; i++)
	{
		switch (acTOC[i] & AMR_TOC_FRAME_QUALITY_MASK)
		{
		case AMR_FRAME_QUALITY_BAD: //bad frame  ingnore  
            x_WarnLog("bit align Decode [%d] frame error, TOC [%d]", i, acTOC[i]);
			break;

		case AMR_FRAME_QUALITY_GOOD: // valid,get the frame bits
			int nFT = (acTOC[i] >> 1) & 0x0f;
			int nFrameLen = amr_nb_frame_sizes[nFT];
            char acFrame[32] = {0};    // 最多32个字节, nFrameLen 最大为31
            acFrame[0] = ((nFT & 0X0F) << 3) | 0x04;

			for (int k = 0; k < nFrameLen; k++)
			{
				if (nFrameLen - 1 == k)
				{
					int nLastbitOff = amrnb_framelenbits[nFT] % 8;
					// 最后一个字节只有nLastbitOff位,这里要特别注意
					acFrame[k + 1] = unpackByteField(pdata, &wBitPosition, nLastbitOff);
					acFrame[k + 1] = acFrame[k + 1] << (8 - nLastbitOff);
				}
				else
				{ // 其它bit都按字节进行填充
					acFrame[k + 1] = unpackByteField(pdata, &wBitPosition, 8);
				}		 
			}

            if (-1 == write(fd_record, acFrame, nFrameLen + 1))
            {
                x_ErrorLog("Write a amr frame failed.");
            }
			break;
		}
	}

	// int nLen = (wBitPosition + 7) / 8;

	// if (nLen != len)
	// {
	// 	amrForDecode.bUsedNum = 0;
	// 	x_InfoLog("payload data size is invalid  [%d], bitOff [%d]", len, wBitPosition);
	// 	return XPOC_ERR_FAIL;
	// }
	return XPOC_ERR_SUCCESS;
}
#endif
UINT tokAMRNBMFAudioData(UCHAR* pdata, int len, 
                        UCHAR outdat[VOICE_FRAME_PACK_MAX][32], 
                        UCHAR outlen[VOICE_FRAME_PACK_MAX]) 
{
    UCHAR acTOC[VOICE_FRAME_PACK_MAX] = {0};
	/* 解码偏移量 */
	int wPos         = 0;
	int wBitPosition = 0;

	//get CMR
	unpackByteField(pdata, &wBitPosition, 4);	

	//判断尾帧
	int tocPos = wBitPosition;
	int nFrameCount = 0;
	UCHAR bHasNextFT = 1; // 是否有下一个ft头
	while (bHasNextFT)
	{
		acTOC[nFrameCount] = unpackByteField(pdata, &wBitPosition, 6);
		bHasNextFT = (UCHAR)(acTOC[nFrameCount] >> 5);

		if (++nFrameCount >= VOICE_FRAME_PACK_MAX)
		{
			bHasNextFT = 0;
		}
	}

	for (int i = 0; i < nFrameCount; i++)
	{
        outlen[i] = 0;
		switch (acTOC[i] & AMR_TOC_FRAME_QUALITY_MASK)
		{
		case AMR_FRAME_QUALITY_BAD: //bad frame  ingnore  
            //printf("bad frame....%d\n", (acTOC[i] >> 1) & 0x0f);
            x_WarnLog("bit align Decode [%d] frame error, TOC [%d]", i, acTOC[i]);
			break;

		case AMR_FRAME_QUALITY_GOOD: // valid,get the frame bits
			int nFT = (acTOC[i] >> 1) & 0x0f;
			int nFrameLen = amr_nb_frame_sizes[nFT];
            memset(outdat[i], 0, 32);
            outdat[i][0] = ((nFT & 0X0F) << 3) | 0x04;
            //if(nFT >= 8 && nFT < 15)
            // printf(".................nFT: %d\n", nFT);

			for (int k = 0; k < nFrameLen; k++)
			{
				if (nFrameLen - 1 == k)
				{
					int nLastbitOff = amrnb_framelenbits[nFT] % 8;
					// 最后一个字节只有nLastbitOff位,这里要特别注意
					outdat[i][k + 1] = unpackByteField(pdata, &wBitPosition, nLastbitOff);
					outdat[i][k + 1] = outdat[i][k + 1] << (8 - nLastbitOff);
				}
				else
				{ // 其它bit都按字节进行填充
					outdat[i][k + 1] = unpackByteField(pdata, &wBitPosition, 8);
				}		 
			}

            if (nFrameLen > 0)
                outlen[i] = nFrameLen+1;

            ///
			break;
		}
	}

	return nFrameCount;
}

UINT WriteAMRNBMFAudioDataToFile(int fd_record, UCHAR* pdata, int len) 
{
    if (pdata == NULL)
		return XPOC_ERR_FAIL;

    UCHAR frames[VOICE_FRAME_PACK_MAX][32] = {0};
    UCHAR frameLen[VOICE_FRAME_PACK_MAX] = {0};
    UINT frameCnt = 0;
    frameCnt = tokAMRNBMFAudioData(pdata, len, frames, frameLen);
    UINT n = 0; 
    for(; n < frameCnt; n++) {
        if (-1 == write(fd_record, frames[n], frameLen[n]))
        {
            x_ErrorLog("Write a amr frame failed.");
            return XPOC_ERR_FAIL;
        }
    }
    
	return XPOC_ERR_SUCCESS;
}

static UINT Ue(UCHAR *pBuff, UINT nLen, UINT *pStartBit)
{
    //计算0bit的个数
    UINT nZeroNum = 0;
    while ((*pStartBit) < nLen * 8)
    {
        if (pBuff[(*pStartBit) / 8] & (0x80 >> ((*pStartBit) % 8))) //&:按位与,%取余
        {
            break;
        }
        nZeroNum++;
        (*pStartBit)++;
    }
    (*pStartBit)++;

    //计算结果
    UINT ret = 0;
    for (UINT i=0; i<nZeroNum; i++)
    {
        ret <<= 1;
        if (pBuff[(*pStartBit) / 8] & (0x80 >> ((*pStartBit) % 8)))
        {
            ret += 1;
        }
        (*pStartBit)++;
    }
    return (1 << nZeroNum) - 1 + ret;
}

static int Se(UCHAR *pBuff, UINT nLen, UINT *pStartBit)
{
    int UeVal=Ue(pBuff,nLen,pStartBit);
    double k=UeVal;
    int nValue=ceil(k/2);
    if (UeVal % 2==0)
        nValue=-nValue;
    return nValue;
}

static UINT u(UINT BitCount,UCHAR *buf,UINT *pStartBit)
{
    UINT ret = 0;
    for (UINT i=0; i<BitCount; i++)
    {
        ret <<= 1;
        if (buf[(*pStartBit) / 8] & (0x80 >> ((*pStartBit) % 8)))
        {
            ret += 1;
        }
        (*pStartBit)++;
    }
    return ret;
}

static void DecodeH264SPS(UCHAR *buf, UINT nLen, int *pWid, int *pHei)
{
    UINT StartBit = 8;
    int profile_idc=u(8,buf,&StartBit);
    int constraint_set0_flag=u(1,buf,&StartBit);//(buf[1] & 0x80)>>7;
    int constraint_set1_flag=u(1,buf,&StartBit);//(buf[1] & 0x40)>>6;
    int constraint_set2_flag=u(1,buf,&StartBit);//(buf[1] & 0x20)>>5;
    int constraint_set3_flag=u(1,buf,&StartBit);//(buf[1] & 0x10)>>4;
    int reserved_zero_4bits=u(4,buf,&StartBit);
    int level_idc=u(8,buf,&StartBit);

    int seq_parameter_set_id=Ue(buf,nLen,&StartBit);
    if( profile_idc == 100 || profile_idc == 110 ||  
            profile_idc == 122 || profile_idc == 144 )  
    {  
        int chroma_format_idc=Ue(buf,nLen,&StartBit);  
        if( chroma_format_idc == 3 )  
            int residual_colour_transform_flag=u(1,buf,&StartBit);  
        int bit_depth_luma_minus8=Ue(buf,nLen,&StartBit);  
        int bit_depth_chroma_minus8=Ue(buf,nLen,&StartBit);  
        int qpprime_y_zero_transform_bypass_flag=u(1,buf,&StartBit);  
        int seq_scaling_matrix_present_flag=u(1,buf,&StartBit);  

        int seq_scaling_list_present_flag[8];  
        if( seq_scaling_matrix_present_flag )  
        {  
            for( int i = 0; i < 8; i++ ) {  
                seq_scaling_list_present_flag[i]=u(1,buf,&StartBit);  
            }  
        }  
    }  

    int log2_max_frame_num_minus4=Ue(buf,nLen,&StartBit);
    int pic_order_cnt_type=Ue(buf,nLen,&StartBit);
    if( pic_order_cnt_type == 0 )
        Ue(buf,nLen,&StartBit);
    else if( pic_order_cnt_type == 1 )
    {
        int delta_pic_order_always_zero_flag=u(1,buf,&StartBit);
        int offset_for_non_ref_pic=Se(buf,nLen,&StartBit);
        int offset_for_top_to_bottom_field=Se(buf,nLen,&StartBit);
        int num_ref_frames_in_pic_order_cnt_cycle=Ue(buf,nLen,&StartBit);
        for( int i = 0; i < num_ref_frames_in_pic_order_cnt_cycle; i++ )
            Se(buf,nLen,&StartBit);
    }
    int num_ref_frames=Ue(buf,nLen,&StartBit);
    int gaps_in_frame_num_value_allowed_flag=u(1,buf,&StartBit);
    int pic_width_in_mbs_minus1=Ue(buf,nLen,&StartBit);
    int pic_height_in_map_units_minus1=Ue(buf,nLen,&StartBit);

    if(pWid && pHei)
    {
        *pWid=(pic_width_in_mbs_minus1+1)*16;
        *pHei=(pic_height_in_map_units_minus1+1)*16;
    }
}
UINT SetAddMp4VidTrack(void *hFile, UCHAR *pSps, UINT len)
{
    if(hFile && pSps)
    {
        int w = 0, h = 0;
        DecodeH264SPS(pSps, len, &w, &h);
        //printf("w: %d, h: %d\n", w, h);

        //printf(".........MP4AddH264VideoTrack..........\n");
        const UINT ulTimeScale = 1000;
        //MP4SetTimeScale(hFile, 90000);
        MP4SetTimeScale(hFile, ulTimeScale);
        UINT trackId = MP4AddH264VideoTrack(hFile, ulTimeScale, MP4_INVALID_DURATION, // MP4_INVALID_DURATION
                                                    w, h, pSps[1], pSps[2], pSps[3], 3); // 4 bytes length before each NAL unit
        if (trackId == MP4_INVALID_TRACK_ID)
        {
            //printf("add video track failed.\n");
            x_InfoLog("add video track failed.\n");
        }

        return trackId;
    }

    return MP4_INVALID_TRACK_ID;
}
UINT SetAddMp4AudTrack(void *hFile, char type)
{
    if(hFile)
    {
        UINT trackId = MP4_INVALID_TRACK_ID;
        if(AUDIOTYPE_AMR_NB == type) 
        {
            trackId = MP4AddAmrAudioTrack(hFile, 8000, 0, 0, 1, FALSE);
        }
        else if(AUDIOTYPE_AMR_WB == type) 
        {
            trackId = MP4AddAmrAudioTrack(hFile, 16000, 0, 0, 1, TRUE);
        }

        if (trackId == MP4_INVALID_TRACK_ID)
        {
            //printf("add audio track failed.\n");
            x_InfoLog("add audio track failed.\n");
        }

        return trackId;
    }

    return MP4_INVALID_TRACK_ID;
}
void SetMp4VidAvcCfg(void *hFile, UINT trackId, AvcCfg *pAvcCfg)
{
    // set metadata
    MP4SetVideoProfileLevel(hFile, 0x7f);
    const int hdrLen = (pAvcCfg->ucSps[0]&0x10)?RTP_HEADER_LEN+RTP_HEADER_EXT_LEN:RTP_HEADER_LEN;
    MP4AddH264SequenceParameterSet(hFile, trackId, pAvcCfg->ucSps+hdrLen, pAvcCfg->ulSpsLen-hdrLen);
    MP4AddH264PictureParameterSet(hFile, trackId, pAvcCfg->ucPps+hdrLen, pAvcCfg->ulPpsLen-hdrLen);
}
UINT CheckNeedVideoSPS(char waitVideoSPS, UCHAR *pdata, int len, MediaFdS *pMediaFd)
{
	if (waitVideoSPS != 1) return XPOC_ERR_SUCCESS;
    if (!pMediaFd) return XPOC_ERR_FAIL;

    const int hdrLen = (pdata[0]&0x10)?RTP_HEADER_LEN+RTP_HEADER_EXT_LEN:RTP_HEADER_LEN;
	unsigned char *  src = (unsigned char*)pdata + hdrLen;
	unsigned char  head1 = *src; // 获取第一个字节 
	unsigned char  nal = head1 & 0x1f; // 获取FU indicator的类型域, 
    //printf(".%llu........nal: %d..........marker: %d.\n", GetCurrMsTime()/1000, nal, (pdata[1]&0x80)>>7);
	if (nal == 7) // SPS
	{//人为修改SPS的帧率,不然VLC播放录像会很快,码流是30 修改成9,计算方式是0x12 / 2
		/*unsigned char ctmp = *(src + 18);
		*(src + 18) = (ctmp & 0xF0) + 1;
		ctmp = *(src + 19);
		*(src + 19) = (ctmp & 0x0F) | ( 0x20);
		return XPOC_ERR_SUCCESS;*/

        //
        if(pMediaFd->ulMp4Vid == MP4_INVALID_TRACK_ID)
        {
            pMediaFd->ulMp4Vid = SetAddMp4VidTrack(pMediaFd->pvMp4HFile, src, len-hdrLen);
            if (pMediaFd->ulMp4Vid == MP4_INVALID_TRACK_ID)
            {
                return XPOC_ERR_FAIL;
            }
        }

        //
        //printf(".....enRcrdServType...%d ...ulMp4Aud...%d....\n", pMediaFd->enRcrdServType, pMediaFd->ulMp4Aud);
        if(/*pMediaFd->enRcrdServType == Serv_VideoPri && */pMediaFd->ulMp4Aud == MP4_INVALID_TRACK_ID)
        {
            pMediaFd->ulMp4Aud = SetAddMp4AudTrack(pMediaFd->pvMp4HFile, audio_record_type);
            if (pMediaFd->ulMp4Aud == MP4_INVALID_TRACK_ID)
            {
                return XPOC_ERR_FAIL;
            }
        }

        //
        pMediaFd->stAvcCfg.ulSpsLen = len;
        memcpy(pMediaFd->stAvcCfg.ucSps, pdata, pMediaFd->stAvcCfg.ulSpsLen);

        return XPOC_ERR_FAIL;
	}
    else if (nal == 8) // PPS
	{
        //
        pMediaFd->stAvcCfg.ulPpsLen = len;
        memcpy(pMediaFd->stAvcCfg.ucPps, pdata, pMediaFd->stAvcCfg.ulPpsLen);

        return XPOC_ERR_FAIL;
	}
    else if (nal == 5 || nal == 6 || nal == 28) // 
    {
        if(nal == 28)
        {
            if(pMediaFd->ulMp4Vid && ((src[1]&0x1f)==5 || (src[1]&0x1f)==6))
                return XPOC_ERR_SUCCESS;
        }
        else
        {
            if(pMediaFd->ulMp4Vid)
                return XPOC_ERR_SUCCESS;
        }
        
    }
	return XPOC_ERR_FAIL;
}

UINT WriteVideoDataToFile(int fd_record, UCHAR *pdata, int len)
{
    unsigned char *pBufOut = NULL;
    int  BufOutLen = 0;
    if (XPOC_ERR_SUCCESS == UnpackRTPH264(pdata, len, &pBufOut, &BufOutLen))
    {
        if (-1 != write(fd_record, pBufOut, BufOutLen))
        {
            return XPOC_ERR_SUCCESS;
        }
    }
    return XPOC_ERR_FAIL;
}

UINT WriteVideoDataToMp4File(MediaFdS *pMediaFd, UCHAR *pdata, int len)
{
    unsigned char *pBufOut = NULL;
    int  BufOutLen = 0;
    if(pMediaFd->pvMp4HFile && pMediaFd->ulMp4Vid)
    {
        UINT ulTs = 0;
        UCHAR ucTsLen = 0;
        Byte_2_U32(pdata+4, ulTs, ucTsLen);
        const int hdrLen = (pdata[0]&0x10)?RTP_HEADER_LEN+RTP_HEADER_EXT_LEN:RTP_HEADER_LEN;
        UCHAR tp = pdata[hdrLen]&0x1f; 
        //printf(".%llu...ulTs: %u...............%d......marker: %d.\n", GetCurrMsTime()/1000, ulTs, tp, (pdata[1]&0x80)>>7);
        if (XPOC_ERR_SUCCESS == UnpackRTPH264(pdata, len, &pBufOut, &BufOutLen))
        {
#if 0            
            // test
            {
                static FILE *fp = NULL;
                if(!fp)
                {
                    fp = fopen("tou1.h264", "wb");
                    char prc[4] = {0,0,0,1};
                    fwrite(prc, 1, 4, fp);
                    fwrite(pMediaFd->stAvcCfg.ucSps+RTP_HEADER_LEN, 1, pMediaFd->stAvcCfg.ulSpsLen-RTP_HEADER_LEN, fp);
                    fwrite(prc, 1, 4, fp);
                    fwrite(pMediaFd->stAvcCfg.ucPps+RTP_HEADER_LEN, 1, pMediaFd->stAvcCfg.ulPpsLen-RTP_HEADER_LEN, fp);
                }
                if(fp)
                {
                    /
                    /*if(tp == 5 && (ulTs != pMediaFd->ulLastRtpTs))
                    {
                        char prc[4] = {0,0,0,1};
                        fwrite(prc, 1, 4, fp);
                        fwrite(pMediaFd->stAvcCfg.ucSps+RTP_HEADER_LEN, 1, pMediaFd->stAvcCfg.ulSpsLen-RTP_HEADER_LEN, fp);
                        fwrite(prc, 1, 4, fp);
                        fwrite(pMediaFd->stAvcCfg.ucPps+RTP_HEADER_LEN, 1, pMediaFd->stAvcCfg.ulPpsLen-RTP_HEADER_LEN, fp);
                    }*/
                    fwrite(pBufOut, 1, BufOutLen, fp);
                }
                    
            }
#endif            

#if 1      
            //printf("...ulTs: %u, ulLastVRtpTs: %u\n", ulTs, pMediaFd->ulLastVRtpTs);
            if(ulTs != pMediaFd->ulLastVRtpTs && pMediaFd->ulLastVRtpTs)
            {
                //*((UINT*)pMediaFd->pFrameBuf) = htonl(pMediaFd->ulFrameLen-4);
                if(*((int *)(pMediaFd->pFrameBuf)) == 0x01000000)
                {
                    *((UINT*)pMediaFd->pFrameBuf) = htonl(pMediaFd->ulFrameLen-4);
                }
                time_t cur = GetCurrMsTime();
                UINT dura = (cur-pMediaFd->tmLastWriteSmpTime);//*90;
                ///UINT dura = (ulTs-pMediaFd->ulLastVRtpTs)/90;
                pMediaFd->tmLastWriteSmpTime = cur;
                if(dura >= 1000) dura /= 2;
                ///dura = pMediaFd->ulAvgVRtpTs?(pMediaFd->ulAvgVRtpTs+dura)/2:dura;
                UCHAR wTp = pMediaFd->pFrameBuf[4]&0x1f; 
                //printf("pMediaFd->ulFrameLen: %u, ulTs: %u, dura: %u, wTp: %d\n", pMediaFd->ulFrameLen, ulTs, dura, wTp);
                pthread_rwlock_wrlock(&pMediaFd->mtxWrMp4);    // added by shm
                bool ret = MP4WriteSample(pMediaFd->pvMp4HFile, pMediaFd->ulMp4Vid, pMediaFd->pFrameBuf, pMediaFd->ulFrameLen, dura, 0, (wTp==7||wTp==6||wTp==5));
                pthread_rwlock_unlock(&pMediaFd->mtxWrMp4);    // added by shm
                //printf("....ulTs: %u...dura: %u...0x%02x 0x%02x 0x%02x 0x%02x..........%d.\n", ulTs, dura, pMediaFd->pFrameBuf[2], pMediaFd->pFrameBuf[3], pMediaFd->pFrameBuf[4], pMediaFd->pFrameBuf[5], ret);
                pMediaFd->ulFrameLen = 0;

                /// fill null
                if(pMediaFd->enRcrdServType != Serv_VideoPri) {
                    WriteSilentDataToMp4File(pMediaFd, dura+pMediaFd->ulAudRemainDura);
                    pMediaFd->ulAudRemainDura = (dura+pMediaFd->ulAudRemainDura)%20;
                }
            }
            
            if(pMediaFd->pFrameBuf)
            {
                if(tp < 24)
                    *((UINT*)pBufOut) = htonl(BufOutLen-4);
                memcpy(pMediaFd->pFrameBuf+pMediaFd->ulFrameLen, pBufOut, BufOutLen);
                pMediaFd->ulFrameLen += BufOutLen;
            }         
#endif            
            //
            pMediaFd->ulLastVRtpTs = ulTs;
            return XPOC_ERR_SUCCESS;
        }
    }
    
    return XPOC_ERR_FAIL;
}

UINT WriteSilentDataToMp4File(MediaFdS *pMediaFd, int dura)
{
    if(pMediaFd->pvMp4HFile && pMediaFd->ulMp4Aud) {
        UCHAR sdat[6] = { 0x44 };
        int k = (dura)/20;
        while(k) 
        {
            pthread_rwlock_wrlock(&pMediaFd->mtxWrMp4);    // added by shm
            MP4WriteSample(pMediaFd->pvMp4HFile, pMediaFd->ulMp4Aud, 
                                    sdat, 6, 160, 0, TRUE);
            pthread_rwlock_unlock(&pMediaFd->mtxWrMp4);    // added by shm
            usleep(1);
            k--;
        }
    }

	return XPOC_ERR_SUCCESS;
}

UINT WriteAudioDataToMp4File(MediaFdS *pMediaFd, UCHAR *pdata, int len, UINT ts)
{
    if(pMediaFd->pvMp4HFile && pMediaFd->ulMp4Aud) {

        UCHAR frames[VOICE_FRAME_PACK_MAX][32] = {0};
        UCHAR frameLen[VOICE_FRAME_PACK_MAX] = {0};
        UINT frameCnt = 0;
        frameCnt = tokAMRNBMFAudioData(pdata, len, frames, frameLen);
        UINT n = 0; 
        for(; n < frameCnt; n++) {
            //printf("--%d-- ts %u, pMediaFd->ulLastARtpTs %u, -- %d\n", n, ts, pMediaFd->ulLastARtpTs, (ts-pMediaFd->ulLastARtpTs));
            UINT dura = 160;
            dura = (n==0&&pMediaFd->ulLastARtpTs)?(ts-pMediaFd->ulLastARtpTs):dura;
            pthread_rwlock_wrlock(&pMediaFd->mtxWrMp4);    // added by shm
            bool ret = MP4WriteSample(pMediaFd->pvMp4HFile, pMediaFd->ulMp4Aud, 
                                            frames[n], frameLen[n], dura, 0, TRUE);
            //bool ret = MP4WriteSample(pMediaFd->pvMp4HFile, pMediaFd->ulMp4Aud, 
            //                                frames[n], frameLen[n], 160, 0, TRUE);
            pthread_rwlock_unlock(&pMediaFd->mtxWrMp4);    // added by shm
            if(!ret)
            {
                //printf("Write a amr frame to mp4 failed.\n");
                x_ErrorLog("Write a amr frame to mp4 failed.");
                return XPOC_ERR_FAIL;
            }
            pMediaFd->ulLastARtpTs = ts+160*n;
        }
    }

	return XPOC_ERR_SUCCESS;
}

/* 5分钟生成一个新文件 */
UINT CreateNewRecordFileOrNot(MediaFdS *ptMediaFd)
{
    XPOC_TRACEFUNC_IN;
    X_NULL_POINTER_CHK_ONE_PARA(ptMediaFd);

    if (Record_WORK == ptMediaFd->iRceRecordState)
    {
        time_t tmRecordEndTime;
        time(&tmRecordEndTime);
        time_t duration = tmRecordEndTime - ptMediaFd->tmRecordStartTime;
        //x_InfoLog("Time[%lld--%lld],duration[%d].", ptMediaFd->tmRecordStartTime, tmRecordEndTime, duration);

        // added by shm, annotated
        // if(duration >= 300)
        // {
        //     return CreateRecordFile(ptMediaFd);
        // }
    }

    XPOC_TRACEFUNC_OUT;
    return XPOC_ERR_FAIL;
}

/* 收到floor消息,对于话权申请 create recordFile; 话权空闲 close recordFile. */
UINT HandleRecordFileForFloorMsg(PktNode_S *ptNode)
{
    XPOC_TRACEFUNC_IN;
    X_NULL_POINTER_CHK_ONE_PARA(ptNode);

    //x_InfoLog("deal rtcp param for record.");
    
    UCHAR *ptBuf = ptNode->PktBuf;
    x_DebugLog("rtcp buf[%x %x %x %x %x %x %x %x].", ptBuf[0],ptBuf[1],ptBuf[2],ptBuf[3],ptBuf[4],ptBuf[5],ptBuf[6],ptBuf[7]);
    x_DebugLog("rtcp buf8[%c %c %c %c].", ptBuf[8],ptBuf[9],ptBuf[10],ptBuf[11]);
    x_DebugLog("rtcp buf8[%x %x %x %x].", ptBuf[8],ptBuf[9],ptBuf[10],ptBuf[11]);

    MediaFdS *ptMediaFd = (MediaFdS*)ptNode->pNodeData;
    if(NULL == ptMediaFd)
    {
        x_ErrorLog("node data null.\n");
        return XPOC_ERR_FAIL;
    }
    x_DebugLog("ptMediaFd:UCCId[%d],mediatype[%d],grpspknum[%s],rcrdstate[%d],rcrdservtype[%d].",
            ptMediaFd->ulUCCId, ptMediaFd->iRceMediaType, ptMediaFd->aucGroupSpkNum, ptMediaFd->iRceRecordState, ptMediaFd->enRcrdServType);

    /* 收到话权创建文件时: 
       对于语音组呼, CCE--RCE之间只有一个录音通道, 在收到 floor_taken 时创建文件, spknum 从消息中解码得到. 所有的语音媒体都从该通道传给 RCE.
       对于半双工语音单呼, CCE--RCE之间对主叫和被叫分别有一个录音通道, 在收到floor_granted时创建文件, spknum 为该通道对应的servid. 该用户发出的语音媒体都从该通道传给 RCE. */
    if (strncmp((char*)(ptBuf + 8), "MCPT", 4) == 0) /* 音频话权 */
    {
        T_ModCtxS *ptModCtx = (T_ModCtxS*)Fplat_GetModuleCtx(XMOD_TYPE_RCE);
        RceGlbData_S* ptGlbData = (RceGlbData_S*)ptModCtx->pAppData;
        //获取音频通道
        MediaFdS *ptRtpFd = &(ptGlbData->stUdpAddrInfo.udpRtpFdList[ptMediaFd->recordIdx]);
    
        if((0x81 == ptBuf[0]) && (Serv_AudioHalfPri == ptMediaFd->enRcrdServType)) /* Floor Granted, 半双工单呼时创建文件 */
        {
			 T_RceUCallCtxTable stUCC;
            memset(&stUCC, 0, sizeof(T_RceUCallCtxTable));
            UINT ulReturn = XRceUCallCtxOpr(XDB_OPR_QRY, NULL, 0, (MediaTypeE)0, NULL, &ptMediaFd->ulUCCId, &stUCC);
            if (XPOC_ERR_SUCCESS != ulReturn)
            {
                x_ErrorLog("ucc data null.\n");
                return XPOC_ERR_FAIL;
            }

            if(Record_IDLE == ptRtpFd->iRceRecordState)
            {
                memcpy(ptRtpFd->aucGroupSpkNum, stUCC.aucServId, strlen(stUCC.aucServId));
                x_InfoLog("floor - caller [%s], spknum[%s].", stUCC.tCallInfoS.aucOrigNum, ptRtpFd->aucGroupSpkNum);
                CreateRecordFile(ptRtpFd);
                if(ptRtpFd->ubFirstProc) ptRtpFd->ubFirstProc = FALSE;
                //printf("CreateRecordFile: %s\n", ptRtpFd->szRecordFileName);
            }
            
        }
        else if((0x82 == ptBuf[0]) && (Serv_AudioGroup == ptMediaFd->enRcrdServType))/* Floor Taken, groupcall时创建文件 */
        {
            UCHAR len = *(ptBuf + 13);
            memset(ptRtpFd->aucGroupSpkNum, 0, STR_LEN32);
            memcpy(ptRtpFd->aucGroupSpkNum, ptBuf + 14, MIN(len, STR_LEN32));
            

            //避免迟后接入产生两个文件 add by lxh 20210414
            if (Record_IDLE == ptRtpFd->iRceRecordState)
            {
                T_RceUCallCtxTable stUCC;
                memset(&stUCC, 0, sizeof(T_RceUCallCtxTable));
                UINT ulReturn = XRceUCallCtxOpr(XDB_OPR_QRY, NULL, 0, (MediaTypeE)0, NULL, &ptMediaFd->ulUCCId, &stUCC);
                if (XPOC_ERR_SUCCESS != ulReturn)
                {
                    x_ErrorLog("ucc data null.\n");
                    return XPOC_ERR_FAIL;
                }
                x_InfoLog("floor - grp [%s], Spknum[%s].", stUCC.tCallInfoS.aucRcrdGrpNum, ptRtpFd->aucGroupSpkNum);

                strcpy(stUCC.tCallInfoS.aucSpkNum, ptRtpFd->aucGroupSpkNum);
                ulReturn = XRceUCallCtxOpr(XDB_OPR_MOD, NULL, 0, (MediaTypeE)0, NULL, &ptMediaFd->ulUCCId, &stUCC);
                CreateRecordFile(ptRtpFd);
                if(ptRtpFd->ubFirstProc) ptRtpFd->ubFirstProc = FALSE;
            }
        }
        else if(ptBuf[0] == 0x83)//Floor Deny  //申请话权拒绝
        {
        }
        else if(ptBuf[0] == 0x85)//Floor Idle//空闲
        {
            CloseRecordFile(ptRtpFd);
        }
        else if (ptBuf[0] == 0x86)//Floor Revoke
        {
        }
    }
    else if (strncmp((char*)(ptBuf + 8), "MCV0", 4) == 0)//视频话权
    {
    }

    XPOC_TRACEFUNC_OUT;
    return XPOC_ERR_SUCCESS;
}


#if 0
#endif

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

    UINT ulReturn = XPOC_ERR_SUCCESS;

    MediaFdS *ptMediaFd = (MediaFdS*)ptNode->pNodeData;
    if(NULL == ptMediaFd)
    {
        x_ErrorLog("node data null.\n");
        return XPOC_ERR_FAIL;
    }    

    // x_DebugLog("mediaFd:usedFg[%d],recordIdx[%d],usPort[%d],fdType[%d],iSocketFd[%d],ulUCCId[%d],rtpPort[%d],rtcpPort[%d].", 
    //     ptMediaFd->ubUsedFg,ptMediaFd->recordIdx,ptMediaFd->usPort,ptMediaFd->fdType,ptMediaFd->iSocketFd,ptMediaFd->ulUCCId,
    //     ptMediaFd->rtpCliPort,ptMediaFd->rtcpCliPort);

    /* rtcp包处理完毕后直接退出. 后面的是rtp包的处理 */
    if(FD_TYPE_UDP_RTCP == ptMediaFd->fdType) /*rtcp*/
    {
        (void)HandleRecordFileForFloorMsg(ptNode);
        return XPOC_ERR_SUCCESS;
    }
    else if(FD_TYPE_TCP_RTP == ptMediaFd->fdType)
    {
        if(ptMediaFd->ubClosingFd)
            return XPOC_ERR_SUCCESS;
    }

    /* 下面为处理 rtp包 的过程. audHalfPri|| audGrp record 不在收到媒体报文时创建文件. recv floor-granted 时创建. */
    x_DebugLog("mediaFd:uccid[%d],mediatype[%d],grpspknum[%s],rcrdstate[%d],rcrdservtype[%d].",
            ptMediaFd->ulUCCId, ptMediaFd->iRceMediaType, ptMediaFd->aucGroupSpkNum, ptMediaFd->iRceRecordState, ptMediaFd->enRcrdServType);
    if((Record_WORK == ptMediaFd->iRceRecordState))
    {
		if(MEDIA_AUDIO == ptMediaFd->iRceMediaType)
        	CreateNewRecordFileOrNot(ptMediaFd);
    }
    /*else if(Serv_AudioHalfPri != ptMediaFd->enRcrdServType && Serv_AudioGroup != ptMediaFd->enRcrdServType)
    {
        CreateRecordFile(ptMediaFd);
        printf("..CreateRecordFile: %s, %d\n", ptMediaFd->szRecordFileName, ptNode->BufLen);
    }*/
    else /* audHalfPri|| audGrp record 第一个录音文件任由rtp包创建 */
    {
        if (Serv_AudioHalfPri == ptMediaFd->enRcrdServType)
        {   
            if(ptMediaFd->ubFirstProc)
            {
                T_RceUCallCtxTable stUCC;
                memset(&stUCC, 0, sizeof(T_RceUCallCtxTable));
                ulReturn = XRceUCallCtxOpr(XDB_OPR_QRY, NULL, 0, (MediaTypeE)0, NULL, &ptMediaFd->ulUCCId, &stUCC);
                if (XPOC_ERR_SUCCESS != ulReturn)
                {
                    x_ErrorLog("ucc data null.\n");
                    return XPOC_ERR_FAIL;
                }
                if (0 != strcmp(stUCC.tCallInfoS.aucOrigNum, stUCC.tCallInfoS.aucRcrdNum)) //被叫
                {
                    x_ErrorLog("recv rtp from callee[%s] first, do no need create recordfile.", stUCC.tCallInfoS.aucRcrdNum);
                    return XPOC_ERR_FAIL;
                }
                else
                {
                    ptMediaFd->ubFirstProc = FALSE;
                }
            }
            else
            {
                x_ErrorLog("AudioHalfPri: ptMediaFd->ubFirstProc is false.\n");
                return XPOC_ERR_FAIL;
            }
        }
        //暂时添加,避免组呼时部分rtp消息迟后于rtcp导致产生多个文件  add by lxh 20210414
        else if (Serv_AudioGroup == ptMediaFd->enRcrdServType)
        {
            if(ptMediaFd->ubFirstProc)
            {
                ptMediaFd->ubFirstProc = FALSE;
            }
            else
            {
                x_ErrorLog("AudioGroup: ptMediaFd->ubFirstProc is false.\n");
                return XPOC_ERR_FAIL;
            }
        }

        CreateRecordFile(ptMediaFd);
    }

    if(Record_WORK != ptMediaFd->iRceRecordState)
    {
        x_ErrorLog("record file does not exist. recordState[%d].", ptMediaFd->iRceRecordState);
        return XPOC_ERR_FAIL;
    }

    if(FD_TYPE_UDP_RTP == ptMediaFd->fdType)
    {
        UCHAR* pAudioBuffer = (UCHAR*)(ptNode->PktBuf + RTP_HEADER_LEN);
        int iAudioBufferLen = ptNode->BufLen - RTP_HEADER_LEN;
        UINT ulTs = 0, ulTsLen = 0;
        Byte_2_U32(ptNode->PktBuf+4, ulTs, ulTsLen);
		if (AUDIOTYPE_AMR_NB == audio_record_type)
		{
            // ptMediaFd->tcpFdIdx为-1,表示没有视频文件,此时即使是视频单呼,也创建amr文件
            if (Serv_VideoPri == ptMediaFd->enRcrdServType && ptMediaFd->tcpFdIdx != -1) {
                T_ModCtxS *ptModCtx = (T_ModCtxS*)Fplat_GetModuleCtx(XMOD_TYPE_RCE);
                RceGlbData_S* ptGlbData = (RceGlbData_S*)ptModCtx->pAppData;
                MediaFdS *ptTcpFd = &ptGlbData->stTcpAddrInfo.tcpRtpFdList[ptMediaFd->tcpFdIdx];
                if(XPOC_ERR_FAIL == (ulReturn=WriteAudioDataToMp4File(ptTcpFd, pAudioBuffer, iAudioBufferLen, ulTs)))
                {
                    x_ErrorLog("WriteAudioDataToMp4File err...\n");
                }
            } else {
                ulReturn = WriteAMRNBMFAudioDataToFile(ptMediaFd->fd_record, pAudioBuffer, iAudioBufferLen);
            }
		}
		else if (AUDIOTYPE_AMR_WB == audio_record_type)
		{
			ulReturn = WriteAMRWBAudioDataToFile(ptMediaFd->fd_record, (char *)pAudioBuffer, iAudioBufferLen);
		}
		else if (AUDIOTYPE_G711 == audio_record_type)
		{
			ulReturn = WriteG711AudioDataToFile(ptMediaFd->fd_record, (char *)pAudioBuffer, iAudioBufferLen);
		}
		else
		{
            x_WarnLog("Unexpected audio_record_type[%d].\n", audio_record_type);
			ulReturn = XPOC_ERR_FAIL;
		}
        if (XPOC_ERR_SUCCESS != ulReturn)
        {
            x_ErrorLog("Write file [%s] failed.", ptMediaFd->szRecordFileName);
            return XPOC_ERR_FAIL;
        }
    }
    else if (FD_TYPE_TCP_RTP == ptMediaFd->fdType) /*rtp*/
    {
        /* ptMediaFd->iRecvBufferLen 保存的是可能存在的上一次分包后残留的最后一个非完整的包数据. */
        if((ptMediaFd->iRecvBufferLen+ptNode->BufLen) >= RCE_MAX_VIDEO_RECV_BUFFER_LEN)/* 如果数据包超长则不保存,丢弃. */
        {
            x_ErrorLog("handle tcp pkt:ptMediaFd->iRecvBufferLen[%d],ptNode->BufLen[%d].", ptMediaFd->iRecvBufferLen, ptNode->BufLen);
            return XPOC_ERR_FAIL;
        }

        memcpy(ptMediaFd->pRecvBuffer + ptMediaFd->iRecvBufferLen, ptNode->PktBuf, ptNode->BufLen);
        ptMediaFd->iRecvBufferLen += ptNode->BufLen;
        
PREbegin:
        //拆组包
        int iDrop = 0;
        for (iDrop = 0; (iDrop + 14) <= ptMediaFd->iRecvBufferLen; iDrop++)
        {
            unsigned char fl1 = (unsigned char)((ptMediaFd->pRecvBuffer + iDrop)[0]);
            unsigned char fl2 = (unsigned char)((ptMediaFd->pRecvBuffer + iDrop)[1]);
            unsigned char h1 = (unsigned char)((ptMediaFd->pRecvBuffer + iDrop)[2]);
            unsigned char h2 = (unsigned char)((ptMediaFd->pRecvBuffer + iDrop)[3]);
            
            if ((h1 & 0xEF) == 0x80 && (h2 & 0X7F) == 98)
            {
                UINT ulSsrc = 0;
                UCHAR  ucSsrcLen = 0;
                Byte_2_U32(ptMediaFd->pRecvBuffer+iDrop+10, ulSsrc, ucSsrcLen); 
                if (!ptMediaFd->ulSsrc) {
                    ptMediaFd->ulSsrc = ulSsrc;
                }

                int RtpLen = fl1 << 8 | fl2;
                if(RtpLen > 4000 || ptMediaFd->ulSsrc != ulSsrc) 
                {
                    iDrop += 3; // skip first fll,fl2 and h1,h2
                    continue;
                }

                if (iDrop + 2 + RtpLen + 14 <= ptMediaFd->iRecvBufferLen)
                {
                    fl1 = (unsigned char)((ptMediaFd->pRecvBuffer + iDrop + 2 + RtpLen)[0]);
                    fl2 = (unsigned char)((ptMediaFd->pRecvBuffer + iDrop + 2 + RtpLen)[1]);
                    h1 = (unsigned char)((ptMediaFd->pRecvBuffer + iDrop + 2 + RtpLen)[2]);
                    h2 = (unsigned char)((ptMediaFd->pRecvBuffer + iDrop + 2 + RtpLen)[3]);
                    Byte_2_U32(ptMediaFd->pRecvBuffer+iDrop+2+RtpLen+10, ulSsrc, ucSsrcLen); 

                    if ((h1 & 0xEF) == 0x80 && (h2 & 0X7F) == 98 && ulSsrc == ptMediaFd->ulSsrc) //通过看下包也是RTP来判断这一包是完全的
                    {
                        UCHAR *pPlayBuffer = (UCHAR*)(ptMediaFd->pRecvBuffer + iDrop + 2);

                        if (XPOC_ERR_SUCCESS == CheckNeedVideoSPS(ptMediaFd->waitVideoSPS, pPlayBuffer, RtpLen, ptMediaFd))
						{
                            if(ptMediaFd->waitVideoSPS)
                            {
                                ///
                                /*if(!ptMediaFd->pvMp4HFile)
                                {
                                    rceUptUserVideoStartTime2DB(ptMediaFd);
                                }*/

                                // set metadata
                                SetMp4VidAvcCfg(ptMediaFd->pvMp4HFile, ptMediaFd->ulMp4Vid, &ptMediaFd->stAvcCfg);
                                /*if(ptMediaFd->pFrameBuf)
                                {
                                    char prc[4] = {0,0,0,1};
                                    *((UINT*)prc) = htonl(ptMediaFd->stAvcCfg.ulSpsLen-RTP_HEADER_LEN);
                                    memcpy(ptMediaFd->pFrameBuf+ptMediaFd->ulFrameLen, prc, 4);
                                    ptMediaFd->ulFrameLen += 4;
                                    memcpy(ptMediaFd->pFrameBuf+ptMediaFd->ulFrameLen, ptMediaFd->stAvcCfg.ucSps+RTP_HEADER_LEN,  ptMediaFd->stAvcCfg.ulSpsLen-RTP_HEADER_LEN);
                                    ptMediaFd->ulFrameLen += ptMediaFd->stAvcCfg.ulSpsLen-RTP_HEADER_LEN;
                                    *((UINT*)prc) = htonl(ptMediaFd->stAvcCfg.ulPpsLen-RTP_HEADER_LEN);
                                    memcpy(ptMediaFd->pFrameBuf+ptMediaFd->ulFrameLen, prc, 4);
                                    ptMediaFd->ulFrameLen += 4;
                                    memcpy(ptMediaFd->pFrameBuf+ptMediaFd->ulFrameLen, ptMediaFd->stAvcCfg.ucPps+RTP_HEADER_LEN,  ptMediaFd->stAvcCfg.ulPpsLen-RTP_HEADER_LEN);
                                    ptMediaFd->ulFrameLen += ptMediaFd->stAvcCfg.ulPpsLen-RTP_HEADER_LEN;
                                    ptMediaFd->ulLastRtpTs = 0;
                                }*/

                                ptMediaFd->waitVideoSPS = 0;
                            }
                            
                            /
                            const int hdrLen = (pPlayBuffer[0]&0x10)?RTP_HEADER_LEN+RTP_HEADER_EXT_LEN:RTP_HEADER_LEN;
                            UCHAR tp = pPlayBuffer[hdrLen]&0x1f;
                            if(tp == 7)
                            {
                                //
                                ptMediaFd->stAvcCfg.ulSpsLen = RtpLen;
                                memcpy(ptMediaFd->stAvcCfg.ucSps, pPlayBuffer, ptMediaFd->stAvcCfg.ulSpsLen);
                            }
                            else if(tp == 8)
                            {
                                //
                                ptMediaFd->stAvcCfg.ulPpsLen = RtpLen;
                                memcpy(ptMediaFd->stAvcCfg.ucPps, pPlayBuffer, ptMediaFd->stAvcCfg.ulPpsLen);

                                // set metadata
                                //SetMp4VidAvcCfg(ptMediaFd->pvMp4HFile, ptMediaFd->ulMp4Vid, &ptMediaFd->stAvcCfg);
                            }
                            else
                            {
                                if(tp == 5 && XPOC_ERR_SUCCESS == CreateNewRecordFileOrNot(ptMediaFd))
                                {
                                    //
                                    ptMediaFd->ulMp4Vid = SetAddMp4VidTrack(ptMediaFd->pvMp4HFile, ptMediaFd->stAvcCfg.ucSps+hdrLen, ptMediaFd->stAvcCfg.ulSpsLen-hdrLen);
                                    if (ptMediaFd->ulMp4Vid == MP4_INVALID_TRACK_ID)
                                    {
                                        iDrop = ptMediaFd->iRecvBufferLen;
                                        break;
                                    }
                                    else
                                    {
                                        iDrop--;
                                        continue;
                                    }
                                }

                                ///
                                if(XPOC_ERR_FAIL == WriteVideoDataToMp4File(ptMediaFd, pPlayBuffer, RtpLen))
                                {
                                    x_ErrorLog("WriteVideoDataToMp4File err...\n");
                                    iDrop = ptMediaFd->iRecvBufferLen;
                                    break;
                                }
                            }
                            
                        }

                        ptMediaFd->iRecvBufferLen = ptMediaFd->iRecvBufferLen - iDrop - 2 - RtpLen;
                        memmove(ptMediaFd->pRecvBuffer, ptMediaFd->pRecvBuffer + iDrop + 2 + RtpLen, ptMediaFd->iRecvBufferLen);
                        //printf("..after skip rtp....ptMediaFd->iRecvBufferLen: %d....\n", ptMediaFd->iRecvBufferLen);
                        //memset(ptMediaFd->pRecvBuffer + ptMediaFd->iRecvBufferLen, 0, RCE_MAX_VIDEO_RECV_BUFFER_LEN - ptMediaFd->iRecvBufferLen);
                        goto PREbegin;
                    }
                    else
                    {
                        //printf("uncompleted data frame....RtpLen: %d..fl1: 0x%02x, fl2: 0x%02x, h1: 0x%02x, h2: 0x%02x..iDrop: %d..ptMediaFd->iRecvBufferLen: %d\n", RtpLen, fl1, fl2, h1, h2, iDrop, ptMediaFd->iRecvBufferLen);
                        iDrop += 3; // skip first fll,fl2 and h1,h2
                        ptMediaFd->waitVideoSPS = 1;
                        continue;
                    }
                }
                else
                {
                    ptMediaFd->iRecvBufferLen -= iDrop;
                    memmove(ptMediaFd->pRecvBuffer, ptMediaFd->pRecvBuffer + iDrop, ptMediaFd->iRecvBufferLen);
                    return XPOC_ERR_SUCCESS;
                }
            }

        }
        ptMediaFd->iRecvBufferLen -= iDrop;
        //printf("ptMediaFd->iRecvBufferLen: %d, iDrop: %d\n", ptMediaFd->iRecvBufferLen, iDrop);
        memmove(ptMediaFd->pRecvBuffer, ptMediaFd->pRecvBuffer + iDrop, ptMediaFd->iRecvBufferLen);
        //memset(ptMediaFd->pRecvBuffer + ptMediaFd->iRecvBufferLen, 0, RCE_MAX_VIDEO_RECV_BUFFER_LEN - ptMediaFd->iRecvBufferLen);
    }

    XPOC_TRACEFUNC_OUT;
    return XPOC_ERR_SUCCESS;
}


/*UINT rceDpSendPktOnly(PktNode_S *ptNode)
{  
    (void)ptNode;
    return XPOC_ERR_SUCCESS;
}*/

#if 0
#endif


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

    T_RceUCallCtxTable stUCC;
    memset(&stUCC, 0, sizeof(T_RceUCallCtxTable));
    UINT ulReturn = XRceUCallCtxOpr(XDB_OPR_QRY, NULL, 0, (MediaTypeE)0, NULL, &ptMediaFd->ulUCCId, &stUCC);
    if(XPOC_ERR_SUCCESS != ulReturn)
        return ulReturn;

    T_ModCtxS *ptModCtx = (T_ModCtxS*)Fplat_GetModuleCtx(XMOD_TYPE_RCE);
    RceGlbData_S* ptGlbData = (RceGlbData_S*)ptModCtx->pAppData;

    ThreadInfo_t *ptSendThread = NULL;
    PktQueue_S *ptQueue = 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("UCCId[%d],servId[%s],fdRtp[%d] ptSendThread is NULL.", 
                stUCC.ulRecordIdx, stUCC.aucServId, ptGlbData->stUdpAddrInfo.udpRtpFdList[stUCC.usRtpFdIdx].iSocketFd);
        return XPOC_ERR_FAIL;
    }
    ptQueue = ptSendThread->pPktQueue;
    if(!ptQueue)
    {
        x_ErrorLog("UCCId[%d],servId[%s],fdRtp[%d] ptQueue is NULL.", 
                stUCC.ulRecordIdx, stUCC.aucServId, ptGlbData->stUdpAddrInfo.udpRtpFdList[stUCC.usRtpFdIdx].iSocketFd);
        return XPOC_ERR_FAIL;
    }


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

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

    ptMediaFd->ulUCCIdbak = ptMediaFd->ulUCCId;    // add by lxh 20210419
    /* ready to send */
    rceRmIdxManageInsertToSendingList(&ptQueue->stIndexManageCtx, pstNodeIndex);


    if(FD_TYPE_TCP_RTP == ptMediaFd->fdType)
    {
        x_DebugLog("[PKT][TCP][RTP]pkt recv ok.NodIdx[%d],bufflen[%d],src[FD][%d:%d].UCCId[%d].", 
                   ptNode->ulPktNodeIdx, buffLen, ptMediaFd->recordIdx, ptMediaFd->iSocketFd, ptMediaFd->ulUCCId);

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

    XPOC_TRACEFUNC_OUT;
    return buffLen;
}

void *rceDpHandlePktFromQueue(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_ErrorLog("Thread type[%d] is invalid, threadId[%lu].\n",ptThread->enThreadType, ptThread->ThreadId);    
        return NULL;
    }
    x_DebugLog("[RCE][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( ; ; )
    {
        rceRmIdxManageGetSendNode(&ptQueue->stIndexManageCtx, &ptNodeIdx);
        if(!ptNodeIdx)/* no data to send */
        {
            usleep(1000);   /* FIXME: sleep 1ms is OK? */
            continue;
        }

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

        if(PROTO_UDP == ptThread->enThreadType)
        {
        }
        else if(PROTO_TCP == ptThread->enThreadType)
        {
            x_DebugLog("[PKT][TCP][RTP]pkt get from send queue. NodeCount[send:%d,free:%d].", 
                      ptQueue->stIndexManageCtx.stSendList.s32Count, ptQueue->stIndexManageCtx.stFreeList.s32Count);
        }

        // modified by shm, 增加读锁
        MediaFdS *ptMediaFd = (MediaFdS*)ptNode->pNodeData;
        pthread_rwlock_rdlock(&ptMediaFd->mtxUseFg);
        if (ptMediaFd->ubUsedFg && ptMediaFd->ulUCCId == ptMediaFd->ulUCCIdbak)
        {
            (void)rceDpDispatchPkt(ptNode);
        }
        pthread_rwlock_unlock(&ptMediaFd->mtxUseFg);
        
        /* this pPktNodeIndex is free now */
        ptNode->ulPktNodeIdx = 0;
        rceRmIdxManageInsertToFreeList(&ptQueue->stIndexManageCtx, ptNodeIdx);
    }

    XPOC_TRACEFUNC_OUT;
}


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

    UCHAR recvbuff[DP_BUFFER_SIZE];
    MediaFdS *ptUdpFd = NULL;

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

    struct epoll_event rcvevent[MAX_RTP_SD_NUM];

    T_ModCtxS *ptModCtx = (T_ModCtxS*)Fplat_GetModuleCtx(XMOD_TYPE_RCE);
    RceGlbData_S* ptGlbData = (RceGlbData_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;

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

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

            (void)rceDpSavePktIntoQueue(recvbuff, recvlen, ptUdpFd);
        }
    }

    XPOC_TRACEFUNC_OUT;
    return NULL;
}


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

    UCHAR recvbuff[DP_BUFFER_SIZE];
    MediaFdS *ptTcpFd = NULL;

    struct epoll_event rcvevent[MAX_RTP_SD_NUM];

    T_ModCtxS *ptModCtx = (T_ModCtxS*)Fplat_GetModuleCtx(XMOD_TYPE_RCE);
    RceGlbData_S* ptGlbData = (RceGlbData_S*)ptModCtx->pAppData;

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

    /* get the thread info */
    for ( ; ; )
    {
        int fdnum = epoll_wait(ptGlbData->stTcpAddrInfo.tcpEpollFd, 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;

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

            SINT recvlen = recv(ptTcpFd->iSocketFd, recvbuff, DP_BUFFER_SIZE, 0);
            if (recvlen <= 0)
            {
                if(recvlen < 0)
                    x_ErrorLog("Call recv() failure. SockFd=%d. RecvLen=%d.\n", ptTcpFd->iSocketFd, recvlen);
                //abort();
                continue;
            }

            (void)rceDpSavePktIntoQueue(recvbuff, recvlen, ptTcpFd);
        }        
    }

    XPOC_TRACEFUNC_OUT;
    return NULL;
}



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值