doubango/tinyDAV/src/codecs/fec/tdav_codec_red.c
c732d49e
 #if HAVE_CRT
 #define _CRTDBG_MAP_ALLOC 
 #include <stdlib.h> 
 #include <crtdbg.h>
 #endif //HAVE_CRT
 /*
74ca6d11
 * Copyright (C) 2020, University of the Basque Country (UPV/EHU)
c732d49e
 * Contact for licensing options: <licensing-mcpttclient(at)mcopenplatform(dot)com>
 *
 * The original file was part of Open Source Doubango Framework
 * Copyright (C) 2010-2011 Mamadou Diop.
 * Copyright (C) 2012 Doubango Telecom <http://doubango.org>
 *
 * This file is part of Open Source Doubango Framework.
 *
 * DOUBANGO is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * DOUBANGO is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with DOUBANGO.
 *
 */
 
 
 /**@file tdav_codec_red.c
  * @brief RTP Payload for Redundant Audio Data as per RFC 2198
  */
 #include "tinydav/codecs/fec/tdav_codec_red.h"
 
 #include "tinyrtp/rtp/trtp_rtp_packet.h"
 
 #include "tsk_memory.h"
 #include "tsk_time.h"
 #include "tsk_debug.h"
 
 typedef struct tdav_codec_red_s
 {
 	TMEDIA_DECLARE_CODEC_VIDEO;
 
 	tdav_codec_red_rtppacket_cb_f callback;
 	const void* callback_data;
 }
 tdav_codec_red_t;
 
 int tdav_codec_red_set_callback(tdav_codec_red_t *self, tdav_codec_red_rtppacket_cb_f callback, const void* callback_data)
 {
 	if(!self){
 		TSK_DEBUG_ERROR("Invalid parameter");
 		return -1;
 	}
 
 	self->callback = callback;
 	self->callback_data = callback_data;
 
 	return 0;
 }
 
 static int tdav_codec_red_open(tmedia_codec_t* self)
 {
 	return 0;
 }
 
 static int tdav_codec_red_close(tmedia_codec_t* self)
 {
 	return 0;
 }
 
 static tsk_size_t tdav_codec_red_encode(tmedia_codec_t* self, const void* in_data, tsk_size_t in_size, void** out_data, tsk_size_t* out_max_size)
 {
 	tdav_codec_red_t *red = (tdav_codec_red_t *)self;
 	tsk_size_t xsize = (in_size + 1);
 	static const uint8_t __first_octet = 0x00; // F=1, PT=0. Up to the caller to update this first octet with the right PT.
 
 	if(!red || !in_data || !in_size || !out_data || !out_max_size){
 		TSK_DEBUG_ERROR("Invalid parameter");
 		return 0;
 	}
 
 	if(*out_max_size < xsize){
 		if(!(*out_data = tsk_realloc(*out_data, xsize))){
 			TSK_DEBUG_ERROR("Failed to realloc data");
 			*out_max_size = 0;
 		}
 		*out_max_size = xsize;
 	}
 
 	((uint8_t*)*out_data)[0] = __first_octet;
 	memcpy(&((uint8_t*)*out_data)[1], in_data, in_size);
 
 	return xsize;
 }
 
 static tsk_size_t tdav_codec_red_decode(tmedia_codec_t* self, const void* in_data, tsk_size_t in_size, void** out_data, tsk_size_t* out_max_size, const tsk_object_t* proto_hdr)
 {
 	tdav_codec_red_t* red = (tdav_codec_red_t*)self;
 	const trtp_rtp_header_t* rtp_hdr = proto_hdr;
 	trtp_rtp_packet_t* red_rtp_pkt = tsk_null;
 	const uint8_t* pdata = in_data;
 	const uint8_t* red_hdr = in_data;
 	tsk_size_t red_hdrs_count, i;
 	tsk_bool_t last;
 	uint8_t F;
 	uint16_t timestamp_offset, block_length;
 
 	if(!red || !in_data || (in_size < TDAV_CODEC_RED_MIN_PKT_SIZE)|| !out_data){
 		TSK_DEBUG_ERROR("Invalid parameter");
 		return 0;
 	}
 
 	if(!red->callback){
 		TSK_DEBUG_WARN("Not callback installed for RED data");
 		return 0;
 	}
 
 	if((F = (pdata[0] & 0x80)) == 0){
 		i = 1;
 		red_hdrs_count = 1;
 	}
 	else{
 		for(i = 0, red_hdrs_count = 0; i < in_size; i+= 4, ++red_hdrs_count){
 			if((F = (pdata[i] & 0x80)) == 0){ ++i; ++red_hdrs_count;  break; }
 		}
 	}
 
 	if(i >= in_size){
 		TSK_DEBUG_ERROR("Invalid data");
 		return 0;
 	}
 	
 	pdata += i;
 	in_size -= i;
 
 	for(i = 0; i < red_hdrs_count && in_size > 0; ++i){
 		TSK_OBJECT_SAFE_FREE(red_rtp_pkt);
 		if(!(red_rtp_pkt = trtp_rtp_packet_create_null())){
 			TSK_DEBUG_ERROR("Failed to create RTP packet");
 			continue;
 		}
 		if(!(red_rtp_pkt->header = trtp_rtp_header_create(rtp_hdr->ssrc, rtp_hdr->seq_num, rtp_hdr->timestamp, rtp_hdr->payload_type, rtp_hdr->marker))){
 			TSK_DEBUG_ERROR("Failed to create RTP header");
 			continue;
 		}
 
 		// Must create an RTP packet for each RED chunck as they will be saved in the JB
 		last = (i == (red_hdrs_count - 1));
 		F = (red_hdr[0] & 0x80);
 		red_rtp_pkt->header->payload_type = (red_hdr[0] & 0x7F);
 		
 		if(last || !F){
 			/*
 			 0 1 2 3 4 5 6 7
              +-+-+-+-+-+-+-+-+
              |0|   Block PT  |
              +-+-+-+-+-+-+-+-+
 			 */
 			block_length = (uint16_t)in_size;
 		}
 		else{
 			/*
 			+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 		    |1| block PT=7  |  timestamp offset         |   block length    |
 		    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 			*/
 			timestamp_offset = ((red_hdr[1] << 8) | red_hdr[2]) >> 2;
 			block_length = ((red_hdr[2] & 0x03) << 8) | red_hdr[3];
 			if(block_length > in_size){
 				TSK_DEBUG_ERROR("Invalid 'block length'");
 				break;
 			}
 			red_rtp_pkt->header->timestamp += timestamp_offset;
 			red_hdr += 4;
 		}
 
 		// decode
 		if(red->callback){
 			// do not use "data_const" as payload will be saved in the jitter buffer and decoded later (async)
 			#if HAVE_CRT //Debug memory
 			if((red_rtp_pkt->payload.data =malloc(block_length))){
 			#else
 			if((red_rtp_pkt->payload.data = tsk_malloc(block_length))){
 			#endif //HAVE_CRT
 			
 				memcpy(red_rtp_pkt->payload.data, pdata, block_length);
 				red_rtp_pkt->payload.size = block_length;
 				red->callback(red->callback_data, red_rtp_pkt);
 			}
 		}
 
 		pdata += block_length;
 		in_size -= block_length;
 	}
 	
 	TSK_OBJECT_SAFE_FREE(red_rtp_pkt);
 
 	return 0; // must be always zero
 }
 
 static tsk_bool_t tdav_codec_red_sdp_att_match(const tmedia_codec_t* codec, const char* att_name, const char* att_value)
 {
 	return tsk_true;
 }
 
 static char* tdav_codec_red_sdp_att_get(const tmedia_codec_t* self, const char* att_name)
 {
 	return tsk_null;
 }
 
 
 /* ============ red object definition ================= */
 
 /* constructor */
 static tsk_object_t* tdav_codec_red_ctor(tsk_object_t * self, va_list * app)
 {
 	tdav_codec_red_t *red = self;
 	if(red){
 		/* init base: called by tmedia_codec_create() */
 		/* init self */
 		
 	}
 	return self;
 }
 /* destructor */
 static tsk_object_t* tdav_codec_red_dtor(tsk_object_t * self)
 { 
 	tdav_codec_red_t *red = self;
 	if(red){
 		/* deinit base */
 		tmedia_codec_video_deinit(red);
 		/* deinit self */
 		
 	}
 
 	return self;
 }
 /* object definition */
 static const tsk_object_def_t tdav_codec_red_def_s = 
 {
 	sizeof(tdav_codec_red_t),
 	tdav_codec_red_ctor, 
 	tdav_codec_red_dtor,
 	tmedia_codec_cmp, 
 };
 /* plugin definition*/
 static const tmedia_codec_plugin_def_t tdav_codec_red_plugin_def_s = 
 {
 	&tdav_codec_red_def_s,
 
 	(/* tmedia_video | tmedia_audio | */tmedia_t140), // FIXME: for now is only supported with T.140
 	tmedia_codec_id_red,
 	"red",
 	"red codec",
 	TMEDIA_CODEC_FORMAT_RED,
 	tsk_true,
 	1000, // rate: FIXME: for now it's only for T.140
 	
 	/* audio */
 	{ 0 },
 
 	/* video (defaul width,height,fps) */
 	{176, 144, 15},
 
 	tsk_null, // set()
 	tdav_codec_red_open,
 	tdav_codec_red_close,
 	tdav_codec_red_encode,
 	tdav_codec_red_decode,
 	tdav_codec_red_sdp_att_match,
 	tdav_codec_red_sdp_att_get
 };
 const tmedia_codec_plugin_def_t *tdav_codec_red_plugin_def_t = &tdav_codec_red_plugin_def_s;