Files
ANSCORE/MediaClient/rtsp/rtsp_cln.cpp

3599 lines
77 KiB
C++

/***************************************************************************************
*
* 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;
}