/***************************************************************************************
 *
 *  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 "h265_rtp_rx.h"


/****************************************************************************/

void h265_save_parameters(H265RXI * p_rxi, uint8 * p_data, uint32 len)
{
    uint8 nal_type = ((p_data[0] >> 1) & 0x3f);
                    
    if (nal_type == HEVC_NAL_VPS && p_rxi->param_sets.vps_len == 0)
	{
		if (len <= sizeof(p_rxi->param_sets.vps) - 4)
		{
			int offset = 0;
			
			p_rxi->param_sets.vps[0] = 0;
			p_rxi->param_sets.vps[1] = 0;
			p_rxi->param_sets.vps[2] = 0;
			p_rxi->param_sets.vps[3] = 1;

			offset = 4;
			
			memcpy(p_rxi->param_sets.vps+offset, p_data, len);
			p_rxi->param_sets.vps_len = len+offset;
		}
	}
	else if (nal_type == HEVC_NAL_SPS && p_rxi->param_sets.sps_len == 0)
	{
		if (len <= sizeof(p_rxi->param_sets.sps) - 4)
		{
			int offset = 0;
			
			p_rxi->param_sets.sps[0] = 0;
			p_rxi->param_sets.sps[1] = 0;
			p_rxi->param_sets.sps[2] = 0;
			p_rxi->param_sets.sps[3] = 1;

			offset = 4;
			
			memcpy(p_rxi->param_sets.sps+offset, p_data, len);
			p_rxi->param_sets.sps_len = len+offset;
		}
	}
	else if (nal_type == HEVC_NAL_PPS && p_rxi->param_sets.pps_len == 0)
	{
		if (len <= sizeof(p_rxi->param_sets.pps) - 4)
		{
			int offset = 0;
			
			p_rxi->param_sets.pps[0] = 0;
			p_rxi->param_sets.pps[1] = 0;
			p_rxi->param_sets.pps[2] = 0;
			p_rxi->param_sets.pps[3] = 1;

			offset = 4;
			
			memcpy(p_rxi->param_sets.pps+offset, p_data, len);
			p_rxi->param_sets.pps_len = len+offset;
		}
	}
}

BOOL h265_handle_aggregated_packet(H265RXI * p_rxi, uint8 * p_data, int len)
{
    uint8 *src  = p_data;
    int src_len = len;
    int skip_between = p_rxi->rxf_don ? 1 : 0;

    while (src_len > 2) 
    {
        uint16 nal_size = ((src[0] << 8) | src[1]);

        // consume the length of the aggregate
        src     += 2;
        src_len -= 2;

        if (nal_size <= src_len) 
        {
            h265_save_parameters(p_rxi, src, nal_size);

            if (p_rxi->d_offset + nal_size + 4 >= p_rxi->buf_len)
            {
                if (p_rxi->rtprxi.rxf_marker)
        	    {
        	        p_rxi->d_offset = 0;
        	    }
        	    
                log_print(HT_LOG_ERR, "%s, packet too big %d!!!", __FUNCTION__, p_rxi->d_offset + nal_size + 4);
        		return FALSE;
            }
            else
            {
                p_rxi->p_buf[p_rxi->d_offset+0] = 0;
        		p_rxi->p_buf[p_rxi->d_offset+1] = 0;
        		p_rxi->p_buf[p_rxi->d_offset+2] = 0;
        		p_rxi->p_buf[p_rxi->d_offset+3] = 1;
		        p_rxi->d_offset += 4;
		        
                // copying
                memcpy(p_rxi->p_buf + p_rxi->d_offset, src, nal_size);

                p_rxi->d_offset += nal_size;
      		}
        } 
        else 
        {
            log_print(HT_LOG_ERR, "%s, nal size exceeds length: %d %d\n", __FUNCTION__, nal_size, src_len);
            return FALSE;
        }

        // eat what we handled
        src     += nal_size + skip_between;
        src_len -= nal_size + skip_between;
    }

    if (p_rxi->pkt_func)
    {
        p_rxi->pkt_func(p_rxi->p_buf, p_rxi->d_offset, p_rxi->rtprxi.ts, p_rxi->rtprxi.seq, p_rxi->user_data);
    }

    p_rxi->d_offset = 0;
		
    return TRUE;
}

