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


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

void h264_save_parameters(H264RXI * p_rxi, uint8 * p_data, uint32 len)
{
    uint8 nal_type = (p_data[0] & 0x1F);
    
    if (nal_type == H264_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 == H264_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 h264_handle_aggregated_packet(H264RXI * p_rxi, uint8 * p_data, int len)
{
    uint8 *src  = p_data;
    int src_len = len;

    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) 
        {
            h264_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;
        src_len -= nal_size;
    }

    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 h264_data_rx(H264RXI * 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 < 1)
	{
		return FALSE;
	}

	if (p_rxi->rtprxi.rxf_loss)
    {
        // Packet loss, discard the previously cached packets
        p_rxi->d_offset = 0;
    }
    
	naluType = (headerStart[0] & 0x1F);
		
	switch (naluType) 
	{
	case 24:  					// STAP-A
        numBytesToSkip = 1; 	// discard the type byte
        return h264_handle_aggregated_packet(p_rxi, p_data+numBytesToSkip, len-numBytesToSkip);
	
	case 25: case 26: case 27:  // STAP-B, MTAP16, or MTAP24
		numBytesToSkip = 3; 	// discard the type byte, and the initial DON
		break;
		
	case 28: case 29: 			// FU-A or FU-B
	{ 
	    // For these NALUs, the first two bytes are the FU indicator and the FU header.
	    // If the start bit is set, we reconstruct the original NAL header into byte 1
	    
	    if (packetSize < 2) 
	    {
	    	return FALSE;
	    }
	    
	    uint8 startBit = headerStart[1] & 0x80;
	    uint8 endBit = headerStart[1] & 0x40;
	    if (startBit) 
	    {
			beginPacket = TRUE;

			headerStart[1] = (headerStart[0] & 0xE0) | (headerStart[1] & 0x1F);
			numBytesToSkip = 1;
	    } 
	    else 
	    {
			// The start bit is not set, so we skip both the FU indicator and header:
			beginPacket = FALSE;
			numBytesToSkip = 2;
	    }
	    
		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 H264 parameter sets

    h264_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 h264_rtp_rx(H264RXI * 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 h264_data_rx(p_rxi, p_rxi->rtprxi.p_data, p_rxi->rtprxi.len);
}

BOOL h264_rxi_init(H264RXI * p_rxi, VRTPRXCBF cbf, void * p_userdata)
{
	memset(p_rxi, 0, sizeof(H264RXI));

	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 h264_rxi_deinit(H264RXI * p_rxi)
{
	if (p_rxi->p_buf_org)
	{
		free(p_rxi->p_buf_org);
    }

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



