/*************************************************************************************** * * 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 "rtsp_backchannel.h" #include "rtsp_rcua.h" #include "rtp.h" #include "rtsp_util.h" #include "base64.h" #include "http_cln.h" #include "media_format.h" #ifdef OVER_WEBSOCKET #include "rtsp_ws.h" #endif #ifdef BACKCHANNEL /***************************************************************************************/ /** * Send rtp data by TCP socket * * @param p_rua rtsp user agent * @param p_data rtp data * @param len rtp data len * @return -1 on error, or the data length has been sent */ int rtp_tcp_tx(RCUA * p_rua, uint8 * p_data, int len) { int offset = 0; SOCKET fd; int buflen; char * p_buff; #ifdef OVER_HTTP if (p_rua->over_http) { fd = p_rua->rtsp_send.cfd; } else #endif #ifdef OVER_WEBSOCKET if (p_rua->over_ws) { fd = p_rua->ws_http.cfd; } else #endif fd = p_rua->fd; if (fd <= 0) { return -1; } #ifdef OVER_HTTP if (p_rua->over_http) { char * tx_buf_base64 = (char *)malloc(2 * len); if (NULL == tx_buf_base64) { return -1; } if (0 == base64_encode(p_data, len, tx_buf_base64, 2 * len)) { free(tx_buf_base64); return -1; } p_buff = tx_buf_base64; buflen = strlen(tx_buf_base64); } else #endif #ifdef OVER_WEBSOCKET if (p_rua->over_ws) { int extra = rtsp_ws_encode_data(p_data, len, 0x82, 1); p_buff = (char *)(p_data - extra); buflen = len + extra; } else #endif { p_buff = (char *)p_data; buflen = len; } while (offset < buflen) { int tlen; #ifdef OVER_HTTP if (p_rua->over_http) { tlen = http_cln_tx(&p_rua->rtsp_send, p_buff+offset, buflen-offset); } else #endif #ifdef OVER_WEBSOCKET if (p_rua->over_ws) { tlen = http_cln_tx(&p_rua->ws_http, p_buff+offset, buflen-offset); } else #endif tlen = send(fd, (const char *)p_buff+offset, buflen-offset, 0); if (tlen > 0) { offset += tlen; } else { int sockerr = sys_os_get_socket_error_num(); if (sockerr == EINTR || sockerr == EAGAIN) { usleep(1000); continue; } log_print(HT_LOG_ERR, "%s, send failed, fd[%d], tlen[%d,%d], err[%d][%s]!!!\r\n", __FUNCTION__, fd, tlen, buflen-offset, sockerr, sys_os_get_socket_error()); break; } } #ifdef OVER_HTTP if (p_rua->over_http) { free(p_buff); } #endif return (offset == buflen) ? len : -1; } int rtp_udp_tx(RCUA * p_rua, int av_t, uint8 * p_data, int len) { int slen; SOCKET fd = 0; struct sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_addr.s_addr = inet_addr(p_rua->ripstr); if (AV_TYPE_BACKCHANNEL == av_t) { addr.sin_port = htons(p_rua->channels[av_t].r_port); fd = p_rua->channels[av_t].udp_fd; if (p_rua->rtp_mcast) { addr.sin_addr.s_addr = inet_addr(p_rua->channels[av_t].destination); } } if (fd <= 0) { return -1; } slen = sendto(fd, (char *)p_data, len, 0, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)); if (slen != len) { log_print(HT_LOG_ERR, "%s, slen = %d, len = %d, ip=0x%08x\r\n", __FUNCTION__, slen, len, p_rua->ripstr); } return slen; } /** * Build audio rtp packet and send * * @param p_rua rtsp user agent * @param p_data payload data * @param len payload data length * @ts the packet timestamp * @return the rtp packet length, -1 on error */ int rtp_audio_build(RCUA * p_rua, uint8 * p_data, int len, uint32 ts, int mbit) { int offset = 0; int slen = 0; uint8 * p_rtp_ptr = p_data - 12; // shift forward RTP head if (p_rua->rtp_tcp) { p_rtp_ptr -= 4; *(p_rtp_ptr+offset) = 0x24; // magic offset++; *(p_rtp_ptr+offset) = p_rua->channels[AV_BACK_CH].interleaved; // channel offset++; offset += rtp_write_uint16(p_rtp_ptr+offset, 12 + len); // rtp payload length } *(p_rtp_ptr+offset) = (RTP_VERSION << 6); offset++; *(p_rtp_ptr+offset) = (p_rua->bc_rtp_info.rtp_pt) | ((mbit & 0x01) << 7); offset++; offset += rtp_write_uint16(p_rtp_ptr+offset, p_rua->bc_rtp_info.rtp_cnt); offset += rtp_write_uint32(p_rtp_ptr+offset, p_rua->bc_rtp_info.rtp_ts); offset += rtp_write_uint32(p_rtp_ptr+offset, p_rua->bc_rtp_info.rtp_ssrc); if (p_rua->rtp_tcp) { slen = rtp_tcp_tx(p_rua, p_rtp_ptr, offset+len); } else { slen = rtp_udp_tx(p_rua, AV_TYPE_BACKCHANNEL, p_rtp_ptr, offset+len); } p_rua->bc_rtp_info.rtp_cnt = (p_rua->bc_rtp_info.rtp_cnt + 1) & 0xFFFF; return (slen == offset+len) ? slen : -1; } /** * Build audio rtp packet and send (not fragment) * * @param p_rua rtsp user agent * @param p_data payload data * @param len payload data length * @ts the packet timestamp * @return the rtp packet length, -1 on error */ int rtp_audio_tx(RCUA * p_rua, uint8 * p_data, int size, uint32 ts) { return rtp_audio_build(p_rua, p_data, size, ts, 1); } int rtp_aac_audio_tx(RCUA * p_rua, uint8 * p_data, int size, uint32 ts) { int ret = 0; int len, max_packet_size, au_size; uint8 *p; max_packet_size = RTP_MAX_LEN; if (p_data[0] == 0xFF && (p_data[1] & 0xF0) == 0xF0) { // skip ADTS header p_data += 7; size -= 7; } p = p_data - 4; au_size = size; while (size > 0) { len = MIN(size, max_packet_size); rtp_write_uint16(p, 2 * 8); rtp_write_uint16(&p[2], au_size * 8); ret = rtp_audio_build(p_rua, p, len + 4, ts, len == size); if (ret < 0) { break; } size -= len; p += len; } return ret; } void rtsp_bc_cb(uint8 *data, int size, int nbsamples, void *pUserdata) { RCUA * p_rua = (RCUA *)pUserdata; if (p_rua->send_bc_data) { uint8 * p_buf = NULL; p_buf = (uint8 *)malloc(size + 40); if (p_buf == NULL) { log_print(HT_LOG_ERR, "%s, malloc failed!!!\r\n", __FUNCTION__); return; } uint8 * p_tbuf = p_buf + 40; // shift forword header memcpy(p_tbuf, data, size); if (AUDIO_CODEC_AAC == p_rua->bc_audio_codec) { rtp_aac_audio_tx(p_rua, p_tbuf, size, rtsp_get_timestamp(nbsamples)); } else { rtp_audio_tx(p_rua, p_tbuf, size, rtsp_get_timestamp(nbsamples)); } free(p_buf); } } #else void rtsp_bc_cb(uint8 *data, int size, int nbsamples, void *pUserdata) { } #endif // BACKCHANNEL