/***************************************************************************************
 *
 *  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 "rtsp_cln.h"
#include "mpeg4.h"
#include "mpeg4_rtp_rx.h"
#include "base64.h"
#include "rtsp_util.h"

#ifdef BACKCHANNEL
#include "rtsp_backchannel.h"
#if __WINDOWS_OS__
#include "audio_capture_win.h"
#elif defined(ANDROID)
#include "audio_capture_android.h"
#elif defined(IOS)
#include "audio_capture_mac.h"
#elif __LINUX_OS__
#include "audio_capture_linux.h"
#endif
#endif

#if defined(OVER_HTTP) || defined(OVER_WEBSOCKET)
#include "http_parse.h"
#include "http_cln.h"
#endif

/***************************************************************************************/
void * rtsp_tcp_rx_thread(void * argv)
{
	CRtspClient * pRtsp = (CRtspClient *)argv;

	pRtsp->tcp_rx_thread();

	return NULL;
}

void * rtsp_udp_rx_thread(void * argv)
{
	CRtspClient * pRtsp = (CRtspClient *)argv;

	pRtsp->udp_rx_thread();

	return NULL;
}

int video_data_cb(uint8 * p_data, int len, uint32 ts, uint32 seq, void * p_userdata)
{
	CRtspClient * pthis = (CRtspClient *)p_userdata;

	pthis->rtsp_video_data_cb(p_data, len, ts, seq);

	return 0;
}

int audio_data_cb(uint8 * p_data, int len, uint32 ts, uint32 seq, void * p_userdata)
{
	CRtspClient * pthis = (CRtspClient *)p_userdata;

	pthis->rtsp_audio_data_cb(p_data, len, ts, seq);

	return 0;
}

int metadata_data_cb(uint8 * p_data, int len, uint32 ts, uint32 seq, void * p_userdata)
{
	CRtspClient * pthis = (CRtspClient *)p_userdata;

	pthis->rtsp_meta_data_cb(p_data, len, ts, seq);

	return 0;
}

/***************************************************************************************/
CRtspClient::CRtspClient(void)
{
	m_pNotify = NULL;
	m_pUserdata = NULL;
	m_pVideoCB = NULL;
	m_pAudioCB = NULL;
	m_pRtcpCB = NULL;
#ifdef METADATA	
	m_pMetadataCB = NULL;
#endif	
	m_pMutex = sys_os_create_mutex();
	
    m_bRunning = TRUE;
	m_tcpRxTid = 0;
	m_udpRxTid = 0;

    m_nRxTimeout = 10;  // Data receiving timeout, default 10s timeout
    m_nConnTimeout = 5; // Connect timeout, default 5s timeout
    
#ifdef EPOLL
    m_ep_event_num = AV_MAX_CHS * 2 + 2;
    
    m_ep_fd = epoll_create(m_ep_event_num);
    if (m_ep_fd < 0)
    {
        log_print(HT_LOG_ERR, "%s, epoll_create failed\r\n", __FUNCTION__);
    }

    m_ep_events = (struct epoll_event *)malloc(sizeof(struct epoll_event) * m_ep_event_num);
    if (m_ep_events == NULL)
    {
        log_print(HT_LOG_ERR, "%s, malloc failed\r\n", __FUNCTION__);
    }
#endif

	memset(&h265rxi, 0, sizeof(H265RXI));
	memset(&aacrxi, 0, sizeof(AACRXI));
	memset(&metadatarxi, 0, sizeof(PCMRXI));

	set_default();
}

CRtspClient::~CRtspClient(void)
{
	rtsp_close();

#ifdef EPOLL
    if (m_ep_fd)
    {
        close(m_ep_fd);
        m_ep_fd = 0;        
    }

    if (m_ep_events)
    {
        free(m_ep_events);
        m_ep_events = NULL;
    }
#endif

	if (m_pMutex)
	{
		sys_os_destroy_sig_mutex(m_pMutex);
		m_pMutex = NULL;
	}
}

void CRtspClient::set_default()
{
    memset(&m_rua, 0, sizeof(RCUA));
    memset(&m_url, 0, sizeof(m_url));
    memset(&m_ip, 0, sizeof(m_ip));
    memset(&m_suffix, 0, sizeof(m_suffix));

    m_nWidth = m_nHeight = 0;
    m_nport = 554;
    m_rua.rtp_tcp = 1;  // default RTP over RTSP
	m_rua.session_timeout = 60;
	strcpy(m_rua.user_agent, "happytimesoft rtsp client");
    m_rua.video_init_ts = -1;
    m_rua.audio_init_ts = -1;
    
    m_rua.audio_spec = NULL;
    m_rua.audio_spec_len = 0;
        
    m_rua.video_codec = VIDEO_CODEC_NONE;
    m_rua.audio_codec = AUDIO_CODEC_NONE;
    m_rua.sample_rate = 8000;
    m_rua.audio_channels = 1;
    m_rua.bit_per_sample = 0;
    
#ifdef BACKCHANNEL
    m_rua.bc_audio_codec = AUDIO_CODEC_NONE;
    m_rua.bc_sample_rate = 0;
    m_rua.bc_channels = 0;
    m_rua.bc_bit_per_sample = 0;
#endif
}

BOOL CRtspClient::rtsp_client_start()
{
    int len;
    int timeout = m_nConnTimeout*1000;
    RCUA * p_rua = &m_rua;
    
    p_rua->fd = tcp_connect_timeout(get_address_by_name(p_rua->ripstr), p_rua->rport, timeout);
	if (p_rua->fd <= 0)
	{
	    log_print(HT_LOG_ERR, "%s, %s:%d connect fail!!!\r\n", 
	        __FUNCTION__, p_rua->ripstr, p_rua->rport);
		return FALSE;
	}

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

	m_rua.cseq = 1;
	m_rua.state = RCS_OPTIONS;

	HRTSP_MSG * tx_msg = rcua_build_options(&m_rua);
	if (tx_msg)
	{
		rcua_send_free_rtsp_msg(&m_rua, tx_msg);
	}

	return TRUE;
}

void CRtspClient::rtsp_send_h264_params(RCUA * p_rua)
{
	int pt;
	char sps[1000], pps[1000] = {'\0'};
	
	if (!rcua_get_sdp_h264_params(p_rua, &pt, sps, sizeof(sps)))
	{
		return;
	}

	char * ptr = strchr(sps, ',');
	if (ptr && ptr[1] != '\0')
	{
		*ptr = '\0';
		strcpy(pps, ptr+1);
	}

	uint8 sps_pps[1000];
	sps_pps[0] = 0x0;
	sps_pps[1] = 0x0;
	sps_pps[2] = 0x0;
	sps_pps[3] = 0x1;
	
	int len = base64_decode(sps, strlen(sps), sps_pps+4, sizeof(sps_pps)-4);
	if (len <= 0)
	{
		return;
	}

	sys_os_mutex_enter(m_pMutex);
	
	if (m_pVideoCB)
	{
		m_pVideoCB(sps_pps, len+4, 0, 0, m_pUserdata);
	}
	
	if (pps[0] != '\0')
	{		
		len = base64_decode(pps, strlen(pps), sps_pps+4, sizeof(sps_pps)-4);
		if (len > 0)
		{
			if (m_pVideoCB)
			{
				m_pVideoCB(sps_pps, len+4, 0, 0, m_pUserdata);
			}
		}
	}

	sys_os_mutex_leave(m_pMutex);
}

void CRtspClient::rtsp_send_h265_params(RCUA * p_rua)
{
	int pt;
	char vps[512] = {'\0'}, sps[512] = {'\0'}, pps[512] = {'\0'};
			
	if (!rcua_get_sdp_h265_params(p_rua, &pt, &h265rxi.rxf_don, vps, sizeof(vps)-1, sps, sizeof(sps)-1, pps, sizeof(pps)-1))
	{
		return;
	}

	uint8 buff[1024];
	buff[0] = 0x0;
	buff[1] = 0x0;
	buff[2] = 0x0;
	buff[3] = 0x1;

	sys_os_mutex_enter(m_pMutex);
	
	if (vps[0] != '\0')
	{
		int len = base64_decode(vps, strlen(vps), buff+4, sizeof(buff)-4);
		if (len <= 0)
		{
		    sys_os_mutex_leave(m_pMutex);
			return;
		}

		if (m_pVideoCB)
		{
			m_pVideoCB(buff, len+4, 0, 0, m_pUserdata);
		}
	}

	if (sps[0] != '\0')
	{
		int len = base64_decode(sps, strlen(sps), buff+4, sizeof(buff)-4);
		if (len <= 0)
		{
		    sys_os_mutex_leave(m_pMutex);
			return;
		}

		if (m_pVideoCB)
		{
			m_pVideoCB(buff, len+4, 0, 0, m_pUserdata);
		}
	}

	if (pps[0] != '\0')
	{
		int len = base64_decode(pps, strlen(pps), buff+4, sizeof(buff)-4);
		if (len <= 0)
		{
		    sys_os_mutex_leave(m_pMutex);
			return;
		}

		if (m_pVideoCB)
		{
			m_pVideoCB(buff, len+4, 0, 0, m_pUserdata);
		}
	}

	sys_os_mutex_leave(m_pMutex);
}

void CRtspClient::rtsp_get_mpeg4_config(RCUA * p_rua)
{
	int pt;
	char config[1000];
	
	if (!rcua_get_sdp_mp4_params(p_rua, &pt, config, sizeof(config)-1))
	{
		return;
	}

    uint32  configLen;
	uint8 * configData = mpeg4_parse_config(config, configLen);
    if (configData)
    {				
    	mpeg4rxi.hdr_len = configLen;
    	memcpy(mpeg4rxi.p_buf, configData, configLen);

        free(configData);   
    }
}

void CRtspClient::rtsp_get_aac_config(RCUA * p_rua)
{
	int pt = 0;
	int sizelength = 13;
	int indexlength = 3;
	int indexdeltalength = 3;
	char config[128];
	
	if (rcua_get_sdp_aac_params(p_rua, &pt, &sizelength, &indexlength, &indexdeltalength, config, sizeof(config)))
	{
		m_rua.audio_spec = mpeg4_parse_config(config, m_rua.audio_spec_len);
	}
	
	aacrxi.size_length = sizelength;
	aacrxi.index_length = indexlength;
	aacrxi.index_delta_length = indexdeltalength;
}


/***********************************************************************
*
* Close RCUA
*
************************************************************************/
void CRtspClient::rtsp_client_stop(RCUA * p_rua)
{
	if (p_rua->fd > 0)
	{
		HRTSP_MSG * tx_msg = rcua_build_teardown(p_rua);
		if (tx_msg)
		{
			rcua_send_free_rtsp_msg(p_rua,tx_msg);
		}	
	}
}

BOOL CRtspClient::rtsp_setup_channel(RCUA * p_rua, int av_t)
{
	HRTSP_MSG * tx_msg = NULL;
	
	p_rua->state = RCS_INIT_V + av_t;

	if (p_rua->rtp_tcp)
	{
	    p_rua->channels[av_t].interleaved = av_t * 2;
	}
	else if (p_rua->rtp_mcast)
	{
		if (p_rua->channels[av_t].r_port)
        {
        	p_rua->channels[av_t].l_port = p_rua->channels[av_t].r_port;
        }
        else
        {
            p_rua->channels[av_t].l_port = p_rua->channels[av_t].r_port = rtsp_get_udp_port();
        }
	}
	else
	{
	    p_rua->channels[av_t].l_port = rtsp_get_udp_port();

	    p_rua->channels[av_t].udp_fd = rcua_init_udp_connection(p_rua->channels[av_t].l_port);
		if (p_rua->channels[av_t].udp_fd <= 0)
		{
	   		return FALSE;
	    }

        p_rua->channels[av_t].rtcp_fd = rcua_init_udp_connection(p_rua->channels[av_t].l_port+1);
	    if (p_rua->channels[av_t].rtcp_fd <= 0)
	    {
	        log_print(HT_LOG_WARN, "%s, init rtcp udp connection failed\r\n", __FUNCTION__);
	    }

#ifdef EPOLL
        struct epoll_event event;
        event.events = EPOLLIN;
        event.data.u64 = m_rua.channels[av_t].udp_fd;
        epoll_ctl(m_ep_fd, EPOLL_CTL_ADD, m_rua.channels[av_t].udp_fd, &event);

        if (m_rua.channels[av_t].rtcp_fd > 0)
        {
            event.events = EPOLLIN;
            event.data.u64 = m_rua.channels[av_t].rtcp_fd;
            epoll_ctl(m_ep_fd, EPOLL_CTL_ADD, m_rua.channels[av_t].rtcp_fd, &event);
        }
#endif
	}
	
	tx_msg = rcua_build_setup(p_rua, av_t);
	if (tx_msg)
	{
		rcua_send_free_rtsp_msg(p_rua, tx_msg);
	}
	
	return TRUE;		
}

BOOL CRtspClient::rtsp_get_transport_info(RCUA * p_rua, HRTSP_MSG * rx_msg, int av_t)
{
	BOOL ret = FALSE;
	
	if (p_rua->rtp_tcp)
	{
	    ret = rtsp_get_tcp_transport_info(rx_msg, &p_rua->channels[av_t].interleaved);	
	}
	else if (p_rua->rtp_mcast)
	{
	    ret = rtsp_get_mc_transport_info(rx_msg, p_rua->channels[av_t].destination, &p_rua->channels[av_t].r_port);
	}
	else
	{
	    ret = rtsp_get_udp_transport_info(rx_msg, &p_rua->channels[av_t].l_port, &p_rua->channels[av_t].r_port);
    }

    return ret;
}

