/***************************************************************************************
 *
 *  IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
 *
 *  By downloading, copying, installing or using the software you agree to this license.
 *  If you do not agree to this license, do not download, install, 
 *  copy or use the software.
 *
 *  Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
 *
 *  Redistribution and use in binary forms, with or without modification, are permitted.
 *
 *  Unless required by applicable law or agreed to in writing, software distributed 
 *  under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
 *  CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
 *  language governing permissions and limitations under the License.
 *
****************************************************************************************/

#include "sys_inc.h"
#include "rtp.h"
#include "word_analyse.h"
#include "rtsp_parse.h"
#include "rtsp_rcua.h"
#include "rfc_md5.h"
#include "base64.h"
#include "rtsp_util.h"
#include "http_cln.h"
#ifdef OVER_WEBSOCKET
#include "rtsp_ws.h"
#endif

/***********************************************************************/

void rcua_build_auth_line(RCUA * p_rua, HRTSP_MSG * tx_msg, const char * p_method)
{
	if (0 == p_rua->need_auth)
    {
    	return;
    }

    HD_AUTH_INFO * p_auth = &p_rua->auth_info;
    
    if (p_rua->auth_mode == 1)	// digest
    {
        int offset;
        int buflen;
        char buff[500] = {'\0'};
        
        http_calc_auth_digest(p_auth, p_method);

        offset = 0;
        buflen = sizeof(buff);
        
        offset += snprintf(buff+offset, buflen-offset, 
            "Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", "
            "uri=\"%s\", response=\"%s\"",
            p_auth->auth_name, p_auth->auth_realm, p_auth->auth_nonce, 
            p_auth->auth_uri, p_auth->auth_response);

        if (p_auth->auth_opaque_flag)
        {
            offset += snprintf(buff+offset, buflen-offset, 
                ", opaque=\"%s\"", p_auth->auth_opaque);
        }
        
        if (p_auth->auth_qop[0] != '\0')
        {
            offset += snprintf(buff+offset, buflen-offset, 
                ", qop=\"%s\", cnonce=\"%s\", nc=%s",
                p_auth->auth_qop, p_auth->auth_cnonce, p_auth->auth_ncstr);
        }

        if (p_auth->auth_algorithm[0] != '\0')
        {
            offset += snprintf(buff+offset, buflen-offset, 
                ", algorithm=%s", p_auth->auth_algorithm);
        }

        rtsp_add_tx_msg_line(tx_msg, "Authorization", "%s", buff);
	}
	else if (p_rua->auth_mode == 0) // basic
	{
		char buff[512] = {'\0'};
		char basic[512] = {'\0'};
		snprintf(buff, sizeof(buff), "%s:%s", p_auth->auth_name, p_auth->auth_pwd);
		base64_encode((uint8 *)buff, (int)strlen(buff), basic, sizeof(basic));
		rtsp_add_tx_msg_line(tx_msg, "Authorization", "Basic %s", basic);
	}
}

HRTSP_MSG * rcua_build_options(RCUA * p_rua)
{
	HRTSP_MSG * tx_msg = rtsp_get_msg_buf();
	if (tx_msg == NULL)
	{
		log_print(HT_LOG_ERR, "%s, rtsp_get_msg_buf return NULL!!!\r\n", __FUNCTION__);
		return NULL;
	}

	tx_msg->msg_type = 0;
	tx_msg->msg_sub_type = RTSP_MT_OPTIONS;

	rtsp_add_tx_msg_fline(tx_msg, "OPTIONS", "%s RTSP/1.0", p_rua->uri);

	rtsp_add_tx_msg_line(tx_msg, "CSeq", "%u", p_rua->cseq);

	rcua_build_auth_line(p_rua, tx_msg, "OPTIONS");    

	if (p_rua->sid[0] != '\0')
	{
		rtsp_add_tx_msg_line(tx_msg, "Session", "%s", p_rua->sid);
	}
	
	rtsp_add_tx_msg_line(tx_msg, "User-Agent", p_rua->user_agent);

	return tx_msg;
}

