doubango/tinyDAV/src/audio/tdav_speakup_jitterbuffer.c
c732d49e
 #if HAVE_CRT
 #define _CRTDBG_MAP_ALLOC 
 #include <stdlib.h> 
 #include <crtdbg.h>
 #endif //HAVE_CRT
 /*
 * Copyright (C) 2017, University of the Basque Country (UPV/EHU)
 * 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_speakup_jitterbuffer.c
  * @brief Speakup Audio jitterbuffer Plugin
  *
  * @author Mamadou Diop <diopmamadou(at)doubango.org>
 
  */
 #include "tinydav/audio/tdav_speakup_jitterbuffer.h"
 
 #if !(HAVE_SPEEX_DSP && HAVE_SPEEX_JB)
 
 #include "tinyrtp/rtp/trtp_rtp_header.h"
 
 #include "tsk_time.h"
 #include "tsk_memory.h"
 #include "tsk_debug.h"
 
 #include <string.h>
 
 #if TSK_UNDER_WINDOWS
 #       include <Winsock2.h> // timeval
 #elif defined(__SYMBIAN32__)
 #       include <_timeval.h> 
 #else
 #       include <sys/time.h>
 #endif
 
 #define TDAV_SPEAKUP_10MS						10
 #define TDAV_SPEAKUP_10MS_FRAME_SIZE(self)		(((self)->rate * TDAV_SPEAKUP_10MS)/1000)
 #define TDAV_SPEAKUP_PTIME_FRAME_SIZE(self)		(((self)->rate * (self)->framesize)/1000)
 
 static int tdav_speakup_jitterbuffer_set(tmedia_jitterbuffer_t *self, const tmedia_param_t* param)
 {
 	TSK_DEBUG_ERROR("Not implemented");
 	return -2;
 }
 
 static int tdav_speakup_jitterbuffer_open(tmedia_jitterbuffer_t* self, uint32_t frame_duration, uint32_t rate, uint32_t channels)
 {
 	tdav_speakup_jitterbuffer_t *jitterbuffer = (tdav_speakup_jitterbuffer_t *)self;
 	if(!jitterbuffer->jbuffer){
 		if(!(jitterbuffer->jbuffer = jb_new())){
 			TSK_DEBUG_ERROR("Failed to create new buffer");
 			return -1;
 		}
 		jitterbuffer->jcodec = JB_CODEC_OTHER;
 	}
 	jitterbuffer->ref_timestamp = 0;
 	jitterbuffer->frame_duration = frame_duration;
 	jitterbuffer->rate = rate;
 	jitterbuffer->channels = channels;
 	jitterbuffer->_10ms_size_bytes = 160 * (rate/8000);
 
 	return 0;
 }
 
 static int tdav_speakup_jitterbuffer_tick(tmedia_jitterbuffer_t* self)
 {
 	return 0;
 }
 
 static int tdav_speakup_jitterbuffer_put(tmedia_jitterbuffer_t* self, void* data, tsk_size_t data_size, const tsk_object_t* proto_hdr)
 {
 	tdav_speakup_jitterbuffer_t *jitterbuffer = (tdav_speakup_jitterbuffer_t *)self;
 	const trtp_rtp_header_t* rtp_hdr = (const trtp_rtp_header_t*)proto_hdr;
     int i;
     long now, ts;
     void* _10ms_buf;
 	uint8_t* pdata;
 
 	if(!self || !data || !data_size || !jitterbuffer->jbuffer || !rtp_hdr){
 		TSK_DEBUG_ERROR("Invalid parameter");
 		return -1;
 	}
 
 	/* synchronize the reference timestamp */
 	if(!jitterbuffer->ref_timestamp){
 		uint64_t now = tsk_time_now();
 		struct timeval tv;
 		long ts = (rtp_hdr->timestamp/(jitterbuffer->rate/1000));
 		//=> Do not use (see clock_gettime() on linux): tsk_gettimeofday(&tv, tsk_null);
 		tv.tv_sec = (long)(now)/1000;
 		tv.tv_usec = (long)(now - (tv.tv_sec*1000))*1000;
 
 		tv.tv_sec -= (ts / jitterbuffer->rate);
 		tv.tv_usec -= (ts % jitterbuffer->rate) * 125;
 		if((tv.tv_usec -= (tv.tv_usec % (TDAV_SPEAKUP_10MS * 10000))) <0){
 			tv.tv_usec += 1000000;
 			tv.tv_sec -= 1;
 		}
 		jitterbuffer->ref_timestamp = tsk_time_get_ms(&tv);
 
 		switch(rtp_hdr->payload_type){
 			case 8: /*TMEDIA_CODEC_FORMAT_G711a*/
 			case 0: /* TMEDIA_CODEC_FORMAT_G711u */
 				jitterbuffer->jcodec = JB_CODEC_G711x;
 				break;
 			case 18: /* TMEDIA_CODEC_FORMAT_G729 */
 				jitterbuffer->jcodec = JB_CODEC_G729A;
 				break;
 			case 3: /* TMEDIA_CODEC_FORMAT_GSM */
 				jitterbuffer->jcodec = JB_CODEC_GSM_EFR;
 				break;
 
 			default:
 				jitterbuffer->jcodec = JB_CODEC_OTHER;
 				break;
 		}
 	}
 
 	// split as several 10ms frames
 	now = (long) (tsk_time_now()-jitterbuffer->ref_timestamp);
 	ts = (long)(rtp_hdr->timestamp/(jitterbuffer->rate/1000));
 	pdata = (uint8_t*)data;
 	for(i=0; i<(int)(data_size/jitterbuffer->_10ms_size_bytes);i++){
 		#if HAVE_CRT //Debug memory
 		if((_10ms_buf = calloc(jitterbuffer->_10ms_size_bytes, 1))){
 		#else
 		if((_10ms_buf = tsk_calloc(jitterbuffer->_10ms_size_bytes, 1))){
 		#endif //HAVE_CRT
 		
 			memcpy(_10ms_buf, &pdata[i*jitterbuffer->_10ms_size_bytes], jitterbuffer->_10ms_size_bytes);
 			jb_put(jitterbuffer->jbuffer, _10ms_buf, JB_TYPE_VOICE, TDAV_SPEAKUP_10MS, ts, now, jitterbuffer->jcodec);
 			_10ms_buf = tsk_null;
 		}
 		ts += TDAV_SPEAKUP_10MS;
 	}
 	
 	return 0;
 }
 
 static tsk_size_t tdav_speakup_jitterbuffer_get(tmedia_jitterbuffer_t* self, void* out_data, tsk_size_t out_size)
 {
 	tdav_speakup_jitterbuffer_t *jitterbuffer = (tdav_speakup_jitterbuffer_t *)self;
 	int jret;
 
 	int i, _10ms_count;
 	long now;
 	short* _10ms_buf = tsk_null;
 	uint8_t* pout_data = (uint8_t*)out_data;
 
 	if(!out_data || (out_size % jitterbuffer->_10ms_size_bytes)){
 		TSK_DEBUG_ERROR("Invalid parameter");
 		return 0;
 	}
 	
 	_10ms_count = (out_size/jitterbuffer->_10ms_size_bytes);
 	now = (long) (tsk_time_now() - jitterbuffer->ref_timestamp);
 	for(i=0; i<_10ms_count; i++){
 
 		jret = jb_get(jitterbuffer->jbuffer, (void**)&_10ms_buf, now, TDAV_SPEAKUP_10MS);
 		switch(jret){
 			case JB_INTERP:
 				TSK_DEBUG_INFO("JB_INTERP");
 				jb_reset_all(jitterbuffer->jbuffer);
 				memset(&pout_data[i*jitterbuffer->_10ms_size_bytes], 0, (_10ms_count*jitterbuffer->_10ms_size_bytes)-(i*jitterbuffer->_10ms_size_bytes));
 				i = _10ms_count; // for exit
 				break;
 			case JB_OK:
 			case JB_EMPTY:
 			case JB_NOFRAME:
 			case JB_NOJB:
 				{
 					if(_10ms_buf && (jret == JB_OK)){
 						/* copy data */
 						memcpy(&pout_data[i*jitterbuffer->_10ms_size_bytes], _10ms_buf, jitterbuffer->_10ms_size_bytes);
 					}
 					else{
 						/* copy silence */
 						memset(&pout_data[i*jitterbuffer->_10ms_size_bytes], 0, jitterbuffer->_10ms_size_bytes);
 					}
 				}
 
 			default:
 				break;
 		}
 		TSK_FREE(_10ms_buf);
 	}
 
 	return (_10ms_count * jitterbuffer->_10ms_size_bytes);
 }
 
 static int tdav_speakup_jitterbuffer_reset(tmedia_jitterbuffer_t* self)
 {
 	tdav_speakup_jitterbuffer_t *jitterbuffer = (tdav_speakup_jitterbuffer_t *)self;
 	if(jitterbuffer->jbuffer){
 		jb_reset_all(jitterbuffer->jbuffer);
 		return 0;
 	}
 	else{
 		TSK_DEBUG_ERROR("invalid parameter");
 		return -1;
 	}
 }
 
 static int tdav_speakup_jitterbuffer_close(tmedia_jitterbuffer_t* self)
 {
 	tdav_speakup_jitterbuffer_t *jitterbuffer = (tdav_speakup_jitterbuffer_t *)self;
 	if(jitterbuffer->jbuffer){
 		jb_destroy(jitterbuffer->jbuffer);
 		jitterbuffer->jbuffer = tsk_null;
 	}
 	return 0;
 }
 
 
 
 //
 //	Speakup jitterbufferr Plugin definition
 //
 
 /* constructor */
 static tsk_object_t* tdav_speakup_jitterbuffer_ctor(tsk_object_t * self, va_list * app)
 {
 	tdav_speakup_jitterbuffer_t *jitterbuffer = self;
 	TSK_DEBUG_INFO("Create speekup jitter buffer");
 	if(jitterbuffer){
 		/* init base */
 		tmedia_jitterbuffer_init(TMEDIA_JITTER_BUFFER(jitterbuffer));
 		/* init self */
 	}
 	return self;
 }
 /* destructor */
 static tsk_object_t* tdav_speakup_jitterbuffer_dtor(tsk_object_t * self)
 { 
 	tdav_speakup_jitterbuffer_t *jitterbuffer = self;
 	if(jitterbuffer){
 		/* deinit base */
 		tmedia_jitterbuffer_deinit(TMEDIA_JITTER_BUFFER(jitterbuffer));
 		/* deinit self */
 		if(jitterbuffer->jbuffer){
 			jb_destroy(jitterbuffer->jbuffer);
 			jitterbuffer->jbuffer = tsk_null;
 		}
 	}
 
 	return self;
 }
 /* object definition */
 static const tsk_object_def_t tdav_speakup_jitterbuffer_def_s = 
 {
 	sizeof(tdav_speakup_jitterbuffer_t),
 	tdav_speakup_jitterbuffer_ctor, 
 	tdav_speakup_jitterbuffer_dtor,
 	tsk_null, 
 };
 /* plugin definition*/
 static const tmedia_jitterbuffer_plugin_def_t tdav_speakup_jitterbuffer_plugin_def_s = 
 {
 	&tdav_speakup_jitterbuffer_def_s,
 	tmedia_audio,
 	"Audio/video JitterBuffer based on Speakup",
 	
 	tdav_speakup_jitterbuffer_set,
 	tdav_speakup_jitterbuffer_open,
 	tdav_speakup_jitterbuffer_tick,
 	tdav_speakup_jitterbuffer_put,
 	tdav_speakup_jitterbuffer_get,
 	tdav_speakup_jitterbuffer_reset,
 	tdav_speakup_jitterbuffer_close,
 };
 const tmedia_jitterbuffer_plugin_def_t *tdav_speakup_jitterbuffer_plugin_def_t = &tdav_speakup_jitterbuffer_plugin_def_s;
 
 #endif /* !(HAVE_SPEEX_DSP && HAVE_SPEEX_JB) */