BOOL CRtspClient::rtsp_unauth_res(RCUA * p_rua, HRTSP_MSG * rx_msg)
{
    HRTSP_MSG * tx_msg = NULL;
    
    if (!p_rua->need_auth || p_rua->auth_retry < 3)
	{
	    p_rua->need_auth = TRUE;
	    
	    if (rtsp_get_digest_info(rx_msg, &p_rua->auth_info))
		{
		    p_rua->auth_mode = 1;
			snprintf(p_rua->auth_info.auth_uri, sizeof(p_rua->auth_info.auth_uri), "%s", p_rua->uri);                
		}
		else
		{
		    p_rua->auth_mode = 0;
		}

		p_rua->cseq++;
		p_rua->auth_retry++;

		switch (p_rua->state)
    	{
    	case RCS_OPTIONS:
    		tx_msg = rcua_build_options(p_rua);
    		break;
    		
    	case RCS_DESCRIBE:
    		tx_msg = rcua_build_describe(p_rua);
    		break;

    	case RCS_INIT_V:
    		tx_msg = rcua_build_setup(p_rua, AV_TYPE_VIDEO);
    		break;

    	case RCS_INIT_A:
    		tx_msg = rcua_build_setup(p_rua, AV_TYPE_AUDIO);
    		break;

#ifdef METADATA
    	case RCS_INIT_M:
    		tx_msg = rcua_build_setup(p_rua, AV_TYPE_METADATA);
    		break;
#endif

#ifdef BACKCHANNEL
    	case RCS_INIT_BC:
    		tx_msg = rcua_build_setup(p_rua, AV_TYPE_BACKCHANNEL);
    		break;
#endif

    	case RCS_READY:
    		tx_msg = rcua_build_play(p_rua);
    		break;
    	}
		
    	if (tx_msg)
    	{
    		rcua_send_free_rtsp_msg(p_rua, tx_msg);
    	}
    	else
    	{
	        return FALSE;
    	}
	}
	else
	{
	    send_notify(RTSP_EVE_AUTHFAILED);
	    return FALSE;
	}

	return TRUE;
}

BOOL CRtspClient::rtsp_options_res(RCUA * p_rua, HRTSP_MSG * rx_msg)
{
	HRTSP_MSG * tx_msg = NULL;
	
	if (rx_msg->msg_sub_type == 200)
	{
		// get supported command list
		p_rua->gp_cmd = rtsp_is_line_exist(rx_msg, "Public", "GET_PARAMETER");
		
		p_rua->cseq++;
		p_rua->state = RCS_DESCRIBE;
		
		tx_msg = rcua_build_describe(p_rua);
		if (tx_msg)
		{
			rcua_send_free_rtsp_msg(p_rua, tx_msg);
		}
	}
	else
	{
	    return FALSE;
	}

	return TRUE;
}

BOOL CRtspClient::rtsp_describe_res(RCUA * p_rua, HRTSP_MSG * rx_msg)
{
	if (rx_msg->msg_sub_type == 200)
	{
	    char cseq_buf[32];
		char cbase[256];
		
		// Session
		rtsp_get_session_info(rx_msg, p_rua->sid, sizeof(p_rua->sid)-1, &p_rua->session_timeout);

		rtsp_get_msg_cseq(rx_msg, cseq_buf, sizeof(cseq_buf)-1);

		// Content-Base			
		if (rtsp_get_cbase_info(rx_msg, cbase, sizeof(cbase)-1))
		{
			strncpy(p_rua->uri, cbase, sizeof(p_rua->uri)-1);
		}

		rtsp_get_sdp_range(rx_msg, &p_rua->play_start, &p_rua->play_end);

		rtsp_find_sdp_control(rx_msg, p_rua->channels[AV_VIDEO_CH].ctl, "video", sizeof(p_rua->channels[AV_VIDEO_CH].ctl)-1);

#ifdef BACKCHANNEL
		if (p_rua->backchannel)
		{
			p_rua->backchannel = rtsp_find_sdp_control(rx_msg, p_rua->channels[AV_BACK_CH].ctl, "audio", sizeof(p_rua->channels[AV_BACK_CH].ctl)-1, "sendonly");
			if (p_rua->backchannel)
			{
				rtsp_find_sdp_control(rx_msg, p_rua->channels[AV_AUDIO_CH].ctl, "audio", sizeof(p_rua->channels[AV_AUDIO_CH].ctl)-1, "recvonly");
			}
			else
			{
				rtsp_find_sdp_control(rx_msg, p_rua->channels[AV_AUDIO_CH].ctl, "audio", sizeof(p_rua->channels[AV_AUDIO_CH].ctl)-1);
			}
		}
		else
		{
			rtsp_find_sdp_control(rx_msg, p_rua->channels[AV_AUDIO_CH].ctl, "audio", sizeof(p_rua->channels[AV_AUDIO_CH].ctl)-1);
		}
#else
		rtsp_find_sdp_control(rx_msg, p_rua->channels[AV_AUDIO_CH].ctl, "audio", sizeof(p_rua->channels[AV_AUDIO_CH].ctl)-1);
#endif

#ifdef METADATA
		rtsp_find_sdp_control(rx_msg, p_rua->channels[AV_METADATA_CH].ctl, "application", sizeof(p_rua->channels[AV_METADATA_CH].ctl)-1);
#endif

		if (rcua_get_media_info(p_rua, rx_msg))
		{
			rtsp_get_video_media_info();
			rtsp_get_audio_media_info();

            if (VIDEO_CODEC_JPEG == m_rua.video_codec)
            {
                // Try to obtain the video size from the SDP based on a=x-dimensions
                rtsp_get_video_size(&m_nWidth, &m_nHeight);
            }
            
#ifdef BACKCHANNEL
			if (p_rua->backchannel)
			{
				rtsp_get_bc_media_info();
			}
#endif
		}

		if (p_rua->mcast_flag)
		{
			p_rua->rtp_mcast = rtsp_is_support_mcast(rx_msg);
		}

        p_rua->cseq++;

        for (int i = 0; i < AV_MAX_CHS; i++)
        {
            if (p_rua->channels[i].ctl[0] != '\0' && !p_rua->channels[i].disabled)
    		{
    			return rtsp_setup_channel(p_rua, i);
    		}
        }
		
	    return FALSE;
	}	
#ifdef BACKCHANNEL	
	else if (p_rua->backchannel) // the server don't support backchannel
	{
		p_rua->backchannel = 0;

		p_rua->cseq++;
		p_rua->state = RCS_DESCRIBE;
		
		HRTSP_MSG * tx_msg = rcua_build_describe(p_rua);
		if (tx_msg)
		{
			rcua_send_free_rtsp_msg(p_rua, tx_msg);
		}	
	}
#endif
    else
    {
        return FALSE;
    }
    
	return TRUE;
}

BOOL CRtspClient::rtsp_setup_res(RCUA * p_rua, HRTSP_MSG * rx_msg, int av_t)
{
	if (rx_msg->msg_sub_type == 200)
	{
	    int i;
		char cbase[256];
		HRTSP_MSG * tx_msg = NULL;
		
		// Content-Base			
		if (rtsp_get_cbase_info(rx_msg, cbase, sizeof(cbase)-1))
		{
			strncpy(p_rua->uri, cbase, sizeof(p_rua->uri)-1);
			snprintf(p_rua->auth_info.auth_uri, sizeof(p_rua->auth_info.auth_uri), "%s", p_rua->uri);
		}
		
		// Session
		if (p_rua->sid[0] == '\0')
		{
			rtsp_get_session_info(rx_msg, p_rua->sid, sizeof(p_rua->sid)-1, &p_rua->session_timeout);
        }

		if (!rtsp_get_transport_info(p_rua, rx_msg, av_t))
		{
			return FALSE;
		}
		
        if (p_rua->rtp_mcast)
		{
		    p_rua->channels[av_t].l_port = p_rua->channels[av_t].r_port;
		    
		    p_rua->channels[av_t].udp_fd = rcua_init_mc_connection(p_rua->channels[av_t].l_port, p_rua->channels[av_t].destination);
			if (p_rua->channels[av_t].udp_fd <= 0)
			{
		   		return FALSE;
		    }

            p_rua->channels[av_t].rtcp_fd = rcua_init_mc_connection(p_rua->channels[av_t].l_port+1, p_rua->channels[av_t].destination);
		    if (p_rua->channels[av_t].rtcp_fd <= 0)
			{
		   		log_print(HT_LOG_WARN, "%s, init rtcp multicast connection failed\r\n", __FUNCTION__);
		    }
		}
        
		if (p_rua->sid[0] == '\0')
		{
			snprintf(p_rua->sid, sizeof(p_rua->sid), "%x%x", rand(), rand());
		}

        p_rua->channels[av_t].setup = 1;
		p_rua->cseq++;

        for (i = av_t+1; i < AV_MAX_CHS; i++)
        {
            if (p_rua->channels[i].ctl[0] != '\0' && !p_rua->channels[i].disabled)
            {
                return rtsp_setup_channel(p_rua, i);
            }
        }

#ifdef BACKCHANNEL
        if (AV_BACK_CH == av_t)
        {
            p_rua->bc_rtp_info.rtp_cnt = rand() & 0xFFFF;
		    p_rua->bc_rtp_info.rtp_pt = p_rua->channels[AV_BACK_CH].cap[0];
		    p_rua->bc_rtp_info.rtp_ssrc = rand();
        }
#endif

	    if (make_prepare_play() == FALSE)
	    {
	        return FALSE;
	    }
	    
        p_rua->state = RCS_READY;
	    
		tx_msg = rcua_build_play(p_rua);
		if (tx_msg)
		{
			rcua_send_free_rtsp_msg(p_rua,tx_msg);
		}
	}
	else if (p_rua->rtp_tcp) // maybe the server don't support rtp over tcp, try rtp over udp
	{
		p_rua->rtp_tcp = 0;

        p_rua->cseq++;
        
	    if (!rtsp_setup_channel(p_rua, av_t))
		{
			return FALSE;
		}
	}
	else
	{
	    return FALSE;
	}

	return TRUE;
}

BOOL CRtspClient::rtsp_play_res(RCUA * p_rua, HRTSP_MSG * rx_msg)
{
	if (rx_msg->msg_sub_type == 200)
	{
        // Session
		rtsp_get_session_info(rx_msg, p_rua->sid, sizeof(p_rua->sid)-1, &p_rua->session_timeout);

        rtsp_get_rtp_info(rx_msg, p_rua->channels[AV_VIDEO_CH].ctl, &p_rua->video_init_ts, 
            p_rua->channels[AV_AUDIO_CH].ctl, &p_rua->audio_init_ts);

        if (p_rua->seek_pos > 0)
        {
            p_rua->media_start = p_rua->seek_pos;
            p_rua->seek_pos = 0;
        }
        
        if (p_rua->state == RCS_PLAYING)
        {
            return TRUE;
        }
        
        p_rua->state = RCS_PLAYING;
		p_rua->keepalive_time = sys_os_get_uptime();
		
		if (m_rua.audio_codec == AUDIO_CODEC_AAC)
		{
			rtsp_get_aac_config(p_rua);
		}
		
		send_notify(RTSP_EVE_CONNSUCC);

		if (m_rua.video_codec == VIDEO_CODEC_H264)
		{
			rtsp_send_h264_params(p_rua);
		}	
		else if (m_rua.video_codec == VIDEO_CODEC_MP4)
		{
			rtsp_get_mpeg4_config(p_rua);
		}
		else if (m_rua.video_codec == VIDEO_CODEC_H265)
		{
			rtsp_send_h265_params(p_rua);
		}

#ifdef BACKCHANNEL
		if (p_rua->backchannel)
		{
			rtsp_init_backchannel(p_rua);
		}
#endif		
	}
	else
	{
		//error handle
		return FALSE;
	}

	return TRUE;
}

#ifdef METADATA

void CRtspClient::set_metadata_cb(metadata_cb cb)
{
	sys_os_mutex_enter(m_pMutex);
	m_pMetadataCB = cb;
	sys_os_mutex_leave(m_pMutex);
}

#endif // METADATA

#ifdef BACKCHANNEL

int CRtspClient::get_bc_flag()
{
	return m_rua.backchannel;
}

void CRtspClient::set_bc_flag(int flag)
{
	m_rua.backchannel = flag;
}

int CRtspClient::get_bc_data_flag()
{
	if (m_rua.backchannel)
	{
		return m_rua.send_bc_data;
	}

	return -1; // unsupport backchannel
}

void CRtspClient::set_bc_data_flag(int flag)
{
	m_rua.send_bc_data = flag;
}

void CRtspClient::set_audio_device(int index)
{
    m_rua.bc_audio_device = index;
}

