/***************************************************************************************
 *
 *  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 "mjpeg.h"
#include "mjpeg_rtp_rx.h"
#include "mjpeg_tables.h"


static void mjpeg_create_huffman_header
(
uint8*& p,
uint8 const* codelens,
int ncodes,
uint8 const* symbols,
int nsymbols,
int tableNo, int tableClass
) 
{
	*p++ = 0xff; *p++ = MARKER_DHT;
	*p++ = 0;               		/* length msb */
	*p++ = 3 + ncodes + nsymbols; 	/* length lsb */
	*p++ = (tableClass << 4) | tableNo;
	memcpy(p, codelens, ncodes);
	p += ncodes;
	memcpy(p, symbols, nsymbols);
	p += nsymbols;
}

uint32 mjpeg_compute_header_size(uint32 qtlen, uint32 dri) 
{
	uint32 qtlen_half = qtlen/2; // in case qtlen is odd; shouldn't happen
	qtlen = qtlen_half*2;

	uint32 numQtables = qtlen > 64 ? 2 : 1;
	return 485 + numQtables*5 + qtlen + (dri > 0 ? 6 : 0);
}

int mjpeg_create_header
(
uint8* buf, uint32 type,
uint32 w, uint32 h,
uint8 const* qtables, uint32 qtlen,
uint32 dri
) 
{
	uint8 *ptr = buf;
	uint32 numQtables = qtlen > 64 ? 2 : 1;

	// MARKER_SOI:
	*ptr++ = 0xFF; *ptr++ = MARKER_SOI;

	// MARKER_APP_FIRST:
	*ptr++ = 0xFF; *ptr++ = MARKER_APP_FIRST;
	*ptr++ = 0x00; *ptr++ = 0x10; // size of chunk
	*ptr++ = 'J'; *ptr++ = 'F'; *ptr++ = 'I'; *ptr++ = 'F'; *ptr++ = 0x00;
	*ptr++ = 0x01; *ptr++ = 0x01; // JFIF format version (1.1)
	*ptr++ = 0x00; // no units
	*ptr++ = 0x00; *ptr++ = 0x01; // Horizontal pixel aspect ratio
	*ptr++ = 0x00; *ptr++ = 0x01; // Vertical pixel aspect ratio
	*ptr++ = 0x00; *ptr++ = 0x00; // no thumbnail

	// MARKER_DRI:
	if (dri > 0) 
	{
		*ptr++ = 0xFF; *ptr++ = MARKER_DRI;
		*ptr++ = 0x00; *ptr++ = 0x04; // size of chunk
		*ptr++ = (uint8)(dri >> 8); *ptr++ = (uint8)(dri); // restart interval
	}

	// MARKER_DQT (luma):
	uint32 tableSize = numQtables == 1 ? qtlen : qtlen/2;
	*ptr++ = 0xFF; *ptr++ = MARKER_DQT;
	*ptr++ = 0x00; *ptr++ = tableSize + 3; // size of chunk
	*ptr++ = 0x00; // precision(0), table id(0)
	memcpy(ptr, qtables, tableSize);
	qtables += tableSize;
	ptr += tableSize;

	if (numQtables > 1) 
	{
		tableSize = qtlen - qtlen/2;
		// MARKER_DQT (chroma):
		*ptr++ = 0xFF; *ptr++ = MARKER_DQT;
		*ptr++ = 0x00; *ptr++ = tableSize + 3; // size of chunk
		*ptr++ = 0x01; // precision(0), table id(1)
		memcpy(ptr, qtables, tableSize);
		qtables += tableSize;
		ptr += tableSize;
	}

	// MARKER_SOF0:
	*ptr++ = 0xFF; *ptr++ = MARKER_SOF0;
	*ptr++ = 0x00; *ptr++ = 0x11; // size of chunk
	*ptr++ = 0x08; // sample precision
	*ptr++ = (uint8)(h >> 8);
	*ptr++ = (uint8)(h); // number of lines (must be a multiple of 8)
	*ptr++ = (uint8)(w >> 8);
	*ptr++ = (uint8)(w); // number of columns (must be a multiple of 8)
	*ptr++ = 0x03; // number of components
	*ptr++ = 0x01; // id of component
	*ptr++ = type ? 0x22 : 0x21; // sampling ratio (h,v)
	*ptr++ = 0x00; // quant table id
	*ptr++ = 0x02; // id of component
	*ptr++ = 0x11; // sampling ratio (h,v)
	*ptr++ = numQtables == 1 ? 0x00 : 0x01; // quant table id
	*ptr++ = 0x03; // id of component
	*ptr++ = 0x11; // sampling ratio (h,v)
	*ptr++ = numQtables == 1 ? 0x00 : 0x01; // quant table id

	mjpeg_create_huffman_header(ptr, lum_dc_codelens, sizeof lum_dc_codelens,
		      lum_dc_symbols, sizeof lum_dc_symbols, 0, 0);
	mjpeg_create_huffman_header(ptr, lum_ac_codelens, sizeof lum_ac_codelens,
		      lum_ac_symbols, sizeof lum_ac_symbols, 0, 1);
	mjpeg_create_huffman_header(ptr, chm_dc_codelens, sizeof chm_dc_codelens,
		      chm_dc_symbols, sizeof chm_dc_symbols, 1, 0);
	mjpeg_create_huffman_header(ptr, chm_ac_codelens, sizeof chm_ac_codelens,
		      chm_ac_symbols, sizeof chm_ac_symbols, 1, 1);

	// MARKER_SOS:
	*ptr++ = 0xFF;  *ptr++ = MARKER_SOS;
	*ptr++ = 0x00; *ptr++ = 0x0C; // size of chunk
	*ptr++ = 0x03; // number of components
	*ptr++ = 0x01; // id of component
	*ptr++ = 0x00; // huffman table id (DC, AC)
	*ptr++ = 0x02; // id of component
	*ptr++ = 0x11; // huffman table id (DC, AC)
	*ptr++ = 0x03; // id of component
	*ptr++ = 0x11; // huffman table id (DC, AC)
	*ptr++ = 0x00; // start of spectral
	*ptr++ = 0x3F; // end of spectral
	*ptr++ = 0x00; // successive approximation bit position (high, low)

	return (int)(ptr - buf);
}


