/***************************************************************************************
 *
 *  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 "srt_cln.h"
#include "h264.h"
#include "h265.h"
#include "media_format.h"
#include "bs.h"
#include "media_util.h"


void * srt_rx_thread(void * argv)
{
    CSrtClient * pSrtClient = (CSrtClient *) argv;

    pSrtClient->rx_thread();

    return NULL;
}

void ts_payload_cb(uint8 * data, uint32 size, uint32 type, uint64 pts, void * userdata)
{
    CSrtClient * pSrtClient = (CSrtClient *) userdata;

    pSrtClient->srt_payload_cb(data, size, type, pts);
}

CSrtClient::CSrtClient()
: m_nport(0)
, m_fd(0)
, m_eid(0)
, m_bRunning(FALSE)
, m_bVideoReady(FALSE)
, m_bAudioReady(FALSE)
, m_tidRx(0)
, m_nVpsLen(0)
, m_nSpsLen(0)
, m_nPpsLen(0)
, m_nAudioConfigLen(0)
, m_nVideoCodec(VIDEO_CODEC_NONE)
, m_nWidth(0)
, m_nHeight(0)
, m_nFrameRate(0)
, m_nAudioCodec(AUDIO_CODEC_NONE)
, m_nSamplerate(44100)
, m_nChannels(2)
, m_nVideoInitTS(0)
, m_nAudioInitTS(0)
, m_pNotify(NULL)
, m_pUserdata(NULL)
, m_pVideoCB(NULL)
, m_pAudioCB(NULL)
{
    memset(m_url, 0, sizeof(m_url));
    memset(m_user, 0, sizeof(m_user));
    memset(m_pass, 0, sizeof(m_pass));
    memset(m_ip, 0, sizeof(m_ip));
    memset(m_streamid, 0, sizeof(m_streamid));

    memset(&m_pVps, 0, sizeof(m_pVps));
    memset(&m_pSps, 0, sizeof(m_pVps));
    memset(&m_pPps, 0, sizeof(m_pVps));
    memset(&m_pAudioConfig, 0, sizeof(m_pAudioConfig));
	
	m_pMutex = sys_os_create_mutex();

	m_nRxTimeout = 10;  // default 10s timeout

	ts_parser_init(&m_tsParser);

	ts_parser_set_cb(&m_tsParser, ts_payload_cb, this);
}

CSrtClient::~CSrtClient()
{
    srt_close();

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

    ts_parser_free(&m_tsParser);
}

BOOL CSrtClient::srt_start(const char * url, const char * user, const char * pass)
{
    int port = 0;
    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, "srt") != 0)
    {
        return FALSE;
    }
    
    if (port <= 0)
    {
        return FALSE;
    }

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

    m_nport = port;

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

    char * p = strstr(path, "streamid");
    if (p)
    {
        p += strlen("streamid=");
        strncpy(m_streamid, p, sizeof(m_streamid)-1);
    }
    
    if (url && url[0] != '\0')
    {
        strncpy(m_url, url, sizeof(m_url)-1);
    }

    if (user && user[0] != '\0')
    {
        strncpy(m_user, user, sizeof(m_user)-1);
    }

    if (pass && pass[0] != '\0')
    {
        strncpy(m_pass, pass, sizeof(m_pass)-1);
    }

    log_print(HT_LOG_INFO, "%s, url=%s, ip=%s, port=%d, streamid=%s\r\n",
        __FUNCTION__, m_url, m_ip, m_nport, m_streamid);
    
    m_bRunning = TRUE;
    m_tidRx = sys_os_create_thread((void *)srt_rx_thread, this);
    
    return TRUE;
}

BOOL CSrtClient::srt_play()
{
    return TRUE;
}

BOOL CSrtClient::srt_stop()
{
    return srt_close();
}

BOOL CSrtClient::srt_pause()
{
    return FALSE;
}

BOOL CSrtClient::srt_close()
{
    sys_os_mutex_enter(m_pMutex);
	m_pAudioCB = NULL;
	m_pVideoCB = NULL;
	m_pNotify = NULL;
	m_pUserdata = NULL;
	sys_os_mutex_leave(m_pMutex);
    
    m_bRunning = FALSE;
    
    while (m_tidRx)
    {
        usleep(10*1000);
    }
    
    if (m_fd)
    {
        ::srt_close(m_fd);
        m_fd = 0;
    }

    if (m_eid)
    {
        srt_epoll_release(m_eid);
        m_eid = 0;
    }

    m_bVideoReady = FALSE;
    m_bAudioReady = FALSE;
    
    return TRUE;
}

BOOL CSrtClient::srt_connect()
{
    srt_send_notify(SRT_EVE_CONNECTING);
    
    SRTSOCKET fd = srt_create_socket();

    if (srt_setsockopt(fd, 0, SRTO_STREAMID, m_streamid, strlen(m_streamid)) < 0) 
    {
    	log_print(HT_LOG_ERR, "%s, srt_setsockopt SRTO_STREAMID failure. err=%s\r\n", 
    	    __FUNCTION__, srt_getlasterror_str());
    	::srt_close(fd);    
        return FALSE;
    }

    struct sockaddr_in sa;
    memset(&sa, 0, sizeof sa);
    sa.sin_family = AF_INET;
    sa.sin_port = htons(m_nport);
    sa.sin_addr.s_addr = get_address_by_name(m_ip);

    struct sockaddr *psa = (struct sockaddr *) &sa;
    
    int status = ::srt_connect(fd, psa, sizeof sa);
    if (status == SRT_ERROR) 
    {
    	log_print(HT_LOG_ERR, "%s, srt_connect failure. ip=%s, port=%d\r\n", 
    	    __FUNCTION__, m_ip, m_nport);
    	::srt_close(fd);    
        return FALSE;
    }

    m_eid = srt_epoll_create();

    int modes = SRT_EPOLL_IN | SRT_EPOLL_ERR;
    
    int ret = srt_epoll_add_usock(m_eid, fd, &modes);
    if (ret < 0) 
    {
        log_print(HT_LOG_ERR, "%s, srt_epoll_add_usock failed, epoll=%d, fd=%d, modes=%d, %s\r\n",
            __FUNCTION__, m_eid, fd, modes, srt_getlasterror_str());
        ::srt_close(fd);    
        return FALSE;
    }

    m_fd = fd;

    return TRUE;
}

void CSrtClient::rx_thread()
{
    if (!srt_connect())
    {
        m_tidRx = 0;
        srt_send_notify(SRT_EVE_CONNFAIL);
        return;
    }

#define TS_UDP_LEN 1316 //7*188

    int  tm_count = 0;
    BOOL nodata_notify = FALSE;
    char buff[TS_UDP_LEN];
    
    while (m_bRunning)
    {
        SRTSOCKET read_socks[1];
        int read_len = 1;
        
        int ret = srt_epoll_wait(m_eid, read_socks, &read_len, NULL, NULL, 1000, 0, 0, 0, 0);
	    if (ret < 0) 
	    {
	    	ret = srt_getlasterror(NULL);
            if (ret == SRT_ETIMEOUT)
            {
                tm_count++;
    	        if (tm_count >= m_nRxTimeout && !nodata_notify)    // in 10s without data
    	        {
    	            nodata_notify = TRUE;
    	            srt_send_notify(SRT_EVE_NODATA);
    	        }

    	        continue;
            }    
            else
            {
                log_print(HT_LOG_ERR, "%s, srt_epoll_wait failed. err=%d\r\n", __FUNCTION__, ret);
                break;
            }
	    }
	    
	    if (read_len <= 0) 
	    {
	    	continue;
	    }

	    SRT_SOCKSTATUS status = srt_getsockstate(m_fd);
        if (status == SRTS_BROKEN || 
            status == SRTS_NONEXIST || 
            status == SRTS_CLOSED)
        {
            srt_epoll_remove_usock(m_eid, m_fd);
            break;
        }
	    
        ret = srt_recvmsg(m_fd, buff, TS_UDP_LEN);
        if (ret < 0) 
        {
            log_print(HT_LOG_WARN, "%s, srt_recvmsg failed, sock=%d, ret=%d, err=%s\r\n",
                __FUNCTION__, m_fd, ret, srt_getlasterror_str());
            continue;
        }

        if (nodata_notify)
        {
            nodata_notify = FALSE;
            srt_send_notify(SRT_EVE_RESUME);
        }
	        
        tm_count = 0;

        ts_parser_parse(&m_tsParser, (uint8*)buff, ret);
    }
    
    m_tidRx = 0;

    srt_send_notify(SRT_EVE_STOPPED);
}

void CSrtClient::srt_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 CSrtClient::srt_h264_rx(uint8 * p_data, uint32 len, uint64 pts)
{
    int s_len = 0, n_len = 0, parse_len = len;
    uint8 nalu_t;
	uint8 * p_cur = p_data;
	uint8 * p_end = p_data + len;
    
	while (p_cur && p_cur < p_end && parse_len > 0)
	{
		uint8 * p_next = avc_split_nalu(p_cur, parse_len, &s_len, &n_len);
        
		nalu_t = (p_cur[s_len] & 0x1F);

		if (nalu_t == H264_NAL_SPS && m_nSpsLen == 0)	
		{
		    if (n_len <= (int)sizeof(m_pSps))
		    {
    			memcpy(m_pSps, p_cur, n_len);
    			m_nSpsLen = n_len;
			}

			video_data_cb(p_cur, n_len, pts);
		}
		else if (nalu_t == H264_NAL_PPS && m_nPpsLen == 0)	
		{
		    if (n_len <= (int)sizeof(m_pPps))
		    {
    			memcpy(m_pPps, p_cur, n_len);
    			m_nPpsLen = n_len;
			}

			video_data_cb(p_cur, n_len, pts);
		}
		else if (nalu_t == H264_NAL_AUD || nalu_t == H264_NAL_SEI)
		{
		   // skip  
		}
		else
		{
		    video_data_cb(p_cur, n_len, pts);
		}
		
		parse_len -= n_len;
		p_cur = p_next;
	}
}

void CSrtClient::srt_h265_rx(uint8 * p_data, uint32 len, uint64 pts)
{
    int s_len = 0, n_len = 0, parse_len = len;
    uint8 nalu_t;
	uint8 * p_cur = p_data;
	uint8 * p_end = p_data + len;
    
	while (p_cur && p_cur < p_end && parse_len > 0)
	{
		uint8 * p_next = avc_split_nalu(p_cur, parse_len, &s_len, &n_len);
        
		nalu_t = (p_cur[s_len] >> 1) & 0x3F;

        if (nalu_t == HEVC_NAL_VPS && m_nVpsLen == 0)	
		{
		    if (n_len <= (int)sizeof(m_pVps))
		    {
    			memcpy(m_pVps, p_cur, n_len);
    			m_nVpsLen = n_len;
			}

			video_data_cb(p_cur, n_len, pts);
		}
		else if (nalu_t == HEVC_NAL_SPS && m_nSpsLen == 0)	
		{
		    if (n_len <= (int)sizeof(m_pSps))
		    {
    			memcpy(m_pSps, p_cur, n_len);
    			m_nSpsLen = n_len;
			}

			video_data_cb(p_cur, n_len, pts);
		}
		else if (nalu_t == HEVC_NAL_PPS && m_nPpsLen == 0)	
		{
		    if (n_len <= (int)sizeof(m_pPps))
		    {
    			memcpy(m_pPps, p_cur, n_len);
    			m_nPpsLen = n_len;
			}

			video_data_cb(p_cur, n_len, pts);
		}
		else if (nalu_t == HEVC_NAL_AUD || 
		    nalu_t == HEVC_NAL_SEI_PREFIX ||
		    nalu_t == HEVC_NAL_SEI_SUFFIX)
		{
		    // skip
		}
		else
		{
		    video_data_cb(p_cur, n_len, pts);
		}
		
		parse_len -= n_len;
		p_cur = p_next;
	}
}

BOOL CSrtClient::srt_parse_adts(uint8 * p_data, uint32 len)
{
    bs_t bs;
    bs_init(&bs, p_data, len);
    
    if (bs_read(&bs, 12) != 0xfff)
    {
        return FALSE;
    }

    const int audio_sample_rates[16] = {
        96000, 88200, 64000, 48000, 44100, 32000,
        24000, 22050, 16000, 12000, 11025, 8000, 7350
    };

    int sr, ch, size;
    
    bs_skip(&bs, 1);            /* id */
    bs_skip(&bs, 2);            /* layer */
    bs_skip(&bs, 1);            /* protection_absent */
    bs_skip(&bs, 2);            /* profile_objecttype */
    sr = bs_read(&bs, 4);       /* sample_frequency_index */
    if (!audio_sample_rates[sr])
    {
        return FALSE;
    }
    
    bs_skip(&bs, 1);            /* private_bit */
    ch = bs_read(&bs, 3);       /* channel_configuration */

    bs_skip(&bs, 1);            /* original/copy */
    bs_skip(&bs, 1);            /* home */

    /* adts_variable_header */
    bs_skip(&bs, 1);            /* copyright_identification_bit */
    bs_skip(&bs, 1);            /* copyright_identification_start */
    size = bs_read(&bs, 13);    /* aac_frame_length */
    if (size < 7)
    {
        return FALSE;
    }
    
    bs_skip(&bs, 11);           /* adts_buffer_fullness */
    bs_skip(&bs, 2);            /* number_of_raw_data_blocks_in_frame */

    m_nSamplerate = audio_sample_rates[sr];
    m_nChannels = ch;
    
    return TRUE;
}

