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