// The default 'luma' and 'chroma' quantizer tables, in zigzag order:
static uint8 const defaultQuantizers[128] = 
{
	// luma table:
	16, 11, 12, 14, 12, 10, 16, 14,
	13, 14, 18, 17, 16, 19, 24, 40,
	26, 24, 22, 22, 24, 49, 35, 37,
	29, 40, 58, 51, 61, 60, 57, 51,
	56, 55, 64, 72, 92, 78, 64, 68,
	87, 69, 55, 56, 80, 109, 81, 87,
	95, 98, 103, 104, 103, 62, 77, 113,
	121, 112, 100, 120, 92, 101, 103, 99,
	// chroma table:
	17, 18, 18, 24, 21, 24, 47, 26,
	26, 47, 99, 66, 56, 66, 99, 99,
	99, 99, 99, 99, 99, 99, 99, 99,
	99, 99, 99, 99, 99, 99, 99, 99,
	99, 99, 99, 99, 99, 99, 99, 99,
	99, 99, 99, 99, 99, 99, 99, 99,
	99, 99, 99, 99, 99, 99, 99, 99,
	99, 99, 99, 99, 99, 99, 99, 99
};

void mjpeg_make_default_qtables(uint8* resultTables, uint32 Q) 
{
	int factor = Q;
	int q;

	if (Q < 1) factor = 1;
	else if (Q > 99) factor = 99;

	if (Q < 50) 
	{
		q = 5000 / factor;
	} 
	else 
	{
		q = 200 - factor*2;
	}

	for (int i = 0; i < 128; ++i) 
	{
		int newVal = (defaultQuantizers[i]*q + 50)/100;
		if (newVal < 1) newVal = 1;
		else if (newVal > 255) newVal = 255;
		resultTables[i] = newVal;
	}
}

