/***************************************************************************************
 *
 *  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 "http_flv_cln.h"
#include "h264.h"
#include "h265.h"
#include "media_format.h"
#include "http_cln.h"
#include "http_parse.h"


void * http_flv_rx_thread(void * argv)
{
    CHttpFlvClient * pRtmpClient = (CHttpFlvClient *) argv;

    pRtmpClient->rx_thread();

    return NULL;
}

CHttpFlvClient::CHttpFlvClient()
: m_bRunning(FALSE)
, m_bVideoReady(FALSE)
, m_bAudioReady(FALSE)
, m_bChunked(FALSE)
, m_bFlvHeader(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)
, m_nRxTimeout(10)
{
    memset(m_url, 0, sizeof(m_url));
    memset(m_user, 0, sizeof(m_user));
    memset(m_pass, 0, sizeof(m_pass));

    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));

	memset(&m_http, 0, sizeof(HTTPREQ));
	
	m_pMutex = sys_os_create_mutex();
}

CHttpFlvClient::~CHttpFlvClient()
{
    http_flv_close();

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

BOOL CHttpFlvClient::http_flv_start(const char * url, const char * user, const char * pass)
{
    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);
    }
    
    m_bRunning = TRUE;
    m_tidRx = sys_os_create_thread((void *)http_flv_rx_thread, this);
    
    return TRUE;
}

BOOL CHttpFlvClient::http_flv_play()
{
    return FALSE;
}

BOOL CHttpFlvClient::http_flv_stop()
{
    return http_flv_close();
}

BOOL CHttpFlvClient::http_flv_pause()
{
    return FALSE;
}

BOOL CHttpFlvClient::http_flv_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);
	
#ifdef HTTPS
    if (m_http.https && m_http.ssl)
    {
	    SSL_shutdown(m_http.ssl);
	}
#endif

    if (m_http.cfd > 0)
    {
        closesocket(m_http.cfd);
        m_http.cfd = 0;
    }
    
    m_bRunning = FALSE;
    
    while (m_tidRx)
    {
        usleep(10*1000);
    }
    
    http_cln_free_req(&m_http);

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

BOOL CHttpFlvClient::http_flv_req(HTTPREQ * p_http)
{
    int slen;
	int offset = 0;
	char bufs[2048] = {'\0'};
    int buflen = sizeof(bufs);
	
	offset += snprintf(bufs+offset, buflen-offset, "GET %s HTTP/1.1\r\n", p_http->url);
	offset += snprintf(bufs+offset, buflen-offset, "Host: %s:%u\r\n", p_http->host, p_http->port);
	offset += snprintf(bufs+offset, buflen-offset, "Accept: */*\r\n");
	offset += snprintf(bufs+offset, buflen-offset, "User-Agent: Happytime http-flv 1.0\r\n");

	if (p_http->need_auth)
    {
    	offset += http_build_auth_msg(p_http, "GET", bufs+offset, buflen-offset);        
	}
	
	offset += snprintf(bufs+offset, buflen-offset, "Connection: keep-alive\r\n\r\n");

    log_print(HT_LOG_DBG, "TX >> %s\r\n\r\n", bufs);

    slen = http_cln_tx(p_http, bufs, offset);
    
	return (slen == offset) ? TRUE : FALSE;
}

BOOL CHttpFlvClient::http_flv_connect()
{
    http_flv_send_notify(HTTP_FLV_EVE_CONNECTING);
    
    memset(&m_http, 0, sizeof(HTTPREQ));

    int port;
    char proto[32], username[100], password[100], host[100], path[200];

    url_split(m_url, proto, sizeof(proto), username, sizeof(username), 
        password, sizeof(password), host, sizeof(host), &port, path, sizeof(path));

    if (strcasecmp(proto, "https") == 0)
    {
        m_http.https = 1;
    }
    else if (strcasecmp(proto, "http") != 0)
    {
        log_print(HT_LOG_ERR, "%s, invalid url %s\r\n", __FUNCTION__, m_url);
        return FALSE;
    }

    if (host[0] == '\0')
    {
        log_print(HT_LOG_ERR, "%s, invalid url %s\r\n", __FUNCTION__, m_url);
        return FALSE;
    }

    if (port == -1)
    {
        if (m_http.https)
        {
            port = 443;
        }
        else
        {
            port = 80;
        }
    }
    
	strcpy(m_http.host, host);
	m_http.port = port;
    strcpy(m_http.url, path);

    int timeout = 10*1000;
    
    m_http.cfd = tcp_connect_timeout(get_address_by_name(host), port, timeout);
	if (m_http.cfd <= 0)
	{
	    log_print(HT_LOG_ERR, "%s, connect failed. %s\r\n", __FUNCTION__, m_url);
        return FALSE;
	}

	if (m_http.https)
	{
#ifdef HTTPS	
		if (!http_cln_ssl_conn(&m_http, timeout))
		{
		    http_cln_free_req(&m_http);
		    return FALSE;
		}
#else
        http_cln_free_req(&m_http);
		log_print(HT_LOG_ERR, "%s, the server require ssl connection, unsupport!\r\n", __FUNCTION__);
		return FALSE;
#endif
	}

	if (!http_flv_req(&m_http))
	{
	    http_cln_free_req(&m_http);
	    log_print(HT_LOG_ERR, "%s, send request failed.\r\n", __FUNCTION__);
        return FALSE;
	}

    return TRUE;
}

