#if HAVE_CRT #define _CRTDBG_MAP_ALLOC #include #include #endif //HAVE_CRT /* * Copyright (C) 2020, University of the Basque Country (UPV/EHU) * Contact for licensing options: * * The original file was part of Open Source Doubango Framework * Copyright (C) 2010-2011 Mamadou Diop. * Copyright (C) 2012 Doubango Telecom * * 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;