HRTSP_MSG * rcua_build_describe(RCUA * p_rua)
{
	HRTSP_MSG * tx_msg = rtsp_get_msg_buf();
	if (tx_msg == NULL)
	{
		log_print(HT_LOG_ERR, "%s, rtsp_get_msg_buf return NULL!!!\r\n", __FUNCTION__);
		return NULL;
	}

	tx_msg->msg_type = 0;
	tx_msg->msg_sub_type = RTSP_MT_DESCRIBE;

	rtsp_add_tx_msg_fline(tx_msg, "DESCRIBE", "%s RTSP/1.0", p_rua->uri);

	rtsp_add_tx_msg_line(tx_msg, "CSeq", "%u", p_rua->cseq);

	rcua_build_auth_line(p_rua, tx_msg, "DESCRIBE");
	
	rtsp_add_tx_msg_line(tx_msg, "Accept", "application/sdp");
	rtsp_add_tx_msg_line(tx_msg, "User-Agent", p_rua->user_agent);

#ifdef BACKCHANNEL
	if (p_rua->backchannel)
	{
		rtsp_add_tx_msg_line(tx_msg, "Require", "www.onvif.org/ver20/backchannel");
	}
#endif

	return tx_msg;
}

void rcua_build_setup_fline(HRTSP_MSG * tx_msg, const char * ctl, const char * uri)
{
    if (strncmp(ctl, "rtsp://", 7) == 0)
	{
		rtsp_add_tx_msg_fline(tx_msg, "SETUP", "%s RTSP/1.0", ctl);
	}	
	else	
	{
		int len = (int)strlen(uri);
		
		if (uri[len-1] == '/')
		{
			rtsp_add_tx_msg_fline(tx_msg, "SETUP", "%s%s RTSP/1.0", uri, ctl);
		}
		else
		{
			rtsp_add_tx_msg_fline(tx_msg, "SETUP", "%s/%s RTSP/1.0", uri, ctl);
		}
	}
}

HRTSP_MSG * rcua_build_setup(RCUA * p_rua, int av_t)
{
	HRTSP_MSG * tx_msg = rtsp_get_msg_buf();
	if (tx_msg == NULL)
	{
		log_print(HT_LOG_ERR, "%s, rtsp_get_msg_buf return NULL!!!\r\n", __FUNCTION__);
		return NULL;
	}

	tx_msg->msg_type = 0;
	tx_msg->msg_sub_type = RTSP_MT_SETUP;

    rcua_build_setup_fline(tx_msg, p_rua->channels[av_t].ctl, p_rua->uri);
	
	rtsp_add_tx_msg_line(tx_msg, "CSeq", "%u", p_rua->cseq);

	if (p_rua->sid[0] != '\0')
	{
		rtsp_add_tx_msg_line(tx_msg, "Session", "%s", p_rua->sid);
    }
    
	rcua_build_auth_line(p_rua, tx_msg, "SETUP");    

    if (p_rua->rtp_tcp) // TCP
	{
	    rtsp_add_tx_msg_line(tx_msg, "Transport", "RTP/AVP/TCP;unicast;interleaved=%u-%u", 
	        p_rua->channels[av_t].interleaved, p_rua->channels[av_t].interleaved+1);	
    }
    else if (p_rua->rtp_mcast) // rtp multicast
    {
        rtsp_add_tx_msg_line(tx_msg, "Transport", "RTP/AVP;multicast;port=%u-%u", 
            p_rua->channels[av_t].r_port, p_rua->channels[av_t].r_port+1);
    }
    else
    {
        rtsp_add_tx_msg_line(tx_msg, "Transport", "RTP/AVP;unicast;client_port=%u-%u", 
            p_rua->channels[av_t].l_port, p_rua->channels[av_t].l_port+1);
    }
	
	rtsp_add_tx_msg_line(tx_msg, "User-Agent", p_rua->user_agent);

#ifdef BACKCHANNEL
	if (av_t == AV_TYPE_BACKCHANNEL) // backchannel
	{
		rtsp_add_tx_msg_line(tx_msg, "Require", "www.onvif.org/ver20/backchannel");
	}
#endif

#ifdef REPLAY
    if (p_rua->replay)
    {
        rtsp_add_tx_msg_line(tx_msg, "Require", "onvif-replay");
    }
#endif

	return tx_msg;
}