int CHttpFlvClient::http_flv_h264_rx(uint8 * data, uint32 size, uint32 ts)
{
	if (data[1] == 0x00)   // AVC sequence header
	{
        if (size < 15)
        {
            log_print(HT_LOG_WARN, "%s, dataLen=%d\r\n", __FUNCTION__, size);
            return 0;
        }
    
        uint32 offset = 10;
	    int spsnum = (data[offset++] & 0x1f);
        int i = 0;
        
        while (i < spsnum)
        {
            uint32 spslen = (data[offset] & 0x000000FF) << 8 | (data[offset + 1] & 0x000000FF);
            
            offset += 2;

            if (offset + spslen > size)
            {
                return -1;
            }

            if (0 == m_nSpsLen && spslen <= sizeof(m_pSps) + 4)
            {
                m_pSps[0] = 0;
                m_pSps[1] = 0;
                m_pSps[2] = 0;
                m_pSps[3] = 1;
                m_nSpsLen = spslen + 4;
                memcpy(m_pSps + 4, data + offset, spslen);
            }
            
            offset += spslen;
            i++;
        }
        
        int ppsnum = (data[offset++] & 0x1f);
        i = 0;
        
        while (i < ppsnum)
        {
            uint32 ppslen = (data[offset] & 0x000000FF) << 8 | (data[offset + 1] & 0x000000FF);
            
            offset += 2;

            if (offset + ppslen > size)
            {
                return -1;
            }
            
            if (0 == m_nPpsLen && ppslen <= sizeof(m_pPps) + 4)
            {
                m_pPps[0] = 0;
                m_pPps[1] = 0;
                m_pPps[2] = 0;
                m_pPps[3] = 1;
                m_nPpsLen = ppslen + 4;
                memcpy(m_pPps + 4, data + offset, ppslen);
            }
            
            offset += ppslen;
            i++;
        }

        if (!m_bVideoReady)
        {
            m_bVideoReady = TRUE;
            http_flv_send_notify(HTTP_FLV_EVE_VIDEOREADY);
        }

        if (m_nSpsLen > 0 && m_nPpsLen > 0)
        {
            http_flv_video_data_cb(m_pSps, m_nSpsLen, 0);
            http_flv_video_data_cb(m_pPps, m_nPpsLen, 0);
        }
	}
	else if (data[1] == 0x01)	// AVC NALU
	{
	    if (size < 10)
        {
            log_print(HT_LOG_WARN, "%s, dataLen=%d\r\n", __FUNCTION__, size);
            return 0;
        }
        
	    int len = 0;
        int num = 5;

        while (num < (int)size)
        {
            len = (data[num] & 0x000000FF) << 24 | (data[num + 1] & 0x000000FF) << 16 | 
                  (data[num + 2] & 0x000000FF) << 8 | (data[num + 3] & 0x000000FF);

            num = num + 4;

            data[num-4]= 0;
            data[num-3]= 0;
            data[num-2]= 0;
            data[num-1]= 1;

            http_flv_video_data_cb(data + num - 4, len + 4, ts);
            
            num = num + len;
        }
	}
	else
	{
		log_print(HT_LOG_WARN, "%s, tag type [%02x]!!!\r\n", __FUNCTION__, data[1]);
	}

	return 0;
}