void CSrtClient::srt_payload_cb(uint8 * p_data, uint32 len, uint32 type, uint64 pts)
{
    if (type == TS_VIDEO_H264)
    {
        if (!m_bVideoReady)
        {
            m_nVideoCodec = VIDEO_CODEC_H264;
            m_bVideoReady = TRUE;
            srt_send_notify(SRT_EVE_VIDEOREADY);
        }

        srt_h264_rx(p_data, len, pts);
    }
    else if (type == TS_VIDEO_HEVC)
    {
        if (!m_bVideoReady)
        {
            m_nVideoCodec = VIDEO_CODEC_H265;
            m_bVideoReady = TRUE;
            srt_send_notify(SRT_EVE_VIDEOREADY);
        }

        srt_h265_rx(p_data, len, pts);
    }
    else if (type == TS_VIDEO_MPEG4)
    {
        if (!m_bVideoReady)
        {
            m_nVideoCodec = VIDEO_CODEC_MP4;
            m_bVideoReady = TRUE;
            srt_send_notify(SRT_EVE_VIDEOREADY);
        }

        video_data_cb(p_data, len, pts);
    }
    else if (type == TS_AUDIO_AAC)
    {
        if (!m_bAudioReady)
        {
            srt_parse_adts(p_data, len);
            
            m_nAudioCodec = AUDIO_CODEC_AAC;
            m_bAudioReady = TRUE;
            srt_send_notify(SRT_EVE_AUDIOREADY);
        }
        
        audio_data_cb(p_data, len, pts);
    }
}