HRTSP_MSG * rcua_build_play(RCUA * p_rua)
{
	HRTSP_MSG * tx_msg = rtsp_get_msg_buf();
	if (tx_msg == NULL)
	{
		log_print(HT_LOG_ERR, "%s, rtsp_get_msg_buf return NULL!!!\r\n", __FUNCTION__);
		return NULL;
	}

	tx_msg->msg_type = 0;
	tx_msg->msg_sub_type = RTSP_MT_PLAY;

	rtsp_add_tx_msg_fline(tx_msg, "PLAY", "%s RTSP/1.0", p_rua->uri);

	rtsp_add_tx_msg_line(tx_msg, "CSeq", "%u", p_rua->cseq);

	rcua_build_auth_line(p_rua, tx_msg, "PLAY");
	
	rtsp_add_tx_msg_line(tx_msg, "Session", "%s", p_rua->sid);

	rtsp_add_tx_msg_line(tx_msg, "Range", "npt=%0.3f-", p_rua->seek_pos/1000.0);

	rtsp_add_tx_msg_line(tx_msg, "User-Agent", p_rua->user_agent);

#ifdef BACKCHANNEL
	if (p_rua->backchannel) // backchannel
	{
		rtsp_add_tx_msg_line(tx_msg, "Require", "www.onvif.org/ver20/backchannel");
	}
#endif

#ifdef REPLAY
    if (p_rua->replay)
    {
        rtsp_add_tx_msg_line(tx_msg, "Require", "onvif-replay");

        if (p_rua->scale_flag)
        {
            rtsp_add_tx_msg_line(tx_msg, "Scale", "%8.2f", p_rua->scale / 100.0);
        }

        if (p_rua->rate_control_flag)
        {
            rtsp_add_tx_msg_line(tx_msg, "Rate-Control", "%s", p_rua->rate_control ? "yes" : "no");
        }

        if (p_rua->immediate_flag && p_rua->immediate)
        {
            rtsp_add_tx_msg_line(tx_msg, "Immediate", "yes");
        }

        if (p_rua->frame_flag)
        {
            if (1 == p_rua->frame)
            {
                rtsp_add_tx_msg_line(tx_msg, "Frames", "predicted");
            }
            else if (2 == p_rua->frame)
            {
                if (p_rua->frame_interval_flag && p_rua->frame_interval)
                {
                    rtsp_add_tx_msg_line(tx_msg, "Frames", "intra/%d", p_rua->frame_interval);
                }
                else
                {
                    rtsp_add_tx_msg_line(tx_msg, "Frames", "intra");
                }
            }
        }

		if (p_rua->range_flag && p_rua->replay_start > 0)
		{
			char range[256] = {'\0'};
			char start[32] = {'\0'}, end[32] = {'\0'};
		    struct tm *t1;	

			t1 = gmtime(&p_rua->replay_start);

			snprintf(start, sizeof(start)-1, "%04d%02d%02dT%02d%02d%02dZ",
				t1->tm_year+1900, t1->tm_mon+1, t1->tm_mday,
				t1->tm_hour, t1->tm_min, t1->tm_sec);

	        if (p_rua->replay_end > 0)
	        {
	            t1 = gmtime(&p_rua->replay_end);

				snprintf(end, sizeof(end)-1, "%04d%02d%02dT%02d%02d%02dZ",
					t1->tm_year+1900, t1->tm_mon+1, t1->tm_mday,
					t1->tm_hour, t1->tm_min, t1->tm_sec);

				snprintf(range, sizeof(range)-1, "clock=%s-%s", start, end);	
	        }
	        else
	        {
	            snprintf(range, sizeof(range)-1, "clock=%s-", start);
	        }
			
			rtsp_add_tx_msg_line(tx_msg, "Range", range);
		}
    }
#endif

	return tx_msg;
}

HRTSP_MSG * rcua_build_pause(RCUA * p_rua)
{
	HRTSP_MSG * tx_msg = rtsp_get_msg_buf();
	if (tx_msg == NULL)
	{
		log_print(HT_LOG_ERR, "%s::rtsp_get_msg_buf return NULL!!!\r\n", __FUNCTION__);
		return NULL;
	}

	tx_msg->msg_type = 0;
	tx_msg->msg_sub_type = RTSP_MT_PAUSE;

	rtsp_add_tx_msg_fline(tx_msg, "PAUSE", "%s RTSP/1.0", p_rua->uri);

	rtsp_add_tx_msg_line(tx_msg, "CSeq", "%u", p_rua->cseq);

	rcua_build_auth_line(p_rua, tx_msg, "PAUSE");
	
	rtsp_add_tx_msg_line(tx_msg, "Session", "%s", p_rua->sid);
	rtsp_add_tx_msg_line(tx_msg, "User-Agent", p_rua->user_agent);

	return tx_msg;
}

HRTSP_MSG * rcua_build_get_parameter(RCUA * p_rua)
{
	HRTSP_MSG * tx_msg = rtsp_get_msg_buf();
	if (tx_msg == NULL)
	{
		log_print(HT_LOG_ERR, "%s, rtsp_get_msg_buf return NULL!!!\r\n", __FUNCTION__);
		return NULL;
	}

	tx_msg->msg_type = 0;
	tx_msg->msg_sub_type = RTSP_MT_GET_PARAMETER;

	rtsp_add_tx_msg_fline(tx_msg, "GET_PARAMETER", "%s RTSP/1.0", p_rua->uri);

	rtsp_add_tx_msg_line(tx_msg, "CSeq", "%u", p_rua->cseq);

	rcua_build_auth_line(p_rua, tx_msg, "GET_PARAMETER");
	
	rtsp_add_tx_msg_line(tx_msg, "Session", "%s", p_rua->sid);
	rtsp_add_tx_msg_line(tx_msg, "User-Agent", p_rua->user_agent);

	return tx_msg;
}