int CHttpFlvClient::http_flv_h265_rx(uint8 * data, uint32 size, uint32 ts)
{
	if (data[1] == 0x00)   // AVC sequence header
	{
	    if (size < 32)
	    {
	        log_print(HT_LOG_WARN, "%s, dataLen=%d\r\n", __FUNCTION__, size);
	        return 0;
	    }
	    
        uint32 offset = 5 + 22;
	    int numOfArrays = data[offset++];

	    for (int i=0; i<numOfArrays; i++)
	    {
	        int naluType = data[offset++];
	        int numNalus = (data[offset] & 0x000000FF) << 8 | (data[offset + 1] & 0x000000FF);

	        offset += 2;

            for (int j = 0; j < numNalus; j++)
            {
                uint32 naluLen = (data[offset] & 0x000000FF) << 8 | (data[offset + 1] & 0x000000FF);

	            offset += 2;

	            if (offset + naluLen > size)
                {
                    return -1;
                }
	        
    	        if (naluType == HEVC_NAL_VPS && 0 == m_nVpsLen && naluLen <= sizeof(m_pVps) + 4)
    	        {
    	            m_pVps[0] = 0;
    	            m_pVps[1] = 0;
    	            m_pVps[2] = 0;
    	            m_pVps[3] = 1;
    	            memcpy(m_pVps+4, data+offset, naluLen);
    	            m_nVpsLen = naluLen + 4;
    	        }
    	        else if (naluType == HEVC_NAL_SPS && 0 == m_nSpsLen && naluLen <= sizeof(m_pSps) + 4)
    	        {
    	            m_pSps[0] = 0;
    	            m_pSps[1] = 0;
    	            m_pSps[2] = 0;
    	            m_pSps[3] = 1;
    	            memcpy(m_pSps+4, data+offset, naluLen);
    	            m_nSpsLen = naluLen + 4;
    	        }
    	        else if (naluType == HEVC_NAL_PPS && 0 == m_nPpsLen && naluLen <= sizeof(m_pPps) + 4)
    	        {
    	            m_pPps[0] = 0;
    	            m_pPps[1] = 0;
    	            m_pPps[2] = 0;
    	            m_pPps[3] = 1;
    	            memcpy(m_pPps+4, data+offset, naluLen);
    	            m_nPpsLen = naluLen + 4;
    	        }

    	        offset += naluLen;
	        }
	    }

        if (!m_bVideoReady)
        {
            m_bVideoReady = TRUE;
            http_flv_send_notify(HTTP_FLV_EVE_VIDEOREADY);
        }

        if (m_nVpsLen > 0 && m_nSpsLen > 0 && m_nPpsLen > 0)
        {
            http_flv_video_data_cb(m_pVps, m_nVpsLen, 0);
            http_flv_video_data_cb(m_pSps, m_nSpsLen, 0);
            http_flv_video_data_cb(m_pPps, m_nPpsLen, 0);
        }
	}
	else if (data[1] == 0x01)	// AVC NALU
	{
	    if (size < 10)
        {
            log_print(HT_LOG_WARN, "%s, dataLen=%d\r\n", __FUNCTION__, size);
            return 0;
        }
    
	    int len = 0;
        int num = 5;

        while (num < (int)size)
        {
            len = (data[num] & 0x000000FF) << 24 | (data[num + 1] & 0x000000FF) << 16 | 
                  (data[num + 2] & 0x000000FF) << 8 | (data[num + 3] & 0x000000FF);

            num = num + 4;

            data[num-4]= 0;
            data[num-3]= 0;
            data[num-2]= 0;
            data[num-1]= 1;

            http_flv_video_data_cb(data + num - 4, len + 4, ts);
            
            num = num + len;
        }
	}
	else
	{
		log_print(HT_LOG_WARN, "%s, tag type [%02x]!!!\r\n", __FUNCTION__, data[1]);
	}

	return 0;
}

int CHttpFlvClient::http_flv_video_rx(uint8 * data, uint32 size, uint32 ts)
{
    int ret = 0;
    uint8 v_type = data[0];
	v_type = (v_type & 0x0f);

    if (v_type == 2)        // Sorenson H.263
    {
    }
    else if (v_type == 3)   // Screen video
    {
    }
    else if (v_type == 4)   // On2 VP6
    {
    }
    else if (v_type == 5)   // On2 VP6 with alpha channel
    {
    }
    else if (v_type == 6)   // Screen video version 2
    {
    }
    else if (v_type == 7)   // AVC
    {
        m_nVideoCodec = VIDEO_CODEC_H264;

        ret = http_flv_h264_rx(data, size, ts);
    }
    else if (v_type == 12)  // H265
    {
        m_nVideoCodec = VIDEO_CODEC_H265;

        ret = http_flv_h265_rx(data, size, ts);
    }

	return ret;
}