BOOL mjpeg_data_rx(MJPEGRXI * p_rxi, uint8 * p_data, int len)
{
	uint8* headerStart = p_data;
	uint32 packetSize = len;
	uint32 resultSpecialHeaderSize = 0;
	
	uint8* qtables = NULL;
	uint32 qtlen = 0;
	uint32 dri = 0;

	// There's at least 8-byte video-specific header
	/*
	0                   1                   2                   3
	0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
	+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	| Type-specific |              Fragment Offset                  |
	+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	|      Type     |       Q       |     Width     |     Height    |
	+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 */

  	resultSpecialHeaderSize = 8;

	uint32 Offset = (uint32)((uint32)headerStart[1] << 16 | (uint32)headerStart[2] << 8 | (uint32)headerStart[3]);
	uint32 Type = (uint32)headerStart[4];
	uint32 type = Type & 1;
	uint32 Q = (uint32)headerStart[5];
	uint32 width = (uint32)headerStart[6] * 8;
	uint32 height = (uint32)headerStart[7] * 8;
	
	if (width == 0) 
	{
	    width = p_rxi->width ? p_rxi->width : 256*8;
	}
	
	if (height == 0)
	{
	    height = p_rxi->height ? p_rxi->height : 256*8;
    }
    
	if (Type > 63) 
	{
		// Restart Marker header present
		/*
		0                   1                   2                   3
		0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
		+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
		|       Restart Interval        |F|L|       Restart Count       |
		+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
		  */
		if (packetSize < resultSpecialHeaderSize + 4)
		{
			return FALSE;
		}

		uint32 RestartInterval = (uint32)((uint16)headerStart[resultSpecialHeaderSize] << 8 | (uint16)headerStart[resultSpecialHeaderSize + 1]);
		dri = RestartInterval;
		resultSpecialHeaderSize += 4;
	}

	if (Offset == 0) 
	{
		if (Q > 127) 
		{
			// Quantization Table header present
			/*
			0                   1                   2                   3
			0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
			+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
			|      MBZ      |   Precision   |             Length            |
			+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
			|                    Quantization Table Data                    |
			|                              ...                              |
			+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
			*/
			if (packetSize < resultSpecialHeaderSize + 4) 
			{
				return FALSE;
			}

			uint32 MBZ = (uint32)headerStart[resultSpecialHeaderSize];
			if (MBZ == 0) 
			{
				// uint32 Precision = (uint32)headerStart[resultSpecialHeaderSize + 1];
				uint32 Length = (uint32)((uint16)headerStart[resultSpecialHeaderSize + 2] << 8 | (uint16)headerStart[resultSpecialHeaderSize + 3]);

				//ASSERT(Length == 128);

				resultSpecialHeaderSize += 4;

				if (packetSize < resultSpecialHeaderSize + Length)
				{
					return FALSE;
				}

				qtlen = Length;
				qtables = &headerStart[resultSpecialHeaderSize];

				resultSpecialHeaderSize += Length;
			}
		}
	}

	// If this is the first (or only) fragment of a JPEG frame
	if (Offset == 0) 
	{
		uint8 newQtables[128];
		if (qtlen == 0) 
		{
			// A quantization table was not present in the RTP JPEG header,
			// so use the default tables, scaled according to the "Q" factor:
			mjpeg_make_default_qtables(newQtables, Q);
			qtables = newQtables;
			qtlen = sizeof newQtables;
		}
		
		p_rxi->d_offset = mjpeg_create_header(p_rxi->p_buf, type, width, height, qtables, qtlen, dri);
	}

	if ((p_rxi->d_offset + packetSize - resultSpecialHeaderSize) >= p_rxi->buf_len)
	{
		log_print(HT_LOG_ERR, "%s, fragment packet too big %d!!!", __FUNCTION__, p_rxi->d_offset + packetSize - resultSpecialHeaderSize);
		return FALSE;
	}
	
	memcpy(p_rxi->p_buf + p_rxi->d_offset, headerStart + resultSpecialHeaderSize, packetSize - resultSpecialHeaderSize);
	p_rxi->d_offset += packetSize - resultSpecialHeaderSize;
		
	// The RTP "M" (marker) bit indicates the last fragment of a frame:
	if (p_rxi->rtprxi.rxf_marker)
	{
		if (p_rxi->d_offset >= 2 && !(p_rxi->p_buf[p_rxi->d_offset-2] == 0xFF && p_rxi->p_buf[p_rxi->d_offset-1] == MARKER_EOI)) 
		{
    		p_rxi->p_buf[p_rxi->d_offset++] = 0xFF;
    		p_rxi->p_buf[p_rxi->d_offset++] = MARKER_EOI;
  		}

  		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 mjpeg_rtp_rx(MJPEGRXI * 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 mjpeg_data_rx(p_rxi, p_rxi->rtprxi.p_data, p_rxi->rtprxi.len);
}

BOOL mjpeg_rxi_init(MJPEGRXI * p_rxi, VRTPRXCBF cbf, void * p_userdata)
{
	memset(p_rxi, 0, sizeof(MJPEGRXI));

	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 FALSE;
    }
    
	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 TRUE;
}

void mjpeg_rxi_deinit(MJPEGRXI * p_rxi)
{
	if (p_rxi->p_buf_org)
	{
		free(p_rxi->p_buf_org);
    }
    
	memset(p_rxi, 0, sizeof(MJPEGRXI));
}