HRTSP_MSG * rcua_build_teardown(RCUA * p_rua)
{
	HRTSP_MSG * tx_msg = rtsp_get_msg_buf();
	if (tx_msg == NULL)
	{
		log_print(HT_LOG_ERR, "%s, rtsp_get_msg_buf return NULL!!!\r\n", __FUNCTION__);
		return NULL;
	}

	tx_msg->msg_type = 0;
	tx_msg->msg_sub_type = RTSP_MT_TEARDOWN;

	rtsp_add_tx_msg_fline(tx_msg, "TEARDOWN", "%s RTSP/1.0", p_rua->uri);

	rtsp_add_tx_msg_line(tx_msg, "CSeq", "%u", p_rua->cseq);

	rcua_build_auth_line(p_rua, tx_msg, "TEARDOWN");
	
	rtsp_add_tx_msg_line(tx_msg, "Session", "%s", p_rua->sid);
	rtsp_add_tx_msg_line(tx_msg, "User-Agent", p_rua->user_agent);

#ifdef BACKCHANNEL
	if (p_rua->backchannel) // backchannel
	{
		rtsp_add_tx_msg_line(tx_msg, "Require", "www.onvif.org/ver20/backchannel");
	}
#endif

	return tx_msg;
}

BOOL rcua_get_media_info(RCUA * p_rua, HRTSP_MSG * rx_msg)
{
	if (rx_msg == NULL || p_rua == NULL)
	{
		return FALSE;
	}
	
	if (!rtsp_msg_with_sdp(rx_msg))
	{
	    return FALSE;
	}
	
	rtsp_get_remote_cap(rx_msg, "video", &(p_rua->channels[AV_VIDEO_CH].cap_count), 
	    p_rua->channels[AV_VIDEO_CH].cap, &p_rua->channels[AV_VIDEO_CH].r_port);
	rtsp_get_remote_cap_desc(rx_msg, "video", p_rua->channels[AV_VIDEO_CH].cap_desc);

#ifdef BACKCHANNEL
	if (p_rua->backchannel)
	{
		rtsp_get_remote_cap(rx_msg, "audio", &(p_rua->channels[AV_AUDIO_CH].cap_count), 
		    p_rua->channels[AV_AUDIO_CH].cap, &p_rua->channels[AV_AUDIO_CH].r_port, "recvonly");
		rtsp_get_remote_cap_desc(rx_msg, "audio", p_rua->channels[AV_AUDIO_CH].cap_desc, "recvonly");
		
		rtsp_get_remote_cap(rx_msg, "audio", &(p_rua->channels[AV_BACK_CH].cap_count), 
		    p_rua->channels[AV_BACK_CH].cap, &p_rua->channels[AV_BACK_CH].r_port, "sendonly");
		rtsp_get_remote_cap_desc(rx_msg, "audio", p_rua->channels[AV_BACK_CH].cap_desc, "sendonly");
	}
	else
#endif		
	{
		rtsp_get_remote_cap(rx_msg, "audio", &(p_rua->channels[AV_AUDIO_CH].cap_count), 
		    p_rua->channels[AV_AUDIO_CH].cap, &p_rua->channels[AV_AUDIO_CH].r_port);
		rtsp_get_remote_cap_desc(rx_msg, "audio", p_rua->channels[AV_AUDIO_CH].cap_desc);
	}

#ifdef METADATA
	rtsp_get_remote_cap(rx_msg, "application", &(p_rua->channels[AV_METADATA_CH].cap_count), 
	    p_rua->channels[AV_METADATA_CH].cap, &p_rua->channels[AV_METADATA_CH].r_port);
	rtsp_get_remote_cap_desc(rx_msg, "application", p_rua->channels[AV_METADATA_CH].cap_desc);
#endif

	return TRUE;
}