BOOL CRtspClient::rtsp_get_bc_media_info()
{
	if (m_rua.channels[AV_BACK_CH].cap_count == 0)
	{
		return FALSE;
	}

	if (m_rua.channels[AV_BACK_CH].cap[0] == 0)
	{
		m_rua.bc_audio_codec = AUDIO_CODEC_G711U;
	}
	else if (m_rua.channels[AV_BACK_CH].cap[0] == 8)
	{
		m_rua.bc_audio_codec = AUDIO_CODEC_G711A;
	}
	else if (m_rua.channels[AV_BACK_CH].cap[0] == 9)
	{
		m_rua.bc_audio_codec = AUDIO_CODEC_G722;
	}

	int i;
	int rtpmap_len = (int)strlen("a=rtpmap:");

	for (i=0; i<MAX_AVN; i++)
	{
		char * ptr = m_rua.channels[AV_BACK_CH].cap_desc[i];
		if (memcmp(ptr, "a=rtpmap:", rtpmap_len) == 0)
		{
			char pt_buf[16];
			char code_buf[64];
			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);

			uppercase(code_buf);

			if (strstr(code_buf, "G726"))
			{
				m_rua.bc_audio_codec = AUDIO_CODEC_G726;
				m_rua.bc_bit_per_sample = atoi(code_buf+5) / 8; // G726-16, G726-24,G726-32,G726-48

				if (m_rua.bc_bit_per_sample == 0)
				{
				    m_rua.bc_bit_per_sample = 2;
				}
			}
			else if (strstr(code_buf, "G722"))
			{
				m_rua.bc_audio_codec = AUDIO_CODEC_G722;
			}
			else if (strstr(code_buf, "PCMU"))
			{
				m_rua.bc_audio_codec = AUDIO_CODEC_G711U;
			}
			else if (strstr(code_buf, "PCMA"))
			{
				m_rua.bc_audio_codec = AUDIO_CODEC_G711A;
			}
			else if (strstr(code_buf, "MPEG4-GENERIC"))
			{
				m_rua.bc_audio_codec = AUDIO_CODEC_AAC;
			}
			else if (strstr(code_buf, "OPUS"))
			{
				m_rua.bc_audio_codec = AUDIO_CODEC_OPUS;
			}

			char * p = strchr(code_buf, '/');
			if (p)
			{
				p++;
				
				char * p1 = strchr(p, '/');
				if (p1)
				{
					*p1 = '\0';
					m_rua.bc_sample_rate = atoi(p);

					p1++;
					if (p1 && *p1 != '\0')
					{
						m_rua.bc_channels = atoi(p1);
					}
					else
					{
						m_rua.bc_channels = 1;
					}
				}
				else
				{
					m_rua.bc_sample_rate = atoi(p);
					m_rua.bc_channels = 1;
				}
			}

			break;
		}
	}

    if (m_rua.bc_audio_codec == AUDIO_CODEC_G722)
    {
        m_rua.bc_sample_rate = 16000;
        m_rua.bc_channels = 1;
    }
    
    if (m_rua.bc_audio_codec == AUDIO_CODEC_OPUS)
    {
        m_rua.bc_sample_rate = 48000;
    }
    
	return TRUE;
}

BOOL CRtspClient::rtsp_init_backchannel(RCUA * p_rua)
{
    int count;
    
#if __WINDOWS_OS__
    count = CWAudioCapture::getDeviceNums();
	if (m_rua.bc_audio_device >= count)
	{
		return FALSE;
	}
	
	p_rua->audio_captrue = CWAudioCapture::getInstance(m_rua.bc_audio_device);
#elif defined(ANDROID)
    count = CAAudioCapture::getDeviceNums();
	if (m_rua.bc_audio_device >= count)
	{
		return FALSE;
	}
	
	p_rua->audio_captrue = CAAudioCapture::getInstance(m_rua.bc_audio_device);
#elif defined(IOS)
    count = CMAudioCapture::getDeviceNums();
	if (m_rua.bc_audio_device >= count)
	{
		return FALSE;
	}
	
	p_rua->audio_captrue = CMAudioCapture::getInstance(m_rua.bc_audio_device);
#elif __LINUX_OS__
    count = CLAudioCapture::getDeviceNums();
	if (m_rua.bc_audio_device >= count)
	{
		return FALSE;
	}
	
	p_rua->audio_captrue = CLAudioCapture::getInstance(m_rua.bc_audio_device);
#endif

	if (NULL == p_rua->audio_captrue)
	{
		return FALSE;
	}
	
	p_rua->audio_captrue->addCallback(rtsp_bc_cb, p_rua);
	p_rua->audio_captrue->initCapture(m_rua.bc_audio_codec, m_rua.bc_sample_rate, m_rua.bc_channels, m_rua.bc_bit_per_sample * 8);
	
	return p_rua->audio_captrue->startCapture();
}

#endif // BACKCHANNEL

#ifdef REPLAY

int CRtspClient::get_replay_flag()
{
    return m_rua.replay;
}

void CRtspClient::set_replay_flag(int flag)
{
    m_rua.replay = flag;
}

void CRtspClient::set_scale(double scale)
{
    if (scale != 0)
    {
        m_rua.scale_flag = 1;
        m_rua.scale = (int)(scale * 100);
    }
    else
    {
        m_rua.scale_flag = 0;
        m_rua.scale = 0;
    }
}

void CRtspClient::set_rate_control_flag(int flag)
{
    m_rua.rate_control_flag = 1;
    m_rua.rate_control = flag;
}

void CRtspClient::set_immediate_flag(int flag)
{
    m_rua.immediate_flag = 1;
    m_rua.immediate = flag;
}

void CRtspClient::set_frames_flag(int flag, int interval)
{
    if (flag == 1 || flag == 2)
    {
        m_rua.frame_flag = 1;
        m_rua.frame = flag;
    }
    else
    {
        m_rua.frame_flag = 0;
        m_rua.frame = 0;
    }

    if (interval > 0)
    {
        m_rua.frame_interval_flag = 1;
        m_rua.frame_interval = interval;
    }
    else
    {
        m_rua.frame_interval_flag = 0;
        m_rua.frame_interval = 0;
    }
}

void CRtspClient::set_replay_range(time_t start, time_t end)
{
	m_rua.range_flag = 1;
	m_rua.replay_start = start;
	m_rua.replay_end = end;
}

#endif // REPLAY

#ifdef OVER_HTTP

void CRtspClient::set_rtsp_over_http(int flag, int port)
{
    if (flag)
    {
        m_rua.mcast_flag = 0;
        m_rua.rtp_tcp = 1;
    }
    
    m_rua.over_http = flag;

    if (port > 0 && port <= 65535)
    {
        m_rua.http_port = port;
    }
}

int CRtspClient::rtsp_build_http_get_req(void * p_user, char * bufs, int buflen, char * cookie)
{
    int offset = 0;
    HTTPREQ * p_req = (HTTPREQ *) p_user;
    
    offset += snprintf(bufs+offset, buflen-offset, "GET %s HTTP/1.1\r\n", p_req->url);
	offset += snprintf(bufs+offset, buflen-offset, "Host: %s:%u\r\n", p_req->host, p_req->port);
	offset += snprintf(bufs+offset, buflen-offset, "User-Agent: Happytimesoft rtsp client\r\n");
	offset += snprintf(bufs+offset, buflen-offset, "x-sessioncookie: %s\r\n", cookie);
	offset += snprintf(bufs+offset, buflen-offset, "Accept: application/x-rtsp-tunnelled\r\n");
	offset += snprintf(bufs+offset, buflen-offset, "Pragma: no-cache\r\n");
	offset += snprintf(bufs+offset, buflen-offset, "Cache-Control: no-cache\r\n");

	if (p_req->need_auth)
    {
    	offset += http_build_auth_msg(p_req, "GET", bufs+offset, buflen-offset);        
	}
	
	offset += snprintf(bufs+offset, buflen-offset, "\r\n");

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

    return offset;
}

int CRtspClient::rtsp_build_http_post_req(void * p_user, char * bufs, int buflen, char * cookie)
{
    int offset = 0;
    HTTPREQ * p_req = (HTTPREQ *) p_user;
    
    offset += snprintf(bufs+offset, buflen-offset, "POST %s HTTP/1.1\r\n", p_req->url);
	offset += snprintf(bufs+offset, buflen-offset, "Host: %s:%u\r\n", p_req->host, p_req->port);
	offset += snprintf(bufs+offset, buflen-offset, "User-Agent: ANS rtsp client\r\n");
	offset += snprintf(bufs+offset, buflen-offset, "x-sessioncookie: %s\r\n", cookie);
	offset += snprintf(bufs+offset, buflen-offset, "Content-Type: application/x-rtsp-tunnelled\r\n");
	offset += snprintf(bufs+offset, buflen-offset, "Pragma: no-cache\r\n");
	offset += snprintf(bufs+offset, buflen-offset, "Cache-Control: no-cache\r\n");
	offset += snprintf(bufs+offset, buflen-offset, "Content-Length: 32767\r\n");

	if (p_req->need_auth)
    {
    	offset += http_build_auth_msg(p_req, "POST", bufs+offset, buflen-offset);        
	}
	
	offset += snprintf(bufs+offset, buflen-offset, "\r\n");

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

    return offset;
}

BOOL CRtspClient::rtsp_over_http_start()
{
    int len;
    int offset = 0;
    int timeout = m_nConnTimeout*1000;
    char buff[2048];
    char cookie[100];
    HTTPREQ * p_http;
    static int cnt = 1;
    HRTSP_MSG * tx_msg;

    srand((uint32)time(NULL)+cnt++);
    snprintf(cookie, sizeof(cookie), "%x%x%x", rand(), rand(), sys_os_get_ms());

RETRY:

    p_http = &m_rua.rtsp_recv;

    if (m_nport != 554)
    {
        p_http->port = m_nport; // Port from the stream address
    }
    else
    {
        p_http->port = m_rua.http_port; // Configured RTSP OVER HTTP port
    }
    
    strcpy(p_http->host, m_ip);
    strcpy(p_http->url, m_suffix);

    if (memcmp(m_url, "https://", 8) == 0)
    {
        p_http->https = 1;
    }

    // First try the stream address port, 
    // If the connection fails, try to connect to the configured port
    
    p_http->cfd = tcp_connect_timeout(get_address_by_name(p_http->host), p_http->port, timeout);
	if (p_http->cfd <= 0)
	{
	    if (p_http->port == m_rua.http_port)
	    {
	        log_print(HT_LOG_ERR, "%s, %s:%d connect failed\r\n", 
	            __FUNCTION__, p_http->host, p_http->port);
    	    goto FAILED;
	    }
	    
	    p_http->port = m_rua.http_port;
	    p_http->cfd = tcp_connect_timeout(get_address_by_name(p_http->host), p_http->port, timeout);
	    if (p_http->cfd <= 0)
	    {
    	    log_print(HT_LOG_ERR, "%s, %s:%d connect failed\r\n", 
	            __FUNCTION__, p_http->host, p_http->port);
    	    goto FAILED;
	    }
	}

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

    if (p_http->https)
    {
#ifdef HTTPS    
        if (!http_cln_ssl_conn(p_http, timeout))
        {
            goto FAILED;
        }
#else
        log_print(HT_LOG_ERR, "%s, the server require ssl connection, unsupport!\r\n", __FUNCTION__);
		goto FAILED;
#endif
    }

    offset = rtsp_build_http_get_req(p_http, buff, sizeof(buff), cookie);
    
	if (http_cln_tx(p_http, buff, offset) <= 0)
	{
	    log_print(HT_LOG_ERR, "%s, http_cln_tx failed\r\n", __FUNCTION__);
	    goto FAILED;
	}

	if (!http_cln_rx_timeout(p_http, timeout))
	{
	    log_print(HT_LOG_ERR, "%s, http_cln_rx_timeout failed\r\n", __FUNCTION__);
	    goto FAILED;
	}

    if (p_http->rx_msg->msg_sub_type == 401)
    {
        if (p_http->need_auth == FALSE)
        {
            http_cln_auth_set(p_http, m_rua.auth_info.auth_name, m_rua.auth_info.auth_pwd);

            http_cln_free_req(p_http);

            goto RETRY;
        }

        send_notify(RTSP_EVE_AUTHFAILED);
        goto FAILED;
    }
    else if (p_http->rx_msg->msg_sub_type != 200 || p_http->rx_msg->ctt_type != CTT_RTSP_TUNNELLED)
    {
        log_print(HT_LOG_ERR, "%s, msg_sub_type=%d, ctt_type=%d\r\n", 
            __FUNCTION__, p_http->rx_msg->msg_sub_type, p_http->rx_msg->ctt_type);
        goto FAILED;
    }

    p_http = &m_rua.rtsp_send;

	p_http->https = m_rua.rtsp_recv.https;
    p_http->port =  m_rua.rtsp_recv.port;
    strcpy(p_http->host, m_ip);
    strcpy(p_http->url, m_suffix);

	p_http->cfd = tcp_connect_timeout(get_address_by_name(p_http->host), p_http->port, timeout);
	if (p_http->cfd <= 0)
	{
	    log_print(HT_LOG_ERR, "%s, %s:%d connect failed\r\n", 
	        __FUNCTION__, p_http->host, p_http->port);
	    goto FAILED;
	}

#ifdef HTTPS
    if (p_http->https)
    {
        if (!http_cln_ssl_conn(p_http, timeout))
        {
            goto FAILED;
        }
    }
#endif

    offset = rtsp_build_http_post_req(p_http, buff, sizeof(buff), cookie);
    
	if (http_cln_tx(p_http, buff, offset) <= 0)
	{
	    log_print(HT_LOG_ERR, "%s, http_cln_tx failed\r\n", __FUNCTION__);
	    goto FAILED;
	}

    m_rua.cseq = 1;
    m_rua.rtp_tcp = 1;
    m_rua.state = RCS_OPTIONS;
    
	tx_msg = rcua_build_options(&m_rua);
	if (tx_msg)
	{
		rcua_send_free_rtsp_msg(&m_rua, tx_msg);
	}
	
	return TRUE;

FAILED:

    http_cln_free_req(&m_rua.rtsp_recv);
    http_cln_free_req(&m_rua.rtsp_send);

	return FALSE;
}