BOOL h265_data_rx(H265RXI * p_rxi, uint8 * p_data, int len)
{
	uint8 * headerStart = p_data;
	uint32  packetSize = len;
	uint32  numBytesToSkip;
	uint8   naluType;	
	BOOL    beginPacket = FALSE;
	BOOL    endPacket = FALSE;

	if (packetSize < 2)
	{
		return FALSE;
	}

	if (p_rxi->rtprxi.rxf_loss)
    {
        // Packet loss, discard the previously cached packets
        p_rxi->d_offset = 0;
    }
    
	naluType = (headerStart[0] & 0x7E) >> 1;
		
	switch (naluType) 
	{
	case 48: 
	{ 
		// Aggregation Packet (AP)
		// We skip over the 2-byte Payload Header, and the DONL header (if any).
		if (p_rxi->rxf_don) 
		{
			if (packetSize < 4)
			{
				return FALSE;
			}
			
			numBytesToSkip = 4;
		} 
		else
		{
			numBytesToSkip = 2;
		}

		return h265_handle_aggregated_packet(p_rxi, p_data+numBytesToSkip, len-numBytesToSkip);
	}
		
	case 49: 
	{ 
		// Fragmentation Unit (FU)
		// This NALU begins with the 2-byte Payload Header, the 1-byte FU header, and (optionally)
		// the 2-byte DONL header.
		// If the start bit is set, we reconstruct the original NAL header at the end of these
		// 3 (or 5) bytes, and skip over the first 1 (or 3) bytes.

		if (packetSize < 3) 
		{
			return FALSE;
		}
		
		uint8 startBit = headerStart[2] & 0x80; // from the FU header
		uint8 endBit = headerStart[2] & 0x40; 	// from the FU header
		if (startBit)
		{
			beginPacket = TRUE;

			uint8 nal_unit_type = headerStart[2] & 0x3F; // the last 6 bits of the FU header
			uint8 newNALHeader[2];
			
			newNALHeader[0] = (headerStart[0] & 0x81) | (nal_unit_type << 1);
			newNALHeader[1] = headerStart[1];

			if (p_rxi->rxf_don) 
			{
				if (packetSize < 5)
				{
					return FALSE;
				}
				
				headerStart[3] = newNALHeader[0];
				headerStart[4] = newNALHeader[1];
				numBytesToSkip = 3;
			}
			else 
			{
				headerStart[1] = newNALHeader[0];
				headerStart[2] = newNALHeader[1];
				numBytesToSkip = 1;
			}
		} 
		else 
		{
			// The start bit is not set, so we skip over all headers:
			beginPacket = FALSE;
			
			if (p_rxi->rxf_don) 
			{
				if (packetSize < 5)
				{
					return FALSE;
				}
				
				numBytesToSkip = 5;
			}
			else 
			{
				numBytesToSkip = 3;
			}
		}
		
		endPacket = (endBit != 0);
		break;
	}
	
	default:
		// This packet contains one complete NAL unit:
		beginPacket = endPacket = TRUE;
		numBytesToSkip = 0;
		break;
	}

    if (p_rxi->rtprxi.rxf_loss)
    {
        // Packet loss, until the next start packet
        
        if (!beginPacket)
        {
            return FALSE;
        }
        else
        {
            p_rxi->rtprxi.rxf_loss = 0;
        }
    }
    
	// save the H265 parameter sets

    h265_save_parameters(p_rxi, headerStart + numBytesToSkip, packetSize - numBytesToSkip);
    
  	if ((p_rxi->d_offset + 4 + packetSize - numBytesToSkip) >= p_rxi->buf_len)
	{
	    if (p_rxi->rtprxi.rxf_marker)
	    {
	        p_rxi->d_offset = 0;
	    }
	    
		log_print(HT_LOG_ERR, "%s, fragment packet too big %d!!!", 
		    __FUNCTION__, p_rxi->d_offset + 4 + packetSize - numBytesToSkip);
		return FALSE;
	}

    if (beginPacket)
    {
        p_rxi->p_buf[p_rxi->d_offset+0] = 0;
    	p_rxi->p_buf[p_rxi->d_offset+1] = 0;
    	p_rxi->p_buf[p_rxi->d_offset+2] = 0;
    	p_rxi->p_buf[p_rxi->d_offset+3] = 1;
    	p_rxi->d_offset += 4;
    }
    
    memcpy(p_rxi->p_buf + p_rxi->d_offset, headerStart + numBytesToSkip, packetSize - numBytesToSkip);
	p_rxi->d_offset += packetSize - numBytesToSkip;

    if (endPacket && p_rxi->rtprxi.rxf_marker)
    {
        if (p_rxi->pkt_func)
  		{
  			p_rxi->pkt_func(p_rxi->p_buf, p_rxi->d_offset, p_rxi->rtprxi.ts, p_rxi->rtprxi.seq, p_rxi->user_data);
  		}

        p_rxi->d_offset = 0;
    }

	return TRUE;
}

BOOL h265_rtp_rx(H265RXI * p_rxi, uint8 * p_data, int len)
{
	if (p_rxi == NULL)
	{
		return FALSE;
	}
	
	if (!rtp_data_rx(&p_rxi->rtprxi, p_data, len))
	{
		return FALSE;
	}

	return h265_data_rx(p_rxi, p_rxi->rtprxi.p_data, p_rxi->rtprxi.len);
}

BOOL h265_rxi_init(H265RXI * p_rxi, VRTPRXCBF cbf, void * p_userdata)
{
	memset(p_rxi, 0, sizeof(H265RXI));

	p_rxi->buf_len = RTP_MAX_VIDEO_BUFF;
	
	p_rxi->p_buf_org = (uint8 *)malloc(p_rxi->buf_len);
	if (p_rxi->p_buf_org == NULL)
	{
		return -1;
    }

	p_rxi->p_buf = p_rxi->p_buf_org + 32;
	p_rxi->buf_len -= 32;
	p_rxi->pkt_func = cbf;
    p_rxi->user_data = p_userdata;

	return 0;
}

void h265_rxi_deinit(H265RXI * p_rxi)
{
	if (p_rxi->p_buf_org)
	{
		free(p_rxi->p_buf_org);
    }

	memset(p_rxi, 0, sizeof(H265RXI));
}