void rcua_send_rtsp_msg(RCUA * p_rua, HRTSP_MSG * tx_msg)
{
    int     slen;
	char  * tx_buf;
	int     offset = 0;
    int     buf_len;
	char    rtsp_tx_buffer[2048+RTSP_SND_RESV_SIZE];
    SOCKET  fd = -1;
    
	if (tx_msg == NULL)
	{
	    return;
	}

#ifdef OVER_HTTP
    if (p_rua->over_http)
    {
        fd = p_rua->rtsp_send.cfd;
    }
    else 
#endif
#ifdef OVER_WEBSOCKET
    if (p_rua->over_ws)
    {
        fd = p_rua->ws_http.cfd;
    }
    else
#endif
    fd = p_rua->fd;
    
    if (fd <= 0)
    {
        return;
    }

	tx_buf = rtsp_tx_buffer + RTSP_SND_RESV_SIZE;
    buf_len = sizeof(rtsp_tx_buffer) - RTSP_SND_RESV_SIZE;

	offset += snprintf(tx_buf+offset, buf_len-offset, "%s %s\r\n", tx_msg->first_line.header, tx_msg->first_line.value_string);

	HDRV * pHdrV = (HDRV *)pps_lookup_start(&(tx_msg->rtsp_ctx));
	while (pHdrV != NULL)
	{
		offset += snprintf(tx_buf+offset, buf_len-offset, "%s: %s\r\n", pHdrV->header, pHdrV->value_string);
		pHdrV = (HDRV *)pps_lookup_next(&(tx_msg->rtsp_ctx), pHdrV);
	}
	pps_lookup_end(&(tx_msg->rtsp_ctx));

	offset += snprintf(tx_buf+offset, buf_len-offset, "\r\n");

	if (tx_msg->sdp_ctx.node_num != 0)
	{
		pHdrV = (HDRV *)pps_lookup_start(&(tx_msg->sdp_ctx));
		while (pHdrV != NULL)
		{
			if (pHdrV->header[0] != '\0')
			{
				offset += snprintf(tx_buf+offset, buf_len-offset, "%s=%s\r\n", pHdrV->header, pHdrV->value_string);
			}	
			else
			{
				offset += snprintf(tx_buf+offset, buf_len-offset, "%s\r\n", pHdrV->value_string);
			}	

			pHdrV = (HDRV *)pps_lookup_next(&(tx_msg->sdp_ctx), pHdrV);
		}
		pps_lookup_end(&(tx_msg->sdp_ctx));
	}

	log_print(HT_LOG_DBG, "TX >> %s\r\n", tx_buf);

#ifdef OVER_HTTP
    if (p_rua->over_http)
    {
        char tx_buf_base64[4096];
        
        base64_encode((uint8 *)tx_buf, offset, tx_buf_base64, sizeof(tx_buf_base64));

        tx_buf = tx_buf_base64;
        offset = strlen(tx_buf_base64);

        slen = http_cln_tx(&p_rua->rtsp_send, tx_buf, offset);
	}
	else
#endif
#ifdef OVER_WEBSOCKET
    if (p_rua->over_ws)
    {
        int extra = rtsp_ws_encode_data((uint8 *)tx_buf, offset, 0x82, 1);
        
        tx_buf -= extra;
        offset += extra;

        slen = http_cln_tx(&p_rua->ws_http, tx_buf, offset);
    }
    else
#endif
    {
	    slen = send(fd, tx_buf, offset, 0);
	}
	
	if (slen != offset)
	{
		log_print(HT_LOG_ERR, "%s, slen=%d, offset=%d, send message failed!!!\r\n", 
		    __FUNCTION__, slen, offset);
	}
}

BOOL rcua_get_sdp_video_desc(RCUA * p_rua, const char * key, int * pt, char * p_sdp, int max_len)
{
    int payload_type = 0, i;
	int rtpmap_len = (int)strlen("a=rtpmap:");
    char key_str[64] = {'\0'};

    strncpy(key_str, key, sizeof(key_str)-1);
    
	for (i=0; i<MAX_AVN; i++)
	{
		char * ptr = p_rua->channels[AV_VIDEO_CH].cap_desc[i];
		if (memcmp(ptr, "a=rtpmap:", rtpmap_len) == 0)
		{
			char pt_buf[16];
			char code_buf[64] = {'\0'};
			int next_offset = 0;

			ptr += rtpmap_len;

			if (GetLineWord(ptr, 0, (int)strlen(ptr), pt_buf, sizeof(pt_buf), &next_offset, WORD_TYPE_NUM) == FALSE)
			{
				return FALSE;
			}
			
			GetLineWord(ptr, next_offset, (int)strlen(ptr)-next_offset, code_buf, sizeof(code_buf), &next_offset, WORD_TYPE_STRING);

			if (memcmp(uppercase(code_buf), uppercase(key_str), strlen(key_str)) == 0)
			{
				payload_type = atoi(pt_buf);
				break;
			}
		}
	}

	if (payload_type <= 0)
	{
		return FALSE;
	}
	
    if (pt)
    {
	    *pt = payload_type;
	}

	if (p_sdp == NULL)
	{
		return TRUE;
	}
	
	p_sdp[0] = '\0';

	char fmtp_buf[32];
	int fmtp_len = snprintf(fmtp_buf, sizeof(fmtp_buf), "a=fmtp:%d", payload_type);

	for (i=0; i<MAX_AVN; i++)
	{
		char * ptr = p_rua->channels[AV_VIDEO_CH].cap_desc[i];
		if (memcmp(ptr, fmtp_buf, fmtp_len) == 0)
		{
			ptr += rtpmap_len+1;
			strncpy(p_sdp, ptr, max_len);
			break;
		}
	}

	return TRUE;
}