int CRtspClient::rtsp_over_http_rx(RCUA * p_rua)
{
    int rlen;
    SOCKET fd = -1;
#ifdef HTTPS    
    int https = 0;
#endif

    fd = p_rua->rtsp_recv.cfd;
    if (fd <= 0)
	{
		return -1;
	}

#ifdef HTTPS
    if (p_rua->rtsp_recv.https && p_rua->rtsp_recv.ssl)
    {
        https = 1;
    }
    
    if (https && SSL_pending(p_rua->rtsp_recv.ssl) > 0)
	{
	    // There is data to read 
	}
	else
#endif
    {
#ifndef EPOLL    
        fd_set fdread;
        struct timeval tv = {1, 0};

        FD_ZERO(&fdread);
        FD_SET(fd, &fdread);
    
        int sret = select((int)(fd+1), &fdread, NULL, NULL, &tv); 
        if (sret == 0) // Time expired 
        { 
            return RTSP_RX_TIMEOUT; 
        }
        else if (!FD_ISSET(fd, &fdread))
        {
            return RTSP_RX_TIMEOUT;
        }
#endif        
    }

	if (p_rua->rtp_rcv_buf == NULL || p_rua->rtp_t_len == 0)
	{
#ifdef HTTPS
        if (https)
        {
            rlen = SSL_read(p_rua->rtsp_recv.ssl, p_rua->rcv_buf+p_rua->rcv_dlen, 2048-p_rua->rcv_dlen);
        }
        else
#endif
        rlen = recv(fd, p_rua->rcv_buf+p_rua->rcv_dlen, 2048-p_rua->rcv_dlen, 0);
		if (rlen <= 0)
		{
			log_print(HT_LOG_WARN, "%s, ret = %d, err = %s\r\n", 
			    __FUNCTION__, rlen, sys_os_get_socket_error());	//recv error, connection maybe disconn?
			return RTSP_RX_FAIL;
		}

		p_rua->rcv_dlen += rlen;

		if (p_rua->rcv_dlen < 4)
		{
			return RTSP_RX_SUCC;
		}
	}
	else
	{
#ifdef HTTPS
        if (https)
        {
            rlen = SSL_read(p_rua->rtsp_recv.ssl, p_rua->rtp_rcv_buf+p_rua->rtp_rcv_len, p_rua->rtp_t_len-p_rua->rtp_rcv_len);
        }
        else
#endif
        rlen = recv(fd, p_rua->rtp_rcv_buf+p_rua->rtp_rcv_len, p_rua->rtp_t_len-p_rua->rtp_rcv_len, 0);
		if (rlen <= 0)
		{
			log_print(HT_LOG_WARN, "%s, ret = %d, err = %s\r\n", 
			    __FUNCTION__, rlen, sys_os_get_socket_error());	// recv error, connection maybe disconn?
			return RTSP_RX_FAIL;
		}

		p_rua->rtp_rcv_len += rlen;
		
		if (p_rua->rtp_rcv_len == p_rua->rtp_t_len)
		{
			tcp_data_rx((uint8*)p_rua->rtp_rcv_buf, p_rua->rtp_rcv_len);
			
			free(p_rua->rtp_rcv_buf);
			p_rua->rtp_rcv_buf = NULL;
			p_rua->rtp_rcv_len = 0;
			p_rua->rtp_t_len = 0;
		}
		
		return RTSP_RX_SUCC;
	}

	return rtsp_tcp_data_rx(p_rua);
}

#endif // OVER_HTTP

#ifdef OVER_WEBSOCKET

void CRtspClient::set_rtsp_over_ws(int flag, int port)
{
    if (flag)
    {
        m_rua.mcast_flag = 0;
        m_rua.rtp_tcp = 1;
    }
    
    m_rua.over_ws = flag;

    if (port > 0 && port <= 65535)
    {
        m_rua.ws_port = port;
    }
}

int CRtspClient::rtsp_build_ws_req(void * p_user, char * bufs, int buflen)
{
    int offset = 0;
    uint8 key[20];
    char  key_base64[32] = {'\0'};
    HTTPREQ * p_req = (HTTPREQ *) p_user;

    snprintf((char *)key, sizeof(key), "%08d%08x", rand(), sys_os_get_ms());

    base64_encode(key, 16, key_base64, sizeof(key_base64)-1);
    
    offset += snprintf(bufs+offset, buflen-offset, "GET %s HTTP/1.1\r\n", p_req->url);
	offset += snprintf(bufs+offset, buflen-offset, "Host: %s:%u\r\n", p_req->host, p_req->port);
	offset += snprintf(bufs+offset, buflen-offset, "Upgrade: websocket\r\n");
	offset += snprintf(bufs+offset, buflen-offset, "Connection: Upgrade\r\n");
	offset += snprintf(bufs+offset, buflen-offset, "Sec-WebSocket-Key: %s\r\n", key_base64);
	offset += snprintf(bufs+offset, buflen-offset, "Sec-WebSocket-Protocol: %s\r\n", WS_PROTOCOL);
	offset += snprintf(bufs+offset, buflen-offset, "Sec-WebSocket-Version: %d\r\n", WS_VERSION);

	if (p_req->need_auth)
    {
    	offset += http_build_auth_msg(p_req, "GET", bufs+offset, buflen-offset);        
	}
	
	offset += snprintf(bufs+offset, buflen-offset, "\r\n");

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

    return offset;
}

BOOL CRtspClient::rtsp_over_ws_start()
{
    int len;
    int offset = 0;
    int timeout = m_nConnTimeout*1000;
    char buff[2048];
    HTTPREQ * p_http;
    HRTSP_MSG * tx_msg;

RETRY:

    p_http = &m_rua.ws_http;

    if (m_nport != 554)
    {
        p_http->port = m_nport; // Port from the stream address
    }
    else
    {
        p_http->port = m_rua.ws_port; // Configured RTSP OVER websocket port
    }
    
    strcpy(p_http->host, m_ip);
    strcpy(p_http->url, m_suffix);

    if (memcmp(m_url, "wss://", 6) == 0)
    {
        p_http->https = 1;
    }
    
    p_http->cfd = tcp_connect_timeout(get_address_by_name(p_http->host), p_http->port, timeout);
	if (p_http->cfd <= 0)
	{
	    if (p_http->port == m_rua.ws_port)
	    {
	        log_print(HT_LOG_ERR, "%s, %s:%d connect failed\r\n", 
	            __FUNCTION__, p_http->host, p_http->port);
    	    goto FAILED;
	    }
	    
	    p_http->port = m_rua.ws_port;
	    p_http->cfd = tcp_connect_timeout(get_address_by_name(p_http->host), p_http->port, timeout);
	    if (p_http->cfd <= 0)
	    {
    	    log_print(HT_LOG_ERR, "%s, %s:%d connect failed\r\n", 
	            __FUNCTION__, p_http->host, p_http->port);
    	    goto FAILED;
	    }
	}

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

    if (p_http->https)
    {
#ifdef HTTPS    
        if (!http_cln_ssl_conn(p_http, timeout))
        {
            goto FAILED;
        }
#else
        log_print(HT_LOG_ERR, "%s, the server require ssl connection, unsupport!\r\n", __FUNCTION__);
		goto FAILED;
#endif
    }
    
	offset = rtsp_build_ws_req(p_http, buff, sizeof(buff));
    
	if (http_cln_tx(p_http, buff, offset) <= 0)
	{
	    log_print(HT_LOG_ERR, "%s, http_cln_tx failed\r\n", __FUNCTION__);
	    goto FAILED;
	}

	if (!http_cln_rx_timeout(p_http, timeout))
	{
	    log_print(HT_LOG_ERR, "%s, http_cln_rx_timeout failed\r\n", __FUNCTION__);
	    goto FAILED;
	}

    if (p_http->rx_msg->msg_sub_type == 401)
    {
        if (p_http->need_auth == FALSE)
        {
            http_cln_auth_set(p_http, m_rua.auth_info.auth_name, m_rua.auth_info.auth_pwd);

            http_cln_free_req(p_http);

            goto RETRY;
        }

        send_notify(RTSP_EVE_AUTHFAILED);
        goto FAILED;
    }
    else if (p_http->rx_msg->msg_sub_type != 101)
    {
        log_print(HT_LOG_ERR, "%s, msg_sub_type=%d, ctt_type=%d\r\n", 
            __FUNCTION__, p_http->rx_msg->msg_sub_type, p_http->rx_msg->ctt_type);
        goto FAILED;
    }

    m_rua.cseq = 1;
    m_rua.rtp_tcp = 1;
    m_rua.state = RCS_OPTIONS;

    m_rua.ws_msg.buff = (char *)malloc(WS_MAXMESSAGE);
    if (NULL == m_rua.ws_msg.buff)
    {
        log_print(HT_LOG_ERR, "%s, malloc failed!\r\n", __FUNCTION__);
        goto FAILED;
    }
    
    m_rua.ws_msg.buff_len = WS_MAXMESSAGE;
    
	tx_msg = rcua_build_options(&m_rua);
	if (tx_msg)
	{
		rcua_send_free_rtsp_msg(&m_rua, tx_msg);
	}
    
	return TRUE;

FAILED:
    
    http_cln_free_req(&m_rua.ws_http);

    return FALSE;
}

BOOL CRtspClient::rtsp_ws_ping_process(RCUA * p_rua, char * p_buff, int len)
{
    int slen;
    int extra = rtsp_ws_encode_data((uint8 *)p_buff, len, 0x8A, 1);

    len += extra;
    p_buff -= extra;

    slen = http_cln_tx(&p_rua->ws_http, p_buff, len);
    if (slen != len)
    {
        return FALSE;
    }
    
    return TRUE;
}

BOOL CRtspClient::rtsp_ws_rtsp_process(RCUA * p_rua, char * p_buff, int len)
{
    if (NULL == p_rua->rtp_rcv_buf || 0 == p_rua->rtp_t_len)
    {
        memcpy(p_rua->rcv_buf + p_rua->rcv_dlen, p_buff, len);
        p_rua->rcv_dlen += len;

        if (p_rua->rcv_dlen < 4)
		{
			return TRUE;
		}
    }
    else
    {
        if (len > p_rua->rtp_t_len - p_rua->rtp_rcv_len)
        {
            log_print(HT_LOG_ERR, "%s, bufflen=%d, rtp_t_len=%d, rtp_rcv_len=%d\r\n", 
                __FUNCTION__, len, p_rua->rtp_t_len, p_rua->rtp_rcv_len);
            return FALSE;
        }

        memcpy(p_rua->rtp_rcv_buf + p_rua->rtp_rcv_len, p_buff, len);
        p_rua->rtp_rcv_len += len;

        if (p_rua->rtp_rcv_len == p_rua->rtp_t_len)
		{
            tcp_data_rx((uint8*)p_rua->rtp_rcv_buf, p_rua->rtp_rcv_len);
			
			free(p_rua->rtp_rcv_buf);
			p_rua->rtp_rcv_buf = NULL;
			p_rua->rtp_rcv_len = 0;
			p_rua->rtp_t_len = 0;
		}
		
		return TRUE;
    }

    return rtsp_tcp_data_rx(p_rua);
}

BOOL CRtspClient::rtsp_ws_data_process(RCUA * p_rua)
{
    WSMSG * p_msg = &p_rua->ws_msg;

PARSE:

    int ret = rtsp_ws_decode_data(&p_rua->ws_msg);
    if (ret > 0)
    {
        if (p_rua->ws_msg.opcode == 1 || p_rua->ws_msg.opcode == 2)
        {
            rtsp_ws_rtsp_process(p_rua, p_msg->buff+p_msg->skip, p_msg->msg_len);
        }
        else if (p_rua->ws_msg.opcode == 0x8)   // close session
        {
            log_print(HT_LOG_INFO, "%s, recv close message\r\n", __FUNCTION__);
            return FALSE;
        }
        else if (p_rua->ws_msg.opcode == 0x9)   // ping 
        {
            rtsp_ws_ping_process(p_rua, p_msg->buff+p_msg->skip, p_msg->msg_len);
        }
        else if (p_rua->ws_msg.opcode == 0xA)   // pong
        {
            log_print(HT_LOG_INFO, "%s, recv pong message\r\n", __FUNCTION__);
        }
        else
        {
            log_print(HT_LOG_INFO, "%s, recv unkonw message %d\r\n", __FUNCTION__, p_rua->ws_msg.opcode);
            return -1;
        }
        
        p_msg->rcv_len -= p_msg->msg_len + p_msg->skip;

        if (p_msg->rcv_len > 0)
        {
            memmove(p_msg->buff, p_msg->buff + p_msg->msg_len + p_msg->skip, p_msg->rcv_len);
        }
        
        if (p_msg->rcv_len >= 6)
        {
            goto PARSE;
        }

        return TRUE;
    }
    else if (ret == 0) // need more data to parse
    {
        return TRUE;
    }
    else
    {
        return FALSE;
    }
}