int CHttpFlvClient::http_flv_g711_rx(uint8 * data, uint32 size, uint32 ts)
{
    if (!m_bAudioReady)
    {
        m_nChannels = ((data[0] & 0x01) ? 2 : 1);
        m_nSamplerate = 8000;
        m_bAudioReady = TRUE;

        http_flv_send_notify(HTTP_FLV_EVE_AUDIOREADY);
    }

    http_flv_audio_data_cb(data + 1, size - 1, ts);

    return 0;
}

int CHttpFlvClient::http_flv_aac_rx(uint8 * data, uint32 size, uint32 ts)
{
    if (size < 4)
    {
        return -1;
    }
    
	// AAC Packet Type: 0x00 - AAC Sequence Header; 0x01 - AAC Raw
	if (data[1] == 0x00)
	{
	    int nAudioSampleType;
	    
		log_print(HT_LOG_DBG, "%s, len[%u] data[%02X %02X %02X %02X]\r\n",
			__FUNCTION__, size, data[0], data[1], data[2], data[3]);

		memcpy(m_pAudioConfig, data+2, 2);
		m_nAudioConfigLen = 2;
		m_nChannels = ((data[0] & 0x01) ? 2 : 1);   // Whether stereo (0: Mono, 1: Stereo)
        nAudioSampleType = (data[0] & 0x0c) >> 2;   // Audio sample rate (0: 5.5kHz, 1:11KHz, 2:22 kHz, 3:44 kHz)
        
        switch (nAudioSampleType)
        {
        case 0:
            m_nSamplerate = 5500;
            break;

        case 1:
            m_nSamplerate = 11025;
            break;

        case 2:
            m_nSamplerate = 22050;
            break; 

        case 3:
            m_nSamplerate = 44100;
            break;

        default:
            m_nSamplerate = 44100;
            break;
        }

        uint16 audioSpecificConfig = 0;	
        
        audioSpecificConfig = (data[2] & 0xff) << 8;
        audioSpecificConfig += 0x00ff & data[3];
        
        uint8 nSampleFrequencyIndex = (audioSpecificConfig & 0x0780) >> 7;
        uint8 nChannels = (audioSpecificConfig & 0x78) >> 3;

	    m_nChannels = nChannels;

	    switch (nSampleFrequencyIndex)
	    {
	    case 0:
            m_nSamplerate = 96000;
            break;

        case 1:
            m_nSamplerate = 88200;
            break;
            
        case 2:
            m_nSamplerate = 64000;
            break;
            
        case 3:
            m_nSamplerate = 48000;
            break;
            
        case 4:
            m_nSamplerate = 44100;
            break;
            
        case 5:
            m_nSamplerate = 32000;
            break;
            
        case 6:
            m_nSamplerate = 24000;
            break;

        case 7:
            m_nSamplerate = 22050;
            break;

        case 8:
            m_nSamplerate = 16000;
            break;     

        case 9:
            m_nSamplerate = 12000;
            break;

        case 10:
            m_nSamplerate = 11025;
            break;

        case 11:
            m_nSamplerate = 8000;
            break;

        case 12:
            m_nSamplerate = 7350;
            break;
            
        default:
            m_nSamplerate = 44100;
            break;
	    }

	    if (!m_bAudioReady)
	    {
	        m_bAudioReady = TRUE;
            http_flv_send_notify(HTTP_FLV_EVE_AUDIOREADY);
        }
	}
	else if (data[1] == 0x01)
	{
	    http_flv_audio_data_cb(data + 2, size - 2, ts);
	}

	return 0;
}

int CHttpFlvClient::http_flv_audio_rx(uint8 * data, uint32 size, uint32 ts)
{
    int ret = 0;
	uint8 a_type = data[0];
	a_type = a_type >> 4;
	
	if (a_type == 0)	    // Linear PCM, platform endian
	{
	}
	else if (a_type == 1)	//  ADPCM
	{
	}
	else if (a_type == 2)   // MP3
	{
	}
	else if (a_type == 3)   // Linear PCM, little endian
	{
	}
	else if (a_type == 7)   // G711A
	{
	    m_nAudioCodec = AUDIO_CODEC_G711A;

	    ret = http_flv_g711_rx(data, size, ts);
	}
	else if (a_type == 8)   // G711U
	{
	    m_nAudioCodec = AUDIO_CODEC_G711U;

	    ret = http_flv_g711_rx(data, size, ts);
	}
	else if (a_type == 10)	// AAC
	{
	    m_nAudioCodec = AUDIO_CODEC_AAC;
	    
		ret = http_flv_aac_rx(data, size, ts);
	}
	else if (a_type == 11)	// Speex
	{
	}
	else if (a_type == 14)	// MP3 8-Khz
	{
	}
	
	return ret;
}