BOOL rcua_get_sdp_audio_desc(RCUA * p_rua, const char * key, int * pt, char * p_sdp, int max_len)
{
    int payload_type = 0, i;
	int rtpmap_len = (int)strlen("a=rtpmap:");
    char key_str[64] = {'\0'};

    strncpy(key_str, key, sizeof(key_str)-1);
    
	for (i=0; i<MAX_AVN; i++)
	{
		char * ptr = p_rua->channels[AV_AUDIO_CH].cap_desc[i];
		if (memcmp(ptr, "a=rtpmap:", rtpmap_len) == 0)
		{
			char pt_buf[16];
			char code_buf[64] = {'\0'};
			int next_offset = 0;
			ptr += rtpmap_len;

			if (GetLineWord(ptr, 0, (int)strlen(ptr), pt_buf, sizeof(pt_buf), &next_offset, WORD_TYPE_NUM) == FALSE)
			{
				return FALSE;
			}
			
			GetLineWord(ptr, next_offset, (int)strlen(ptr)-next_offset, code_buf, sizeof(code_buf), &next_offset, WORD_TYPE_STRING);
			
			if (memcmp(uppercase(code_buf), uppercase(key_str), strlen(key_str)) == 0)
			{
				payload_type = atoi(pt_buf);
				break;
			}
		}
	}

	if (payload_type <= 0)
	{
		return FALSE;
	}
	
    if (pt)
    {
	    *pt = payload_type;
	}

	if (p_sdp == NULL)
	{
		return TRUE;
	}
	
	p_sdp[0] = '\0';

	char fmtp_buf[32];
	int fmtp_len = snprintf(fmtp_buf, sizeof(fmtp_buf), "a=fmtp:%d", payload_type);

	for (i=0; i<MAX_AVN; i++)
	{
		char * ptr = p_rua->channels[AV_AUDIO_CH].cap_desc[i];
		if (memcmp(ptr, fmtp_buf, fmtp_len) == 0)
		{
			ptr += rtpmap_len+1;
			strncpy(p_sdp, ptr, max_len);
			break;
		}
	}

	return TRUE;
}

BOOL rcua_get_sdp_h264_desc(RCUA * p_rua, int * pt, char * p_sdp, int max_len)
{
    return rcua_get_sdp_video_desc(p_rua, "H264/90000", pt, p_sdp, max_len);
}

BOOL rcua_get_sdp_h264_params(RCUA * p_rua, int * pt, char * p_sps_pps, int max_len)
{
    BOOL ret = FALSE;
    char sdp[1024] = {'\0'};
    
    if (rcua_get_sdp_h264_desc(p_rua, pt, sdp, sizeof(sdp)) == FALSE)
    {
        return FALSE;
    }
    
    char * p_substr = strstr(sdp, "sprop-parameter-sets=");
	if (p_substr != NULL)
	{
		p_substr += strlen("sprop-parameter-sets=");
		char * p_tmp = p_substr;

		while (*p_tmp != ' ' && *p_tmp != ';' && *p_tmp != '\0') 
		{
			p_tmp++;
		}
		
		int sps_base64_len = (int)(p_tmp - p_substr);
		if (sps_base64_len < max_len)
		{
			memcpy(p_sps_pps, p_substr, sps_base64_len);
			p_sps_pps[sps_base64_len] = '\0';
			ret = TRUE;
		}
	}

	return ret;
}

BOOL rcua_get_sdp_h265_desc(RCUA * p_rua, int * pt, char * p_sdp, int max_len)
{
    return rcua_get_sdp_video_desc(p_rua, "H265/90000", pt, p_sdp, max_len);
}