int CRtspClient::rtsp_over_ws_rx(RCUA * p_rua)
{
    int rlen;
	WSMSG * p_msg = &p_rua->ws_msg;
    SOCKET fd = -1;
#ifdef HTTPS    
    int https = 0;
#endif

    fd = p_rua->ws_http.cfd;
    if (fd <= 0)
	{
		return -1;
	}

#ifdef HTTPS
    if (p_rua->ws_http.https && p_rua->ws_http.ssl)
    {
        https = 1;
    }
    
    if (https && SSL_pending(p_rua->ws_http.ssl) > 0)
	{
	    // There is data to read 
	}
	else
#endif
    {
#ifndef EPOLL    
        fd_set fdread;
        struct timeval tv = {1, 0};

        FD_ZERO(&fdread);
        FD_SET(fd, &fdread);
    
        int sret = select((int)(fd+1), &fdread, NULL, NULL, &tv); 
        if (sret == 0) // Time expired 
        { 
            return RTSP_RX_TIMEOUT; 
        }
        else if (!FD_ISSET(fd, &fdread))
        {
            return RTSP_RX_TIMEOUT;
        }
#endif        
    }

#ifdef HTTPS
    if (https)
    {
        rlen = SSL_read(p_rua->ws_http.ssl, p_msg->buff+p_msg->rcv_len, p_msg->buff_len-p_msg->rcv_len);
    }
    else
#endif
    rlen = recv(fd, p_msg->buff+p_msg->rcv_len, p_msg->buff_len-p_msg->rcv_len, 0);
	if (rlen <= 0)
	{
		log_print(HT_LOG_WARN, "%s, ret = %d, err = %s\r\n", 
		    __FUNCTION__, rlen, sys_os_get_socket_error());	//recv error, connection maybe disconn?
		return RTSP_RX_FAIL;
	}

	p_msg->rcv_len += rlen;

	if (p_msg->rcv_len < 4)
	{
		return RTSP_RX_SUCC;
	}

	return rtsp_ws_data_process(p_rua) ? RTSP_RX_SUCC : RTSP_RX_FAIL;
}

#endif // OVER_WEBSOCKET

BOOL CRtspClient::rtsp_client_state(RCUA * p_rua, HRTSP_MSG * rx_msg)
{
    BOOL ret = TRUE;

	if (rx_msg->msg_type == 0)	// Request message?
	{
		return FALSE;
	}

    if (rx_msg->msg_sub_type == 401)
    {
        return rtsp_unauth_res(p_rua, rx_msg);
    }
    else
    {
        p_rua->auth_retry = 0;
    }
    
	switch (p_rua->state)
	{
	case RCS_NULL:
		break;

	case RCS_OPTIONS:
		ret = rtsp_options_res(p_rua, rx_msg);
		break;
		
	case RCS_DESCRIBE:
		ret = rtsp_describe_res(p_rua, rx_msg);
		break;

	case RCS_INIT_V:
		ret = rtsp_setup_res(p_rua, rx_msg, AV_VIDEO_CH);
		break;

	case RCS_INIT_A:
		ret = rtsp_setup_res(p_rua, rx_msg, AV_AUDIO_CH);
		break;

#ifdef METADATA
	case RCS_INIT_M:
		ret = rtsp_setup_res(p_rua, rx_msg, AV_METADATA_CH);
		break;
#endif

#ifdef BACKCHANNEL
	case RCS_INIT_BC:
		ret = rtsp_setup_res(p_rua, rx_msg, AV_BACK_CH);
		break;
#endif

	case RCS_READY:
		ret = rtsp_play_res(p_rua, rx_msg);
		break;

	case RCS_PLAYING:
	    rtsp_play_res(p_rua, rx_msg);
		break;

	case RCS_RECORDING:
		break;
	}		

	return ret;
}

BOOL CRtspClient::make_prepare_play()
{    
	if (m_rua.channels[AV_VIDEO_CH].ctl[0] != '\0')
	{
		if (m_rua.video_codec == VIDEO_CODEC_H264)
		{
			h264_rxi_init(&h264rxi, video_data_cb, this);
		}
		else if (m_rua.video_codec == VIDEO_CODEC_H265)
		{
			h265_rxi_init(&h265rxi, video_data_cb, this);
		}
		else if (m_rua.video_codec == VIDEO_CODEC_JPEG)
		{
			mjpeg_rxi_init(&mjpegrxi, video_data_cb, this);

			mjpegrxi.width = m_nWidth;
			mjpegrxi.height = m_nHeight;
		}
		else if (m_rua.video_codec == VIDEO_CODEC_MP4)
		{
			mpeg4_rxi_init(&mpeg4rxi, video_data_cb, this);
		}
	}
	
    if (m_rua.channels[AV_AUDIO_CH].ctl[0] != '\0')
    {
		if (m_rua.audio_codec == AUDIO_CODEC_AAC)
		{
			aac_rxi_init(&aacrxi, audio_data_cb, this);
		}
		else if (m_rua.audio_codec != AUDIO_CODEC_NONE )
		{
			pcm_rxi_init(&pcmrxi, audio_data_cb, this);
		}
    }

#ifdef METADATA
    if (m_rua.channels[AV_METADATA_CH].ctl[0] != '\0')
    {
        pcm_rxi_init(&metadatarxi, metadata_data_cb, this);
    }
#endif

#ifndef EPOLL
	if (!m_rua.rtp_tcp)
	{
    	m_udpRxTid = sys_os_create_thread((void *)rtsp_udp_rx_thread, this);
    }
#endif

    return TRUE;
}

void CRtspClient::tcp_data_rx(uint8 * lpData, int rlen)
{
	RILF * p_rilf = (RILF *)lpData;
	uint8 * p_rtp = (uint8 *)p_rilf + 4;
	uint32 rtp_len = rlen - 4;

	if (p_rilf->channel == m_rua.channels[AV_VIDEO_CH].interleaved)
	{
		if (VIDEO_CODEC_H264 == m_rua.video_codec)
		{
			h264_rtp_rx(&h264rxi, p_rtp, rtp_len);
		}
		else if (VIDEO_CODEC_JPEG == m_rua.video_codec)
		{
			mjpeg_rtp_rx(&mjpegrxi, p_rtp, rtp_len);
		}
		else if (VIDEO_CODEC_MP4 == m_rua.video_codec)
		{
			mpeg4_rtp_rx(&mpeg4rxi, p_rtp, rtp_len);
		}
		else if (VIDEO_CODEC_H265 == m_rua.video_codec)
		{
			h265_rtp_rx(&h265rxi, p_rtp, rtp_len);
		}
	}
	else if (p_rilf->channel == m_rua.channels[AV_AUDIO_CH].interleaved)
	{
		if (AUDIO_CODEC_AAC == m_rua.audio_codec)
		{
			aac_rtp_rx(&aacrxi, p_rtp, rtp_len);
		}
		else if (AUDIO_CODEC_NONE != m_rua.audio_codec)
		{
			pcm_rtp_rx(&pcmrxi, p_rtp, rtp_len);
		}
	}
#ifdef METADATA	
	else if (p_rilf->channel == m_rua.channels[AV_METADATA_CH].interleaved)
	{
	    pcm_rtp_rx(&metadatarxi, p_rtp, rtp_len);
	}
#endif
    else if (p_rilf->channel == m_rua.channels[AV_VIDEO_CH].interleaved+1)
	{
	    rtcp_data_rx(p_rtp, rtp_len, AV_VIDEO_CH);
	}
	else if (p_rilf->channel == m_rua.channels[AV_AUDIO_CH].interleaved+1)
	{
	    rtcp_data_rx(p_rtp, rtp_len, AV_AUDIO_CH);
	}
#ifdef METADATA		
	else if (p_rilf->channel == m_rua.channels[AV_METADATA_CH].interleaved+1)
	{
	    rtcp_data_rx(p_rtp, rtp_len, AV_METADATA_CH);
	}
#endif
}

void CRtspClient::udp_data_rx(uint8 * lpData, int rlen, int type)
{
	uint8 * p_rtp = lpData;
	uint32 rtp_len = rlen;

	if (rtp_len >= 2 && RTP_PT_IS_RTCP(p_rtp[1]))
	{
		rtcp_data_rx(p_rtp, rtp_len, type);
	}
	else if (AV_TYPE_VIDEO == type)
	{
		if (VIDEO_CODEC_H264 == m_rua.video_codec)
		{
			h264_rtp_rx(&h264rxi, p_rtp, rtp_len);
		}
		else if (VIDEO_CODEC_JPEG == m_rua.video_codec)
		{
			mjpeg_rtp_rx(&mjpegrxi, p_rtp, rtp_len);
		}
		else if (VIDEO_CODEC_MP4 == m_rua.video_codec)
		{
			mpeg4_rtp_rx(&mpeg4rxi, p_rtp, rtp_len);
		}
		else if (VIDEO_CODEC_H265 == m_rua.video_codec)
		{
			h265_rtp_rx(&h265rxi, p_rtp, rtp_len);
		}
	}
	else if (AV_TYPE_AUDIO == type)
	{
		if (AUDIO_CODEC_AAC == m_rua.audio_codec)
		{
			aac_rtp_rx(&aacrxi, p_rtp, rtp_len);
		}
		else if (AUDIO_CODEC_NONE != m_rua.audio_codec)
		{
			pcm_rtp_rx(&pcmrxi, p_rtp, rtp_len);
		}
	}
#ifdef METADATA	
	else if (AV_TYPE_METADATA == type)
	{
	    pcm_rtp_rx(&metadatarxi, p_rtp, rtp_len);
	}
#endif	
}

void CRtspClient::rtcp_data_rx(uint8 * lpData, int rlen, int type)
{
    sys_os_mutex_enter(m_pMutex);
	if (m_pRtcpCB)
	{
		m_pRtcpCB(lpData, rlen, type, m_pUserdata);
	}
	sys_os_mutex_leave(m_pMutex);
}

int CRtspClient::rtsp_msg_parser(RCUA * p_rua)
{
	int rtsp_pkt_len = rtsp_pkt_find_end(p_rua->rcv_buf);
	if (rtsp_pkt_len == 0) // wait for next recv
	{
		return RTSP_PARSE_MOREDATA;
	}
	
	HRTSP_MSG * rx_msg = rtsp_get_msg_buf();
	if (rx_msg == NULL)
	{
		log_print(HT_LOG_ERR, "%s, rtsp_get_msg_buf return null!!!\r\n", __FUNCTION__);
		return RTSP_PARSE_FAIL;
	}
	
	memcpy(rx_msg->msg_buf, p_rua->rcv_buf, rtsp_pkt_len);
	rx_msg->msg_buf[rtsp_pkt_len] = '\0';

	log_print(HT_LOG_DBG, "RX << %s\r\n", rx_msg->msg_buf);

	int parse_len = rtsp_msg_parse_part1(rx_msg->msg_buf, rtsp_pkt_len, rx_msg);
	if (parse_len != rtsp_pkt_len)	//parse error
	{
		log_print(HT_LOG_ERR, "%s, rtsp_msg_parse_part1=%d, rtsp_pkt_len=%d!!!\r\n", 
		    __FUNCTION__, parse_len, rtsp_pkt_len);
		rtsp_free_msg(rx_msg);

		p_rua->rcv_dlen = 0;
		return RTSP_PARSE_FAIL;
	}
	
	if (rx_msg->ctx_len > 0)	
	{
		if (p_rua->rcv_dlen < (parse_len + rx_msg->ctx_len))
		{
			rtsp_free_msg(rx_msg);
			return RTSP_PARSE_MOREDATA;
		}

		memcpy(rx_msg->msg_buf+rtsp_pkt_len, p_rua->rcv_buf+rtsp_pkt_len, rx_msg->ctx_len);
        rx_msg->msg_buf[rtsp_pkt_len+rx_msg->ctx_len] = '\0';

        log_print(HT_LOG_DBG, "%s\r\n", rx_msg->msg_buf+rtsp_pkt_len);
        
		int sdp_parse_len = rtsp_msg_parse_part2(rx_msg->msg_buf+rtsp_pkt_len, rx_msg->ctx_len, rx_msg);
		if (sdp_parse_len != rx_msg->ctx_len)
		{
		}
		parse_len += rx_msg->ctx_len;
	}
	
	if (parse_len < p_rua->rcv_dlen)
	{
		while (p_rua->rcv_buf[parse_len] == ' ' || 
		    p_rua->rcv_buf[parse_len] == '\r' || 
		    p_rua->rcv_buf[parse_len] == '\n')
		{
			parse_len++;
		}
		
		memmove(p_rua->rcv_buf, p_rua->rcv_buf + parse_len, p_rua->rcv_dlen - parse_len);
		p_rua->rcv_dlen -= parse_len;
	}
	else
	{
		p_rua->rcv_dlen = 0;
	}
	
	int ret = rtsp_client_state(p_rua, rx_msg);
	
	rtsp_free_msg(rx_msg);

	return ret ? RTSP_PARSE_SUCC : RTSP_PARSE_FAIL;
}

#ifdef EPOLL

int CRtspClient::rtsp_epoll_rx()
{
    int i, nfds;
    
    nfds = epoll_wait(m_ep_fd, m_ep_events, m_ep_event_num, 1000);
    if (0 == nfds)
    {
        return RTSP_RX_TIMEOUT;
    }
    
	for (i = 0; i < nfds; i++)
	{
		if (m_ep_events[i].events & EPOLLIN)
		{
			int fd = (int)(m_ep_events[i].data.u64 & 0xFFFF);

			if ((m_ep_events[i].data.u64 & ((uint64)1 << 63)) != 0)
			{
			    if (rtsp_tcp_rx() == RTSP_RX_FAIL)
			    {
			        return RTSP_RX_FAIL;
			    }
			}
	        else
	        {
	            int ch;
	            int alen;
                char buf[2048];
                struct sockaddr_in addr;
                
            	memset(&addr, 0, sizeof(addr));
            	alen = sizeof(struct sockaddr_in);

                int rlen = recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr *)&addr, (socklen_t*)&alen);
            	if (rlen <= 12)
            	{
            		log_print(HT_LOG_ERR, "%s, recvfrom return %d, err[%s]!!!\r\n", 
            		    __FUNCTION__, rlen, sys_os_get_socket_error());
            		continue;
            	}

            	for (ch = 0; ch < AV_MAX_CHS; ch++)
            	{
            	    if (m_rua.channels[ch].udp_fd == fd)
            	    {
            	        udp_data_rx((uint8*)buf, rlen, ch);
            	    }
            	    else if (m_rua.channels[ch].rtcp_fd == fd)
            	    {
            	        rtcp_data_rx((uint8*)buf, rlen, ch);
            	    }
            	}
			}
		}
	}

	return RTSP_RX_SUCC;
}

