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