int CHttpFlvClient::http_flv_metadata_rx(uint8 * data, uint32 size)
{
    AMFObject obj;	
    AVal val;	
    AMFObjectProperty * property;	
    AMFObject subObject;	
    
    int nRes = AMF_Decode(&obj, (char*)data, size, FALSE);	
    if (nRes < 0)	
    {		
        log_print(HT_LOG_ERR, "%s, error decoding invoke packet\r\n", __FUNCTION__);
        return -1;	
    } 	

    AMF_Dump(&obj);

    for (int n = 0; n < obj.o_num; n++)
    {
        property = AMF_GetProp(&obj, NULL, n);
        if (NULL == property)
        {
            continue;
        }

        if (property->p_type == AMF_OBJECT || property->p_type == AMF_ECMA_ARRAY)
        {				
            AMFProp_GetObject(property, &subObject);
            
            for (int m = 0; m < subObject.o_num; m++)
            {					
                property = AMF_GetProp(&subObject, NULL, m);					
                if (NULL == property)
                {
                    continue;
                }
                
                if (property->p_type == AMF_OBJECT)						
                { 						
                }						
                else if (property->p_type == AMF_BOOLEAN)						
                {							
                    int bVal = AMFProp_GetBoolean(property);							
                    if (strncmp("stereo", property->p_name.av_val, property->p_name.av_len) == 0)							
                    {								
                        m_nChannels = bVal > 0 ? 2 : 1;							
                    }
                }						
                else if (property->p_type == AMF_NUMBER)						
                {							
                    double dVal = AMFProp_GetNumber(property);							
                    if (strncmp("width", property->p_name.av_val, property->p_name.av_len) == 0)							
                    {								
                        m_nWidth = (int)dVal;							
                    }							
                    else if (strcasecmp("height", property->p_name.av_val) == 0)							
                    {								
                        m_nHeight = (int)dVal;							
                    }							
                    else if (strcasecmp("framerate", property->p_name.av_val) == 0)							
                    {								
                        m_nFrameRate = dVal;							
                    }							
                    else if (strcasecmp("videocodecid", property->p_name.av_val) == 0)							
                    {								
                        switch ((int)dVal)
                        {
                        case 7:
                            m_nVideoCodec = VIDEO_CODEC_H264;
                            break;
                        case 12:
                            m_nVideoCodec = VIDEO_CODEC_H265;
                            break;
                        }					
                    }							
                    else if (strcasecmp("audiosamplerate", property->p_name.av_val) == 0)							
                    {								
                        m_nSamplerate = (int)dVal;							
                    }							
                    else if (strcasecmp("audiosamplesize", property->p_name.av_val) == 0)							
                    {								
                    }							
                    else if (strcasecmp("audiocodecid", property->p_name.av_val) == 0)							
                    {								
                        switch ((int)dVal)
                        {
                        case 7:
                            m_nAudioCodec = AUDIO_CODEC_G711A;
                            break;
                        case 8:
                            m_nAudioCodec = AUDIO_CODEC_G711U;
                            break;
                        case 10:
                            m_nAudioCodec = AUDIO_CODEC_AAC;
                            break;    
                        }						
                    }							
                    else if (strcasecmp("filesize", property->p_name.av_val) == 0)							
                    {								
                    }						
                }
                else if (property->p_type == AMF_STRING)						
                {							
                    AMFProp_GetString(property, &val);						
                } 					
            }			
        }			
        else			
        {				
            AMFProp_GetString(property, &val);			
        }
    }	

    AMF_Reset(&obj);

    if (m_nVideoCodec != VIDEO_CODEC_NONE && !m_bVideoReady)
    {
        m_bVideoReady = TRUE;
        http_flv_send_notify(HTTP_FLV_EVE_VIDEOREADY);
    }
    
    if (m_nAudioCodec != AUDIO_CODEC_NONE && !m_bAudioReady)
    {
        m_bAudioReady = TRUE;
        http_flv_send_notify(HTTP_FLV_EVE_AUDIOREADY);
    }
    
	return 0;
}