#endif

int CRtspClient::rtsp_tcp_data_rx(RCUA * p_rua)
{
rx_point:

	if (rtsp_is_rtsp_msg(p_rua->rcv_buf))	//Is RTSP Packet?
	{
		int ret = rtsp_msg_parser(p_rua);
		if (ret == RTSP_PARSE_FAIL)
		{
			return RTSP_RX_FAIL;
		}
		else if (ret == RTSP_PARSE_MOREDATA)
		{
			return RTSP_RX_SUCC;
		}

		if (p_rua->rcv_dlen >= 4)
		{
			goto rx_point;
		}
	}
	else
	{
		RILF * p_rilf = (RILF *)(p_rua->rcv_buf);
		if (p_rilf->magic != 0x24)
		{		
			log_print(HT_LOG_WARN, "%s, p_rilf->magic[0x%02X]!!!\r\n", __FUNCTION__, p_rilf->magic);

            // Try to recover from wrong data
            
			for (int i = 1; i <= p_rua->rcv_dlen - 4; i++)
			{
				if (p_rua->rcv_buf[i] == 0x24 &&
					(p_rua->rcv_buf[i+1] == p_rua->channels[AV_VIDEO_CH].interleaved ||
					 p_rua->rcv_buf[i+1] == p_rua->channels[AV_AUDIO_CH].interleaved))
				{
					memmove(p_rua->rcv_buf, p_rua->rcv_buf+i, p_rua->rcv_dlen - i);
					p_rua->rcv_dlen -= i;
					goto rx_point;
				}
			}

			p_rua->rcv_dlen = 0;
			
			return RTSP_RX_SUCC;
		}
		
		uint16 rtp_len = ntohs(p_rilf->rtp_len);
		if (rtp_len > (p_rua->rcv_dlen - 4))
		{
			if (p_rua->rtp_rcv_buf)
			{
				free(p_rua->rtp_rcv_buf);
			}
			
			p_rua->rtp_rcv_buf = (char *)malloc(rtp_len+4);
			if (p_rua->rtp_rcv_buf == NULL) 
			{
			    return RTSP_RX_FAIL;
			}
			
			memcpy(p_rua->rtp_rcv_buf, p_rua->rcv_buf, p_rua->rcv_dlen);
			p_rua->rtp_rcv_len = p_rua->rcv_dlen;
			p_rua->rtp_t_len = rtp_len+4;

			p_rua->rcv_dlen = 0;

			return RTSP_RX_SUCC;
		}
			
		tcp_data_rx((uint8*)p_rilf, rtp_len+4);

		p_rua->rcv_dlen -= rtp_len+4;
		if (p_rua->rcv_dlen > 0)
		{
			memmove(p_rua->rcv_buf, p_rua->rcv_buf+rtp_len+4, p_rua->rcv_dlen);
		}

		if (p_rua->rcv_dlen >= 4)
		{
			goto rx_point;
		}
	}

	return RTSP_RX_SUCC;
}

int CRtspClient::rtsp_over_tcp_rx(RCUA * p_rua)
{
    int rlen;
    SOCKET fd;

    fd = p_rua->fd;
	if (fd <= 0)
	{
		return -1;
	}

#ifndef EPOLL    
    fd_set fdread;
    struct timeval tv = {1, 0};

    FD_ZERO(&fdread);
    FD_SET(fd, &fdread);

    int sret = select((int)(fd+1), &fdread, NULL, NULL, &tv); 
    if (sret == 0) // Time expired 
    { 
        return RTSP_RX_TIMEOUT; 
    }
    else if (!FD_ISSET(fd, &fdread))
    {
        return RTSP_RX_TIMEOUT;
    }
#endif        

	if (p_rua->rtp_rcv_buf == NULL || p_rua->rtp_t_len == 0)
	{
        rlen = recv(fd, p_rua->rcv_buf+p_rua->rcv_dlen, 2048-p_rua->rcv_dlen, 0);
		if (rlen <= 0)
		{
			log_print(HT_LOG_WARN, "%s, ret = %d, err = %s\r\n", 
			    __FUNCTION__, rlen, sys_os_get_socket_error());	//recv error, connection maybe disconn?
			return RTSP_RX_FAIL;
		}

		p_rua->rcv_dlen += rlen;

		if (p_rua->rcv_dlen < 4)
		{
			return RTSP_RX_SUCC;
		}
	}
	else
	{
        rlen = recv(fd, p_rua->rtp_rcv_buf+p_rua->rtp_rcv_len, p_rua->rtp_t_len-p_rua->rtp_rcv_len, 0);
		if (rlen <= 0)
		{
			log_print(HT_LOG_WARN, "%s, ret = %d, err = %s\r\n", 
			    __FUNCTION__, rlen, sys_os_get_socket_error());	// recv error, connection maybe disconn?
			return RTSP_RX_FAIL;
		}

		p_rua->rtp_rcv_len += rlen;
		
		if (p_rua->rtp_rcv_len == p_rua->rtp_t_len)
		{
			tcp_data_rx((uint8*)p_rua->rtp_rcv_buf, p_rua->rtp_rcv_len);
			
			free(p_rua->rtp_rcv_buf);
			p_rua->rtp_rcv_buf = NULL;
			p_rua->rtp_rcv_len = 0;
			p_rua->rtp_t_len = 0;
		}
		
		return RTSP_RX_SUCC;
	}

	return rtsp_tcp_data_rx(p_rua);
}

int CRtspClient::rtsp_tcp_rx()
{
	RCUA * p_rua = &m_rua;

#ifdef OVER_HTTP    
    if (p_rua->over_http)
    {
        return rtsp_over_http_rx(p_rua);
    }
    else 
#endif
#ifdef OVER_WEBSOCKET
    if (p_rua->over_ws)
    {
        return rtsp_over_ws_rx(p_rua);
    }
    else 
#endif

    return rtsp_over_tcp_rx(p_rua);
}

int CRtspClient::rtsp_udp_rx()
{
	int i, max_fd = 0;
    fd_set fdr;
    
	FD_ZERO(&fdr);

    for (i = 0; i < AV_MAX_CHS; i++)
    {
        if (m_rua.channels[i].udp_fd > 0)
        {
            FD_SET(m_rua.channels[i].udp_fd, &fdr);
            max_fd = (max_fd >= (int)m_rua.channels[i].udp_fd)? max_fd : (int)m_rua.channels[i].udp_fd;
        }
        
        if (m_rua.channels[i].rtcp_fd > 0)
        {
            FD_SET(m_rua.channels[i].rtcp_fd, &fdr);
            max_fd = (max_fd >= (int)m_rua.channels[i].rtcp_fd)? max_fd : (int)m_rua.channels[i].rtcp_fd;
        }
    }

	struct timeval tv = {1, 0};

	int sret = select(max_fd+1, &fdr, NULL, NULL, &tv);
	if (sret <= 0)
	{
		return RTSP_RX_TIMEOUT;
    }

    int alen;
    char buf[2048];
    struct sockaddr_in addr;
    
	memset(&addr, 0, sizeof(addr));
	alen = sizeof(struct sockaddr_in);

    for (i = 0; i < AV_MAX_CHS; i++)
    {
        if (m_rua.channels[i].udp_fd > 0 && FD_ISSET(m_rua.channels[i].udp_fd, &fdr))
        {
            int rlen = recvfrom(m_rua.channels[i].udp_fd, buf, sizeof(buf), 0, (struct sockaddr *)&addr, (socklen_t*)&alen);
        	if (rlen <= 12)
        	{
        		log_print(HT_LOG_ERR, "%s, recvfrom return %d, err[%s]!!!\r\n", 
        		    __FUNCTION__, rlen, sys_os_get_socket_error());
        	}
        	else
        	{
        	    udp_data_rx((uint8*)buf, rlen, i);
        	}
        }

        if (m_rua.channels[i].rtcp_fd > 0 && FD_ISSET(m_rua.channels[i].rtcp_fd, &fdr))
        {
            int rlen = recvfrom(m_rua.channels[i].rtcp_fd, buf, sizeof(buf), 0, (struct sockaddr *)&addr, (socklen_t*)&alen);
        	if (rlen <= 12)
        	{
        		log_print(HT_LOG_ERR, "%s, recvfrom return %d, err[%s]!!!\r\n", 
        		    __FUNCTION__, rlen, sys_os_get_socket_error());
        	}
        	else
        	{
        	    rtcp_data_rx((uint8*)buf, rlen, i);
        	}
        }
    }

    return RTSP_RX_SUCC;
}

BOOL CRtspClient::rtsp_start(const char * url, const char * ip, int port, const char * user, const char * pass)
{
	if (m_rua.state != RCS_NULL)
	{
		rtsp_play();
		return TRUE;
	}

	if (user && user != m_rua.auth_info.auth_name)
	{
		strcpy(m_rua.auth_info.auth_name, user);
	}

	if (pass && pass != m_rua.auth_info.auth_pwd)
	{
		strcpy(m_rua.auth_info.auth_pwd, pass);
	}
	
	if (url && url != m_url)
	{
		strcpy(m_url, url);
	}
	
	if (ip && ip != m_ip)
	{
		strcpy(m_ip, ip);
	}
	
	m_nport = port;

    m_rua.rport = m_nport;
	strcpy(m_rua.ripstr, m_ip);

	if (m_suffix[0] == '/')
	{
        snprintf(m_rua.uri, sizeof(m_rua.uri)-1, "rtsp://%s:%d%s", m_ip, m_nport, m_suffix);
    }
    else
    {
        snprintf(m_rua.uri, sizeof(m_rua.uri)-1, "rtsp://%s:%d/%s", m_ip, m_nport, m_suffix);
    }

    m_bRunning = TRUE;
	m_tcpRxTid = sys_os_create_thread((void *)rtsp_tcp_rx_thread, this);
	if (m_tcpRxTid == 0)
	{
		log_print(HT_LOG_ERR, "%s, sys_os_create_thread failed!!!\r\n", __FUNCTION__);
		return FALSE;
	}
	
	return TRUE;
}

BOOL CRtspClient::rtsp_start(const char * url, const char * user, const char * pass)
{
    int port;
    char proto[32], username[64], password[64], host[100], path[200];

    url_split(url, proto, sizeof(proto), username, sizeof(username), 
        password, sizeof(password), host, sizeof(host), &port, path, sizeof(path));

    if (strcasecmp(proto, "rtsp") == 0)
    {
        port = (port == -1) ? 554 : port;
    }
#ifdef OVER_HTTP    
    else if (strcasecmp(proto, "http") == 0)
    {
        set_rtsp_over_http(1, 0);
        
        port = (port == -1) ? 80 : port;
    }
    else if (strcasecmp(proto, "https") == 0)
    {
        set_rtsp_over_http(1, 0);
        
        port = (port == -1) ? 443 : port;
    }
#endif
#ifdef OVER_WEBSOCKET    
    else if (strcasecmp(proto, "ws") == 0)
    {
        set_rtsp_over_ws(1, 0);

        port = (port == -1) ? 80 : port;
    }
    else if (strcasecmp(proto, "wss") == 0)
    {
        set_rtsp_over_ws(1, 0);

        port = (port == -1) ? 443 : port;
    }
#endif
    else
    {
        return FALSE;
    }

    if (host[0] == '\0')
    {
	    return FALSE;
    }

    strncpy(m_ip, host, sizeof(m_ip) - 1);

    if (username[0] != '\0')
	{
	    strcpy(m_rua.auth_info.auth_name, username);
	}
	else if (user && user[0] != '\0')
	{
	    strcpy(m_rua.auth_info.auth_name, user);
	}

    if (password[0] != '\0')
	{
	    strcpy(m_rua.auth_info.auth_pwd, password);
	}
	else if (pass && pass[0] != '\0')
	{
	    strcpy(m_rua.auth_info.auth_pwd, pass);
	}

    if (path[0] != '\0')
	{
	    strncpy(m_suffix, path, sizeof(m_suffix) - 1);
	}

    if (path[0] == '/')
    {
	    snprintf(m_url, sizeof(m_url)-1, "%s://%s:%d%s", proto, host, port, path);
	}
	else
	{
	    snprintf(m_url, sizeof(m_url)-1, "%s://%s:%d/%s", proto, host, port, path);
	}
	
	m_nport = port;

    return rtsp_start(m_url, m_ip, m_nport, m_rua.auth_info.auth_name, m_rua.auth_info.auth_pwd);
}

BOOL CRtspClient::rtsp_play(int npt_start)
{
	m_rua.cseq++;
	m_rua.seek_pos = npt_start;
	
	HRTSP_MSG * tx_msg = rcua_build_play(&m_rua);	
	if (tx_msg)
	{
		rcua_send_free_rtsp_msg(&m_rua, tx_msg);
	}
	
	return TRUE;
}