void CSrtClient::video_data_cb(uint8 * p_data, int len, uint32 ts)
{
    if (m_nVideoInitTS == 0 && ts != 0)
    {
        m_nVideoInitTS = ts;
    }

	sys_os_mutex_enter(m_pMutex);
	if (m_pVideoCB)
	{
		m_pVideoCB(p_data, len, ts, m_pUserdata);
	}
	sys_os_mutex_leave(m_pMutex);
}

void CSrtClient::audio_data_cb(uint8 * p_data, int len, uint32 ts)
{
    if (m_nAudioInitTS == 0 && ts != 0)
    {
        m_nAudioInitTS = ts;
    }
    
	sys_os_mutex_enter(m_pMutex);
	if (m_pAudioCB)
	{
		m_pAudioCB(p_data, len, ts, m_pUserdata);
	}
	sys_os_mutex_leave(m_pMutex);
}

void CSrtClient::get_h264_params()
{
    if (m_nSpsLen > 0)
    {
        video_data_cb(m_pSps, m_nSpsLen, 0);
    }

    if (m_nPpsLen > 0)
    {
        video_data_cb(m_pPps, m_nPpsLen, 0);
    }
}

BOOL CSrtClient::get_h264_params(uint8 * p_sps, int * sps_len, uint8 * p_pps, int * pps_len)
{
    if (m_nSpsLen > 0)
    {
        *sps_len = m_nSpsLen;
        memcpy(p_sps, m_pSps, m_nSpsLen);
    }

    if (m_nPpsLen > 0)
    {
        *pps_len = m_nPpsLen;
        memcpy(p_pps, m_pPps, m_nPpsLen);
    }

    return TRUE;
}