BOOL rcua_get_sdp_h265_params(RCUA * p_rua, int * pt, BOOL * donfield, char * p_vps, int vps_len, char * p_sps, int sps_len, char * p_pps, int pps_len)
{
    char sdp[1024] = {'\0'};
    
    if (rcua_get_sdp_h265_desc(p_rua, pt, sdp, sizeof(sdp)) == FALSE)
    {
        return FALSE;
    }
    
    char * p_substr = strstr(sdp, "sprop-depack-buf-nalus=");
	if (p_substr != NULL)
	{
		p_substr += strlen("sprop-depack-buf-nalus=");
		char * p_tmp = p_substr;

		while (*p_tmp != ' ' && *p_tmp != ';' && *p_tmp != '\0')
		{
			p_tmp++;
		}
		
		int len = (int)(p_tmp - p_substr);
		if (len > 0)
		{
			if (donfield)
			{
			    *donfield = (atoi(p_substr) > 0 ? TRUE : FALSE);
			}    
		}
		else
		{
			return FALSE;
		}
	}
	
	p_substr = strstr(sdp, "sprop-vps=");
	if (p_substr != NULL)
	{
		p_substr += strlen("sprop-vps=");
		char * p_tmp = p_substr;

		while (*p_tmp != ' ' && *p_tmp != ';' && *p_tmp != '\0')
		{
			p_tmp++;
		}
		
		int len = (int)(p_tmp - p_substr);
		if (len < vps_len)
		{
			memcpy(p_vps, p_substr, len);
			p_vps[len] = '\0';
		}
		else
		{
			return FALSE;
		}
	}

	p_substr = strstr(sdp, "sprop-sps=");
	if (p_substr != NULL)
	{
		p_substr += strlen("sprop-sps=");
		char * p_tmp = p_substr;

		while (*p_tmp != ' ' && *p_tmp != ';' && *p_tmp != '\0')
		{
			p_tmp++;
		}
		
		int len = (int)(p_tmp - p_substr);
		if (len < sps_len)
		{
			memcpy(p_sps, p_substr, len);
			p_sps[len] = '\0';
		}
		else
		{
			return FALSE;
		}
	}

	p_substr = strstr(sdp, "sprop-pps=");
	if (p_substr != NULL)
	{
		p_substr += strlen("sprop-pps=");
		char * p_tmp = p_substr;

		while (*p_tmp != ' ' && *p_tmp != ';' && *p_tmp != '\0')
		{
			p_tmp++;
		}
		
		int len = (int)(p_tmp - p_substr);
		if (len < pps_len)
		{
			memcpy(p_pps, p_substr, len);
			p_pps[len] = '\0';
		}
		else
		{
			return FALSE;
		}
	}

	return TRUE;
}

BOOL rcua_get_sdp_mp4_desc(RCUA * p_rua, int * pt, char * p_sdp, int max_len)
{
    return rcua_get_sdp_video_desc(p_rua, "MP4V-ES/90000", pt, p_sdp, max_len);
}

BOOL rcua_get_sdp_mp4_params(RCUA * p_rua, int * pt, char * p_cfg, int max_len)
{
    BOOL ret = FALSE;
    char sdp[1024] = {'\0'};
    
    if (rcua_get_sdp_mp4_desc(p_rua, pt, sdp, sizeof(sdp)) == FALSE)
    {
        return FALSE;
    }
    
    char * p_substr = strstr(sdp, "config=");
    if (p_substr != NULL)
    {
    	p_substr += strlen("config=");
    	char * p_tmp = p_substr;

    	while (*p_tmp != ' ' && *p_tmp != ';' && *p_tmp != '\0')
    	{
    		p_tmp++;
    	}
    	
    	int cfg_len = (int)(p_tmp - p_substr);
    	if (cfg_len < max_len)
    	{
    		memcpy(p_cfg, p_substr, cfg_len);
    		p_cfg[cfg_len] = '\0';
    		ret = TRUE;
    	}
    	else
    	{
    		ret = FALSE;
    	}
    }

    return ret;
}

BOOL rcua_get_sdp_aac_desc(RCUA * p_rua, int * pt, char * p_sdp, int max_len)
{
    return rcua_get_sdp_audio_desc(p_rua, "MPEG4-GENERIC", pt, p_sdp, max_len);
}