BOOL CRtspClient::rtsp_stop()
{
    if (RCS_NULL == m_rua.state)
    {
        return TRUE;
    }
    
	m_rua.cseq++;
	m_rua.state = RCS_NULL;
	
	HRTSP_MSG * tx_msg = rcua_build_teardown(&m_rua);
	if (tx_msg)
	{
		rcua_send_free_rtsp_msg(&m_rua, tx_msg);	
	}
	
	return TRUE;
}

BOOL CRtspClient::rtsp_pause()
{
	m_rua.cseq++;
	
	HRTSP_MSG * tx_msg = rcua_build_pause(&m_rua);	
	if (tx_msg)
	{
		rcua_send_free_rtsp_msg(&m_rua, tx_msg);	
	}
	
	return TRUE;
}

BOOL CRtspClient::rtsp_close()
{
	sys_os_mutex_enter(m_pMutex);
	m_pAudioCB = NULL;
	m_pVideoCB = NULL;
	m_pRtcpCB = NULL;
#ifdef METADATA	
	m_pMetadataCB = NULL;
#endif
	m_pNotify = NULL;
	m_pUserdata = NULL;
	sys_os_mutex_leave(m_pMutex);

#ifdef OVER_HTTP
    if (m_rua.over_http)
    {
#ifdef HTTPS    
        if (m_rua.rtsp_recv.https && m_rua.rtsp_recv.ssl)
        {
            SSL_shutdown(m_rua.rtsp_recv.ssl);
        }
        
        if (m_rua.rtsp_send.https && m_rua.rtsp_send.ssl)
        {
            SSL_shutdown(m_rua.rtsp_send.ssl);
        }
#endif

        if (m_rua.rtsp_recv.cfd > 0)
        {
#ifdef EPOLL    	
            epoll_ctl(m_ep_fd, EPOLL_CTL_DEL, m_rua.rtsp_recv.cfd, NULL);
#endif        
            closesocket(m_rua.rtsp_recv.cfd);
            m_rua.rtsp_recv.cfd = 0;
        }
        
        if (m_rua.rtsp_send.cfd)
        {
            closesocket(m_rua.rtsp_send.cfd);
            m_rua.rtsp_send.cfd = 0;
        }
    }
#endif

#ifdef OVER_WEBSOCKET
    if (m_rua.over_ws)
    {
#ifdef HTTPS    
        if (m_rua.ws_http.https && m_rua.ws_http.ssl)
        {
            SSL_shutdown(m_rua.ws_http.ssl);
        }
#endif

        if (m_rua.ws_http.cfd > 0)
        {
#ifdef EPOLL    	
            epoll_ctl(m_ep_fd, EPOLL_CTL_DEL, m_rua.ws_http.cfd, NULL);
#endif        
            closesocket(m_rua.ws_http.cfd);
            m_rua.ws_http.cfd = 0;
        }
    }
#endif
	
    m_bRunning = FALSE;

    // Close sockets NOW so select() returns immediately in the rx threads,
    // allowing them to see m_bRunning==FALSE and exit without waiting for
    // the full select timeout (~1s).  Without this, threads may still be
    // blocked in select() when the DLL is unloaded → crash.
    if (m_rua.fd > 0)
    {
#ifdef EPOLL
        epoll_ctl(m_ep_fd, EPOLL_CTL_DEL, m_rua.fd, NULL);
#endif
        closesocket(m_rua.fd);
        m_rua.fd = 0;
    }

    for (int i = 0; i < AV_MAX_CHS; i++)
    {
        if (m_rua.channels[i].udp_fd > 0)
    	{
#ifdef EPOLL
    	    epoll_ctl(m_ep_fd, EPOLL_CTL_DEL, m_rua.channels[i].udp_fd, NULL);
#endif
    		closesocket(m_rua.channels[i].udp_fd);
    		m_rua.channels[i].udp_fd = 0;
    	}

    	if (m_rua.channels[i].rtcp_fd > 0)
    	{
#ifdef EPOLL
    	    epoll_ctl(m_ep_fd, EPOLL_CTL_DEL, m_rua.channels[i].rtcp_fd, NULL);
#endif
    		closesocket(m_rua.channels[i].rtcp_fd);
    		m_rua.channels[i].rtcp_fd = 0;
    	}
    }

	while (m_tcpRxTid != 0)
	{
		usleep(10*1000);
	}

	while (m_udpRxTid != 0)
	{
		usleep(10*1000);
	}

    // All sockets (fd, udp_fd, rtcp_fd) already closed above the busy-wait

#ifdef BACKCHANNEL
	if (m_rua.audio_captrue)
	{
		m_rua.audio_captrue->delCallback(rtsp_bc_cb, &m_rua);
		m_rua.audio_captrue->freeInstance(0);
		m_rua.audio_captrue = NULL;
	}
#endif
    
	if (m_rua.video_codec == VIDEO_CODEC_H264)
	{
		h264_rxi_deinit(&h264rxi);
	}
	else if (m_rua.video_codec == VIDEO_CODEC_H265)
	{
		h265_rxi_deinit(&h265rxi);
	}
	else if (m_rua.video_codec == VIDEO_CODEC_JPEG)
	{
		mjpeg_rxi_deinit(&mjpegrxi);
	}
	else if (m_rua.video_codec == VIDEO_CODEC_MP4)
	{
		mpeg4_rxi_deinit(&mpeg4rxi);
	}

	if (m_rua.audio_codec == AUDIO_CODEC_AAC)
	{
		aac_rxi_deinit(&aacrxi);
	}
	else if (m_rua.audio_codec != AUDIO_CODEC_NONE)
	{
		pcm_rxi_deinit(&pcmrxi);
	}

#ifdef METADATA
	pcm_rxi_deinit(&metadatarxi);
#endif

	if (m_rua.audio_spec)
	{
		free(m_rua.audio_spec);
		m_rua.audio_spec = NULL;
	}

#ifdef OVER_HTTP
    if (m_rua.over_http)
    {
        http_cln_free_req(&m_rua.rtsp_recv);
        http_cln_free_req(&m_rua.rtsp_send);
    }
#endif

#ifdef OVER_WEBSOCKET
    if (m_rua.over_ws)
    {
        http_cln_free_req(&m_rua.ws_http);
    }

    if (m_rua.ws_msg.buff)
    {
        free(m_rua.ws_msg.buff);
        m_rua.ws_msg.buff = NULL;
    }
#endif

    set_default();
	
	return TRUE;
}

void CRtspClient::rtsp_video_data_cb(uint8 * p_data, int len, uint32 ts, uint32 seq)
{
	sys_os_mutex_enter(m_pMutex);
	if (m_pVideoCB)
	{
		m_pVideoCB(p_data, len, ts, seq, m_pUserdata);
	}
	sys_os_mutex_leave(m_pMutex);
}

void CRtspClient::rtsp_audio_data_cb(uint8 * p_data, int len, uint32 ts, uint32 seq)
{
	sys_os_mutex_enter(m_pMutex);
	if (m_pAudioCB)
	{
		m_pAudioCB(p_data, len, ts, seq, m_pUserdata);
	}
	sys_os_mutex_leave(m_pMutex);
}

void CRtspClient::rtsp_meta_data_cb(uint8 * p_data, int len, uint32 ts, uint32 seq)
{
	sys_os_mutex_enter(m_pMutex);
	if (m_pMetadataCB)
	{
		m_pMetadataCB(p_data, len, ts, seq, m_pUserdata);
	}
	sys_os_mutex_leave(m_pMutex);
}

void CRtspClient::rtsp_keep_alive()
{
	uint32 cur_time = sys_os_get_uptime();
	uint32 keepalive_interval = (m_rua.session_timeout > 20) ? (m_rua.session_timeout / 2) : (m_rua.session_timeout - 5);
	if (cur_time - m_rua.keepalive_time >= 15)
	{
		m_rua.keepalive_time = cur_time;
		m_rua.cseq++;

		HRTSP_MSG* tx_msg;

		// Change to ue OPTIONS command to keep alive
		tx_msg = rcua_build_options(&m_rua);
		if (!tx_msg) {
			tx_msg = rcua_build_get_parameter(&m_rua);
		}
		if (tx_msg)
		{
			rcua_send_free_rtsp_msg(&m_rua, tx_msg);
		}

	}
}

//void CRtspClient::rtsp_keep_alive()
//{
//	uint32 cur_time = sys_os_get_uptime();
//	
//	if (cur_time - m_rua.keepalive_time >= (uint32)(m_rua.session_timeout - 5))
//	{
//		m_rua.keepalive_time = cur_time;
//		m_rua.cseq++;
//		
//		HRTSP_MSG * tx_msg;
//
//		if (m_rua.gp_cmd) // the rtsp server supports GET_PARAMETER command
//		{
//			tx_msg = rcua_build_get_parameter(&m_rua);
//		}
//		else
//		{
//			tx_msg = rcua_build_options(&m_rua);
//		}
//		
//		if (tx_msg)
//    	{
//    		rcua_send_free_rtsp_msg(&m_rua, tx_msg);
//    	}
//	}
//}


//void CRtspClient::rtsp_keep_alive()
//{
//	uint32 cur_time = sys_os_get_uptime();
//	uint32 keepalive_interval = (m_rua.session_timeout > 20) ? (m_rua.session_timeout / 2) : (m_rua.session_timeout - 5);
//	if (cur_time - m_rua.keepalive_time >= 15)
//		//if (cur_time - m_rua.keepalive_time >= keepalive_interval)
//		//if (cur_time - m_rua.keepalive_time >= (uint32)(m_rua.session_timeout - 5))
//	{
//		m_rua.keepalive_time = cur_time;
//		m_rua.cseq++;
//
//		HRTSP_MSG* tx_msg;
//		// Change to ue OPTIONS command to keep alive
//		tx_msg = rcua_build_options(&m_rua);
//		if (!tx_msg) {
//			tx_msg = rcua_build_get_parameter(&m_rua);
//		}
//		if (tx_msg)
//		{
//			rcua_send_free_rtsp_msg(&m_rua, tx_msg);
//		}
//
//	}
//}

void CRtspClient::tcp_rx_thread()
{
	int  ret;
	int  tm_count = 0;
    BOOL nodata_notify = FALSE;
#ifdef EPOLL
    int  fd;
    uint64 e_dat;
#endif

	send_notify(RTSP_EVE_CONNECTING);

#ifdef OVER_HTTP
    if (m_rua.over_http)
    {
        ret = rtsp_over_http_start();
    }
    else 
#endif
#ifdef OVER_WEBSOCKET
    if (m_rua.over_ws)
    {
        ret = rtsp_over_ws_start();
    }
    else 
#endif
    ret = rtsp_client_start();
    
	if (!ret)	
	{
		send_notify(RTSP_EVE_CONNFAIL);
		goto rtsp_rx_exit;
	}

#ifdef EPOLL
    
#ifdef OVER_HTTP    
    if (m_rua.over_http)
    {
        fd = m_rua.rtsp_recv.cfd;
    }
    else
#endif
#ifdef OVER_WEBSOCKET
    if (m_rua.over_ws)
    {
        fd = m_rua.ws_http.cfd;
    }
    else
#endif
    fd = m_rua.fd;
    
    e_dat = fd;
    e_dat |= ((uint64)1 << 63);

    struct epoll_event event;
    event.events = EPOLLIN;
    event.data.u64 = e_dat;
    epoll_ctl(m_ep_fd, EPOLL_CTL_ADD, fd, &event);
#endif

	while (m_bRunning)
	{
#ifdef EPOLL
        ret = rtsp_epoll_rx();
        if (ret == RTSP_RX_FAIL)
	    {
	        break;
	    }
	    else if (ret == RTSP_RX_TIMEOUT)
    	{
    		tm_count++;
	        if (tm_count >= m_nRxTimeout && !nodata_notify)    // in 10s without data
	        {
	            nodata_notify = TRUE;
	            send_notify(RTSP_EVE_NODATA);
	        }
        }
        else // should be RTSP_RX_SUCC
        {
        	if (nodata_notify)
	        {
	            nodata_notify = FALSE;
	            send_notify(RTSP_EVE_RESUME);
	        }
	        
        	tm_count = 0;
        }
#else
	    ret = rtsp_tcp_rx();
	    if (ret == RTSP_RX_FAIL)
	    {
	        break;
	    }
	    else if (m_rua.rtp_tcp)
	    {
		    if (ret == RTSP_RX_TIMEOUT)
	    	{
	    		tm_count++;
		        if (tm_count >= m_nRxTimeout && !nodata_notify)    // in 10s without data
		        {
		            nodata_notify = TRUE;
		            send_notify(RTSP_EVE_NODATA);
		        }
	        }
	        else // should be RTSP_RX_SUCC
	        {
	        	if (nodata_notify)
		        {
		            nodata_notify = FALSE;
		            send_notify(RTSP_EVE_RESUME);
		        }
		        
	        	tm_count = 0;
	        }
        }
        else
        {
        	if (ret == RTSP_RX_TIMEOUT)
        	{
        		usleep(100*1000);
        	}
        }
#endif

	    if (m_rua.state == RCS_PLAYING)
	    {	    	
	    	rtsp_keep_alive();
	    }
	}

    if (m_rua.fd > 0)
	{
#ifdef EPOLL    	
        epoll_ctl(m_ep_fd, EPOLL_CTL_DEL, m_rua.fd, NULL);
#endif

		closesocket(m_rua.fd);
		m_rua.fd = 0;
	}

    if (m_rua.rtp_rcv_buf)
    {
        free(m_rua.rtp_rcv_buf);
        m_rua.rtp_rcv_buf = NULL;
    }
    
	send_notify(RTSP_EVE_STOPPED);

rtsp_rx_exit:

	m_tcpRxTid = 0;
	log_print(HT_LOG_DBG, "%s, exit\r\n", __FUNCTION__);
}