void CSrtClient::get_h265_params()
{
    if (m_nVpsLen > 0)
    {
        video_data_cb(m_pVps, m_nVpsLen, 0);
    }
    
    if (m_nSpsLen > 0)
    {
        video_data_cb(m_pSps, m_nSpsLen, 0);
    }

    if (m_nPpsLen > 0)
    {
        video_data_cb(m_pPps, m_nPpsLen, 0);
    }
}

BOOL CSrtClient::get_h265_params(uint8 * p_sps, int * sps_len, uint8 * p_pps, int * pps_len, uint8 * p_vps, int * vps_len)
{
    if (m_nVpsLen > 0)
    {
        *vps_len = m_nVpsLen;
        memcpy(p_vps, m_pVps, m_nVpsLen);
    }
    
    if (m_nSpsLen > 0)
    {
        *sps_len = m_nSpsLen;
        memcpy(p_sps, m_pSps, m_nSpsLen);
    }

    if (m_nPpsLen > 0)
    {
        *pps_len = m_nPpsLen;
        memcpy(p_pps, m_pPps, m_nPpsLen);
    }

    return TRUE;
}

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

void CSrtClient::set_video_cb(srt_video_cb cb) 
{
	sys_os_mutex_enter(m_pMutex);
	m_pVideoCB = cb;
	sys_os_mutex_leave(m_pMutex);
}

void CSrtClient::set_audio_cb(srt_audio_cb cb)
{
	sys_os_mutex_enter(m_pMutex);
	m_pAudioCB = cb;
	sys_os_mutex_leave(m_pMutex);
}

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