BOOL CHttpFlvClient::http_flv_res_rx(HTTPREQ * p_http)
{
    if (http_is_http_msg(p_http->rbuf) == FALSE)
	{
	    log_print(HT_LOG_ERR, "%s, invalid http msg\r\n", __FUNCTION__);
		return FALSE;
	}

	int http_pkt_len = http_pkt_find_end(p_http->rbuf);
	if (http_pkt_len == 0) 
	{
		return TRUE;
	}
	
	p_http->hdr_len = http_pkt_len;

    HTTPMSG * rx_msg = http_get_msg_buf(http_pkt_len + 1);
	if (rx_msg == NULL)
	{
		log_print(HT_LOG_ERR, "%s, get_msg_buf ret null!!!\r\n", __FUNCTION__);
		return FALSE;
	}

	memcpy(rx_msg->msg_buf, p_http->rbuf, http_pkt_len);
	rx_msg->msg_buf[http_pkt_len] = '\0';
	
	log_print(HT_LOG_DBG, "RX from %s << %s\r\n", p_http->host, rx_msg->msg_buf);
	
	int parse_len = http_msg_parse_part1(rx_msg->msg_buf, http_pkt_len, rx_msg);
	if (parse_len != http_pkt_len)
	{
		log_print(HT_LOG_ERR, "%s, http_msg_parse_part1=%d, http_pkt_len=%d!!!\r\n", 
		    __FUNCTION__, parse_len, http_pkt_len);

		http_free_msg(rx_msg);
		return FALSE;
	}

	if (rx_msg->msg_sub_type != 200)
	{
	    http_free_msg(rx_msg);
	    return FALSE;
	}

	if (rx_msg->ctt_type != CTT_FLV)
	{
	    http_free_msg(rx_msg);
	    return FALSE;
	}

	char * p_encoding = http_get_headline(rx_msg, "Transfer-Encoding");
	if (p_encoding)
	{
	    if (strcasecmp(p_encoding, "chunked") == 0)
	    {
	        m_bChunked = TRUE;
	    }
	}

	http_free_msg(rx_msg);

	p_http->rcv_dlen -= p_http->hdr_len;

	if (p_http->rcv_dlen > 0)
	{
	    memmove(p_http->rbuf, p_http->rbuf+p_http->hdr_len, p_http->rcv_dlen);
	}
	else
    {
        if (p_http->dyn_recv_buf)
        {
            free(p_http->dyn_recv_buf);
            p_http->dyn_recv_buf = NULL;
        }

        p_http->rbuf = NULL;
        p_http->rcv_dlen = 0;
        p_http->mlen = 0;
    }

	return TRUE;
}

BOOL CHttpFlvClient::http_flv_data_handler(HTTPREQ * p_http)
{
    // At least received FLV TAG header length 
    if (p_http->rcv_dlen < 11)
    {
        return TRUE;
    }

    uint8 * p = (uint8 *)p_http->rbuf;
    uint32 size = (uint32)((p[1] << 16) | (p[2] << 8) | p[3]);

    // Tag header length + data size + PreviousTagSize + (chunked '\r\n')
    int tlen = 11 + size + 4 + (m_bChunked ? 2 : 0);
    
    if (p_http->rcv_dlen < tlen)
    {
        // The receive buffer is not enough, allocate memory
        
        if (p_http->mlen < tlen)
        {
            char * old_recv_buf = p_http->dyn_recv_buf;

            p_http->dyn_recv_buf = (char *)malloc(tlen);
            if (NULL == p_http->dyn_recv_buf)
            {
                if (old_recv_buf)
                {
                    free(old_recv_buf);
                }
                
                return FALSE;
            }
            else
            {
                memmove(p_http->dyn_recv_buf, p_http->rbuf, p_http->rcv_dlen);

                p_http->mlen = tlen;
                p_http->rbuf = p_http->dyn_recv_buf;

                // Free the old receive buffer 
                if (old_recv_buf)
                {
                    free(old_recv_buf);
                }
            }
        }
    }
    else 
    {
        int ret = 0;
        int type = p[0];
        uint32 ts = (uint32)((p[4] << 16) | (p[5] << 8) | p[6] | (p[7] << 24));

        if (8 == type)
        {
            ret = http_flv_audio_rx((uint8 *)p+11, size, ts);
        }
        else if (9 == type)
        {
            ret = http_flv_video_rx((uint8 *)p+11, size, ts);
        }
        else if (18 == type)
        {
            ret = http_flv_metadata_rx((uint8 *)p+11, size);
        }

        p_http->rcv_dlen -= tlen;
        
        if (p_http->rcv_dlen > 0)
        {
            memmove(p_http->rbuf, p_http->rbuf+tlen, p_http->rcv_dlen);
        }
        else
        {
            if (p_http->dyn_recv_buf)
            {
                free(p_http->dyn_recv_buf);
                p_http->dyn_recv_buf = NULL;
            }

            p_http->rbuf = NULL;
            p_http->rcv_dlen = 0;
            p_http->mlen = 0;
        }

        if (ret < 0)
        {
            return FALSE;
        }
    }

    return TRUE;
}