BOOL rcua_get_sdp_aac_params(RCUA * p_rua, int *pt, int *sizelength, int *indexlength, int *indexdeltalength, char * p_cfg, int max_len)
{
    char sdp[1024] = {'\0'};
    
    if (rcua_get_sdp_aac_desc(p_rua, pt, sdp, sizeof(sdp)) == FALSE)
    {
        return FALSE;
    }

    char * p_substr = strstr(sdp, "config=");
	if (p_substr != NULL)
	{
		p_substr += strlen("config=");
		char * p_tmp = p_substr;

		while (*p_tmp != ' ' && *p_tmp != ';' && *p_tmp != '\0')
		{
			p_tmp++;
		}
		
		int len = (int)(p_tmp - p_substr);
		if (len < max_len)
		{
			memcpy(p_cfg, p_substr, len);
			p_cfg[len] = '\0';
		}
		else
		{
		    return FALSE;
		}
	}

	p_substr = strstr(sdp, "sizelength=");
	if (p_substr != NULL)
	{
		p_substr += strlen("sizelength=");
		char * p_tmp = p_substr;

		while (*p_tmp != ' ' && *p_tmp != ';' && *p_tmp != '\0')
		{
			p_tmp++;
		}
		
		int len = (int)(p_tmp - p_substr);
		if (len > 0)
		{
			*sizelength = atoi(p_substr);
		}
	}

	p_substr = strstr(sdp, "indexlength=");
	if (p_substr != NULL)
	{
		p_substr += strlen("indexlength=");
		char * p_tmp = p_substr;

		while (*p_tmp != ' ' && *p_tmp != ';' && *p_tmp != '\0')
		{
			p_tmp++;
		}
		
		int len = (int)(p_tmp - p_substr);
		if (len > 0)
		{
			*indexlength = atoi(p_substr);
		}
	}

	p_substr = strstr(sdp, "indexdeltalength=");
	if (p_substr != NULL)
	{
		p_substr += strlen("indexdeltalength=");
		char * p_tmp = p_substr;

		while (*p_tmp != ' ' && *p_tmp != ';' && *p_tmp != '\0')
		{
			p_tmp++;
		}
		
		int len = (int)(p_tmp - p_substr);
		if (len > 0)
		{
			*indexdeltalength = atoi(p_substr);
		}
	}

    return TRUE;
}

SOCKET rcua_init_udp_connection(uint16 port)
{
    struct sockaddr_in addr;

    SOCKET fd = socket(AF_INET, SOCK_DGRAM, 0);
    if (fd <= 0)
    {
        log_print(HT_LOG_ERR, "%s, socket SOCK_DGRAM error!\n", __FUNCTION__);
        return -1;
    }

    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = htonl(INADDR_ANY);
    addr.sin_port = htons(port);

    int len = 1024*1024;
    if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (char*)&len, sizeof(int)))
    {
        log_print(HT_LOG_WARN, "%s, setsockopt SO_RCVBUF error!\r\n", __FUNCTION__);
    }

    if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) == -1)
    {
        log_print(HT_LOG_ERR, "%s, Bind udp socket fail, port = %d, error = %s\r\n", 
            __FUNCTION__, port, sys_os_get_socket_error());
        closesocket(fd);
        return -1;
    }

    return fd;
}

SOCKET rcua_init_mc_connection(uint16 port, char * destination)
{
    struct sockaddr_in addr;
    struct ip_mreq mcast;

    SOCKET fd = socket(AF_INET, SOCK_DGRAM, 0);
    if (fd <= 0)
    {
        log_print(HT_LOG_ERR, "%s, socket SOCK_DGRAM error!\n", __FUNCTION__);
        return -1;
    }

    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = get_default_if_ip();
    addr.sin_port = htons(port);

    /* reuse socket addr */
    int opt = 1;
    if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&opt, sizeof(opt))) 
    {  
        log_print(HT_LOG_WARN, "%s, setsockopt SO_REUSEADDR error!\r\n", __FUNCTION__);
    }

    int len = 1024*1024;
    if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (char*)&len, sizeof(int)))
    {
        log_print(HT_LOG_WARN, "%s, setsockopt SO_RCVBUF error!\r\n", __FUNCTION__);
    }

    if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) == -1)
    {
        log_print(HT_LOG_ERR, "%s, Bind udp socket fail, port = %d, error = %s\r\n", 
            __FUNCTION__, port, sys_os_get_socket_error());
        closesocket(fd);
        return -1;
    }

    int ttl = 255;
    setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, (char*)&ttl, sizeof(ttl));

    mcast.imr_multiaddr.s_addr = inet_addr(destination);
    mcast.imr_interface.s_addr = addr.sin_addr.s_addr;

    if (setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*)&mcast, sizeof(mcast)) < 0)
    {
        log_print(HT_LOG_ERR, "%s, setsockopt IP_ADD_MEMBERSHIP error!%s\r\n", 
            __FUNCTION__, sys_os_get_socket_error());
        closesocket(fd);
        return -1;
    }

    return fd;
}



