/* * Copyright (C) 2011 Mamadou Diop. * * Contact: Mamadou Diop * * 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 */ #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 #if TSK_UNDER_WINDOWS # include // timeval #elif defined(__SYMBIAN32__) # include <_timeval.h> #else # include #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((_10ms_buf = tsk_calloc(jitterbuffer->_10ms_size_bytes, 1))) { 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) */