BOOL CHttpFlvClient::http_flv_header_rx(HTTPREQ * p_http)
{
    if (p_http->rbuf[0] != 'F' || p_http->rbuf[1] != 'L' || p_http->rbuf[2] != 'V')
    {
        return FALSE;
    }

    m_bFlvHeader = TRUE;

    return TRUE;
}

BOOL CHttpFlvClient::http_flv_chunked(HTTPREQ * p_http)
{
    int i = 0;
    char buff[32] = {'\0'};
    char * p = p_http->rbuf;

    // Get the data length of the hexadecimal string 
    
    while (i < p_http->rcv_dlen - 1)
    {
        if (p[i] == '\r' && p[i+1] == '\n')
        {
            i += 2;
            break;
        }
        else if (i < 31)
        {
            buff[i] = p[i];
        }

        ++i;
    }

    // Need more data 
    if (i >= p_http->rcv_dlen - 1)
    {
        return FALSE;
    }
    
    int len = strtol(buff, NULL, 16);   // data length
    int tlen = len + i + 2;             // Data length + length itself +'\r\n'           
    
    if (p_http->rcv_dlen < tlen)
    {
        // The receive buffer is not enough, allocate memory
        
        if (p_http->mlen < tlen)
        {
            char * old_recv_buf = p_http->dyn_recv_buf;

            p_http->dyn_recv_buf = (char *)malloc(tlen);
            if (NULL == p_http->dyn_recv_buf)
            {
                // What should  do if the allocation fails? 

                // Free the old receive buffer 
                if (old_recv_buf)
                {
                    free(old_recv_buf);
                }
                
                return FALSE;
            }
            else
            {
                memmove(p_http->dyn_recv_buf, p_http->rbuf, p_http->rcv_dlen);

                p_http->mlen = tlen;
                p_http->rbuf = p_http->dyn_recv_buf;

                // Free the old receive buffer
                if (old_recv_buf)
                {
                    free(old_recv_buf);
                }
            }
        }

        // Need more data 
        return FALSE;
    }

    // skip length
    p_http->mlen -= i;
    p_http->rcv_dlen -= i;
    p_http->rbuf += i;

    return TRUE;
}

BOOL CHttpFlvClient::http_flv_data_rx(HTTPREQ * p_http)
{
    if (m_bChunked)
    {
        if (!http_flv_chunked(p_http))
        {
            return TRUE;
        }
    }

    if (!m_bFlvHeader)
    {
        // Receive FLV header 
        
        int headsize = 13 + (m_bChunked ? 2 : 0);
        
        if (p_http->rcv_dlen >= headsize)
        {
            if (!http_flv_header_rx(p_http))
            {
                return FALSE;
            }
            else
            {
                // skip flv header
                p_http->mlen -= headsize;
                p_http->rcv_dlen -= headsize;
                p_http->rbuf += headsize;

                if (m_bChunked)
                {
                    if (!http_flv_chunked(p_http))
                    {
                        return TRUE;
                    }
                }
            }
        }
        else
        {
            // Need more data
            return TRUE;
        }
    }

    if (m_bFlvHeader)
    {
        return http_flv_data_handler(p_http);
    }

    return TRUE;
}