void CRtspClient::udp_rx_thread()
{
	int  ret;
	int  tm_count = 0;
    BOOL nodata_notify = FALSE;
    
    while (m_bRunning)
	{
	    ret = rtsp_udp_rx();
	    if (ret == RTSP_RX_FAIL)
	    {
	        break;
	    }
	    else if (ret == RTSP_RX_TIMEOUT)
    	{
    		tm_count++;
	        if (tm_count >= m_nRxTimeout && !nodata_notify)    // in 10s without data
	        {
	            nodata_notify = TRUE;
	            send_notify(RTSP_EVE_NODATA);
	        }
        }
        else // should be RTSP_RX_SUCC
        {
        	if (nodata_notify)
	        {
	            nodata_notify = FALSE;
	            send_notify(RTSP_EVE_RESUME);
	        }
	        
        	tm_count = 0;
        }
	}

	m_udpRxTid = 0;
	
	log_print(HT_LOG_DBG, "%s, exit\r\n", __FUNCTION__);
}

void CRtspClient::send_notify(int event)
{
	sys_os_mutex_enter(m_pMutex);
	
	if (m_pNotify)
	{
		m_pNotify(event, m_pUserdata);
	}

	sys_os_mutex_leave(m_pMutex);
}

void CRtspClient::get_h264_params()
{
	rtsp_send_h264_params(&m_rua);
}

BOOL CRtspClient::get_h264_params(uint8 * p_sps, int * sps_len, uint8 * p_pps, int * pps_len)
{
	if (h264rxi.param_sets.sps_len > 0 && h264rxi.param_sets.pps_len > 0)
	{
	    memcpy(p_sps, h264rxi.param_sets.sps, h264rxi.param_sets.sps_len);
	    *sps_len = h264rxi.param_sets.sps_len;

	    memcpy(p_pps, h264rxi.param_sets.pps, h264rxi.param_sets.pps_len);
	    *pps_len = h264rxi.param_sets.pps_len;

	    return TRUE;
	}

	char sps[1000] = {'\0'}, pps[1000] = {'\0'};
	
	if (!rcua_get_sdp_h264_params(&m_rua, NULL, sps, sizeof(sps)))
	{
		return FALSE;
	}

	char * ptr = strchr(sps, ',');
	if (ptr && ptr[1] != '\0')
	{
		*ptr = '\0';
		strcpy(pps, ptr+1);
	}

	uint8 sps_pps[1000];
	sps_pps[0] = 0x0;
	sps_pps[1] = 0x0;
	sps_pps[2] = 0x0;
	sps_pps[3] = 0x1;
	
	int len = base64_decode(sps, strlen(sps), sps_pps+4, sizeof(sps_pps)-4);
	if (len <= 0)
	{
		return FALSE;
	}

    if ((sps_pps[4] & 0x1f) == 7)
    {
	    memcpy(p_sps, sps_pps, len+4);
	    *sps_len = len+4;
	}
	else if ((sps_pps[4] & 0x1f) == 8)
	{
	    memcpy(p_pps, sps_pps, len+4);
		*pps_len = len+4;
	}
	
	if (pps[0] != '\0')
	{		
		len = base64_decode(pps, strlen(pps), sps_pps+4, sizeof(sps_pps)-4);
		if (len > 0)
		{
		    if ((sps_pps[4] & 0x1f) == 7)
            {
        	    memcpy(p_sps, sps_pps, len+4);
        	    *sps_len = len+4;
        	}
        	else if ((sps_pps[4] & 0x1f) == 8)
        	{
        	    memcpy(p_pps, sps_pps, len+4);
        		*pps_len = len+4;
        	}
		}
	}

	return TRUE;
}

BOOL CRtspClient::get_h264_sdp_desc(char * p_sdp, int max_len)
{
	return rcua_get_sdp_h264_desc(&m_rua, NULL, p_sdp, max_len);
}

BOOL CRtspClient::get_h265_sdp_desc(char * p_sdp, int max_len)
{
	return rcua_get_sdp_h265_desc(&m_rua, NULL, p_sdp, max_len);
}

BOOL CRtspClient::get_mp4_sdp_desc(char * p_sdp, int max_len)
{	
	return rcua_get_sdp_mp4_desc(&m_rua, NULL, p_sdp, max_len);
}

BOOL CRtspClient::get_aac_sdp_desc(char * p_sdp, int max_len)
{
	return rcua_get_sdp_aac_desc(&m_rua, NULL, p_sdp, max_len);
}

void CRtspClient::get_h265_params()
{
	rtsp_send_h265_params(&m_rua);
}

BOOL CRtspClient::get_h265_params(uint8 * p_sps, int * sps_len, uint8 * p_pps, int * pps_len, uint8 * p_vps, int * vps_len)
{
    if (h265rxi.param_sets.sps_len > 0 && h265rxi.param_sets.pps_len > 0 && h265rxi.param_sets.vps_len > 0)
	{
	    memcpy(p_sps, h265rxi.param_sets.sps, h265rxi.param_sets.sps_len);
	    *sps_len = h265rxi.param_sets.sps_len;

	    memcpy(p_pps, h265rxi.param_sets.pps, h265rxi.param_sets.pps_len);
	    *pps_len = h265rxi.param_sets.pps_len;

        memcpy(p_vps, h265rxi.param_sets.vps, h265rxi.param_sets.vps_len);
	    *vps_len = h265rxi.param_sets.vps_len;
	    
	    return TRUE;
	}
	
    int pt;
    BOOL don;
	char vps[1000] = {'\0'}, sps[1000] = {'\0'}, pps[1000] = {'\0'};
	
	if (!rcua_get_sdp_h265_params(&m_rua, &pt, &don, vps, sizeof(vps), sps, sizeof(sps), pps, sizeof(pps)))
	{
		return FALSE;
	}

	uint8 buff[1000];
	buff[0] = 0x0;
	buff[1] = 0x0;
	buff[2] = 0x0;
	buff[3] = 0x1;
	
	if (vps[0] != '\0')
	{
		int len = base64_decode(vps, strlen(vps), buff+4, sizeof(buff)-4);
		if (len <= 0)
		{
			return FALSE;
		}

        memcpy(p_vps, buff, len+4);
	    *vps_len = len+4;
	}

	if (sps[0] != '\0')
	{
		int len = base64_decode(sps, strlen(sps), buff+4, sizeof(buff)-4);
		if (len <= 0)
		{
			return FALSE;
		}

		memcpy(p_sps, buff, len+4);
	    *sps_len = len+4;
	}

	if (pps[0] != '\0')
	{
		int len = base64_decode(pps, strlen(pps), buff+4, sizeof(buff)-4);
		if (len <= 0)
		{
			return FALSE;
		}

		memcpy(p_pps, buff, len+4);
	    *pps_len = len+4;
	}

	return TRUE;
}	

void CRtspClient::rtsp_get_video_size(int * w, int * h)
{
    int i;

    for (i = 0; i < MAX_AVN; i++)
    {
        char * sdpline = m_rua.channels[AV_VIDEO_CH].cap_desc[i];
        
        if (sdpline[0] == '\0')
        {
            break;
        }
        
        if (strncasecmp(sdpline, "a=x-dimensions:", 15) == 0)
        {
            int width, height;
            
            if (sscanf(sdpline, "a=x-dimensions:%d,%d", &width, &height) == 2) 
            {
                if (w) *w = width;
                if (h) *h = height;

                break;
            }
        }
    }
}

BOOL CRtspClient::rtsp_get_video_media_info()
{
	if (m_rua.channels[AV_VIDEO_CH].cap_count == 0)
	{
		return FALSE;
	}

	if (m_rua.channels[AV_VIDEO_CH].cap[0] == 26)
	{
		m_rua.video_codec = VIDEO_CODEC_JPEG;
	}

	int i;
	int rtpmap_len = (int)strlen("a=rtpmap:");

	for (i=0; i<MAX_AVN; i++)
	{
		char * ptr = m_rua.channels[AV_VIDEO_CH].cap_desc[i];
		if (memcmp(ptr, "a=rtpmap:", rtpmap_len) == 0)
		{
			char pt_buf[16];
			char code_buf[64];
			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 (strcasecmp(code_buf, "H264/90000") == 0)
			{
				m_rua.video_codec = VIDEO_CODEC_H264;
			}
			else if (strcasecmp(code_buf, "JPEG/90000") == 0)
			{
				m_rua.video_codec = VIDEO_CODEC_JPEG;
			}
			else if (strcasecmp(code_buf, "MP4V-ES/90000") == 0)
			{
				m_rua.video_codec = VIDEO_CODEC_MP4;
			}
			else if (strcasecmp(code_buf, "H265/90000") == 0)
			{
				m_rua.video_codec = VIDEO_CODEC_H265;
			}

			break;
		}
	}

	return TRUE;
}

BOOL CRtspClient::rtsp_get_audio_media_info()
{
	if (m_rua.channels[AV_AUDIO_CH].cap_count == 0)
	{
		return FALSE;
	}

	if (m_rua.channels[AV_AUDIO_CH].cap[0] == 0)
	{
		m_rua.audio_codec = AUDIO_CODEC_G711U;
	}
	else if (m_rua.channels[AV_AUDIO_CH].cap[0] == 8)
	{
		m_rua.audio_codec = AUDIO_CODEC_G711A;
	}
	else if (m_rua.channels[AV_AUDIO_CH].cap[0] == 9)
	{
		m_rua.audio_codec = AUDIO_CODEC_G722;
	}

	int i;
	int rtpmap_len = (int)strlen("a=rtpmap:");

	for (i=0; i<MAX_AVN; i++)
	{
		char * ptr = m_rua.channels[AV_AUDIO_CH].cap_desc[i];
		if (memcmp(ptr, "a=rtpmap:", rtpmap_len) == 0)
		{
			char pt_buf[16];
			char code_buf[64];
			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);

			uppercase(code_buf);

			if (strstr(code_buf, "G726"))
			{
				m_rua.audio_codec = AUDIO_CODEC_G726;
				m_rua.bit_per_sample = atoi(code_buf+5) / 8; // G726-16, G726-24,G726-32,G726-48

				if (m_rua.bit_per_sample == 0)
				{
				    m_rua.bit_per_sample = 2;
				}
			}
			else if (strstr(code_buf, "G722"))
			{
				m_rua.audio_codec = AUDIO_CODEC_G722;
			}
			else if (strstr(code_buf, "PCMU"))
			{
				m_rua.audio_codec = AUDIO_CODEC_G711U;
			}
			else if (strstr(code_buf, "PCMA"))
			{
				m_rua.audio_codec = AUDIO_CODEC_G711A;
			}
			else if (strstr(code_buf, "MPEG4-GENERIC"))
			{
				m_rua.audio_codec = AUDIO_CODEC_AAC;
			}
			else if (strstr(code_buf, "OPUS"))
			{
				m_rua.audio_codec = AUDIO_CODEC_OPUS;
			}

			char * p = strchr(code_buf, '/');
			if (p)
			{
				p++;
				
				char * p1 = strchr(p, '/');
				if (p1)
				{
					*p1 = '\0';
					m_rua.sample_rate = atoi(p);

					p1++;
					if (p1 && *p1 != '\0')
					{
						m_rua.audio_channels = atoi(p1);
					}
					else
					{
						m_rua.audio_channels = 1;
					}
				}
				else
				{
					m_rua.sample_rate = atoi(p);
					m_rua.audio_channels = 1;
				}
			}

			break;
		}
	}

    if (m_rua.audio_codec == AUDIO_CODEC_G722)
    {
        m_rua.sample_rate = 16000;
        m_rua.audio_channels = 1;
    }
    
    if (m_rua.audio_codec == AUDIO_CODEC_OPUS)
    {
        m_rua.sample_rate = 48000;
    }
    
	return TRUE;
}

void CRtspClient::set_notify_cb(notify_cb notify, void * userdata) 
{
	sys_os_mutex_enter(m_pMutex);	
	m_pNotify = notify;
	m_pUserdata = userdata;
	sys_os_mutex_leave(m_pMutex);
}

void CRtspClient::set_video_cb(video_cb cb) 
{
	sys_os_mutex_enter(m_pMutex);
	m_pVideoCB = cb;
	sys_os_mutex_leave(m_pMutex);
}

void CRtspClient::set_audio_cb(audio_cb cb)
{
	sys_os_mutex_enter(m_pMutex);
	m_pAudioCB = cb;
	sys_os_mutex_leave(m_pMutex);
}

void CRtspClient::set_rtcp_cb(rtcp_cb cb)
{
    sys_os_mutex_enter(m_pMutex);
	m_pRtcpCB = cb;
	sys_os_mutex_leave(m_pMutex);
}

void CRtspClient::set_channel(int channel, int flag)
{
    if (channel >= 0 && channel < AV_MAX_CHS)
    {
        m_rua.channels[channel].disabled = !flag;
    }
}

void CRtspClient::set_rtp_multicast(int flag)
{
    if (flag)
	{
		m_rua.rtp_tcp = 0;
	}
	
	m_rua.mcast_flag = flag;
}

void CRtspClient::set_rtp_over_udp(int flag)
{
    if (flag)
    {
        m_rua.rtp_tcp = 0;
    }
    else
    {
        m_rua.rtp_tcp = 1;
    }
}

void CRtspClient::set_rx_timeout(int timeout)
{
    m_nRxTimeout = timeout;
}

void CRtspClient::set_conn_timeout(int timeout)
{
    m_nConnTimeout = timeout;
}