int CHttpFlvClient::http_flv_rx(HTTPREQ * p_http)
{
    int rlen;
    int sret;
    fd_set fdread;
    struct timeval tv = {1, 0};

    FD_ZERO(&fdread);
    FD_SET(p_http->cfd, &fdread); 

#ifdef HTTPS
    if (p_http->https && SSL_pending(p_http->ssl) > 0)
	{
	    // There is data to read 
	}
	else
#endif
    {
        sret = select((int)(p_http->cfd+1), &fdread, NULL, NULL, &tv); 
        if (sret == 0) // Time expired 
        {
            return HTTP_FLV_RX_TIMEOUT;
        }
        else if (!FD_ISSET(p_http->cfd, &fdread))
        {
            return HTTP_FLV_RX_TIMEOUT;
        }
    }
    
    if (p_http->rbuf == NULL)
	{
		p_http->rbuf = p_http->rcv_buf;
		p_http->mlen = sizeof(p_http->rcv_buf)-4;
		p_http->rcv_dlen = 0;
	}

#ifdef HTTPS
    if (p_http->https)
	{
		rlen = SSL_read(p_http->ssl, p_http->rbuf+p_http->rcv_dlen, p_http->mlen-p_http->rcv_dlen);
	}
	else 
#endif
    rlen = recv(p_http->cfd, p_http->rbuf+p_http->rcv_dlen, p_http->mlen-p_http->rcv_dlen, 0);

	if (rlen <= 0)
	{
		log_print(HT_LOG_INFO, "%s, recv return = %d, dlen[%d], mlen[%d]\r\n", 
		    __FUNCTION__, rlen, p_http->rcv_dlen, p_http->mlen);	
		return HTTP_FLV_RX_FAIL;
	}

	p_http->rcv_dlen += rlen;

    if (p_http->rcv_dlen < 16)
	{
		return HTTP_FLV_RX_SUCC;
	}
	
	if (p_http->hdr_len == 0)
	{
	    // Receive GET request response 
	    
	    if (!http_flv_res_rx(p_http))
	    {
	        return HTTP_FLV_RX_FAIL;
	    }
	    else if (p_http->hdr_len > 0 && p_http->rcv_dlen > 0)
	    {
	        // There is still data that has not been processed 
	        
            if (!http_flv_data_rx(p_http))
            {
                return HTTP_FLV_RX_FAIL;
            }
	    }
	}
	else if (!http_flv_data_rx(p_http))
	{
        return HTTP_FLV_RX_FAIL;
	}

	// Need to receive more data 
	
	return HTTP_FLV_RX_SUCC;
}

void CHttpFlvClient::rx_thread()
{
    int  ret = 0;
    int  tm_count = 0;
    BOOL nodata_notify = FALSE;
    
    if (!http_flv_connect())
    {
        m_tidRx = 0;
        http_flv_send_notify(HTTP_FLV_EVE_CONNFAIL);
        return;
    }

    while (m_bRunning)
    {
        ret = http_flv_rx(&m_http);
        if (ret == HTTP_FLV_RX_FAIL)
	    {
	        break;
	    }
	    else
	    {
		    if (ret == HTTP_FLV_RX_TIMEOUT)
	    	{
	    		tm_count++;
		        if (tm_count >= m_nRxTimeout && !nodata_notify)    // in 10s without data
		        {
		            nodata_notify = TRUE;
		            http_flv_send_notify(HTTP_FLV_EVE_NODATA);
		        }
	        }
	        else // should be HTTP_FLV_RX_SUCC
	        {
	        	if (nodata_notify)
		        {
		            nodata_notify = FALSE;
		            http_flv_send_notify(HTTP_FLV_EVE_RESUME);
		        }
		        
	        	tm_count = 0;
	        }
        }
    }
    
    m_tidRx = 0;
    http_flv_send_notify(HTTP_FLV_EVE_STOPPED);

    log_print(HT_LOG_INFO, "%s, exit\r\n", __FUNCTION__);
}

void CHttpFlvClient::http_flv_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 CHttpFlvClient::http_flv_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 CHttpFlvClient::http_flv_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 CHttpFlvClient::get_h264_params()
{
    if (m_nSpsLen > 0)
    {
        http_flv_video_data_cb(m_pSps, m_nSpsLen, 0);
    }

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

BOOL CHttpFlvClient::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 CHttpFlvClient::get_h265_params()
{
    if (m_nVpsLen > 0)
    {
        http_flv_video_data_cb(m_pVps, m_nVpsLen, 0);
    }
    
    if (m_nSpsLen > 0)
    {
        http_flv_video_data_cb(m_pSps, m_nSpsLen, 0);
    }

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

BOOL CHttpFlvClient::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 CHttpFlvClient::set_notify_cb(http_flv_notify_cb notify, void * userdata) 
{
	sys_os_mutex_enter(m_pMutex);	
	m_pNotify = notify;
	m_pUserdata = userdata;
	sys_os_mutex_leave(m_pMutex);
}

void CHttpFlvClient::set_video_cb(http_flv_video_cb cb) 
{
	sys_os_mutex_enter(m_pMutex);
	m_pVideoCB = cb;
	sys_os_mutex_leave(m_pMutex);
}

void CHttpFlvClient::set_audio_cb(http_flv_audio_cb cb)
{
	sys_os_mutex_enter(m_pMutex);
	m_pAudioCB = cb;
	sys_os_mutex_leave(m_pMutex);
}

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



