From 921eec02b32823023156108e8c3a066e61583768 Mon Sep 17 00:00:00 2001 From: Alexandre Bourget <alexandre.bourget@savoirfairelinux.com> Date: Tue, 4 Sep 2007 17:14:02 -0400 Subject: [PATCH] IAX: Fix hangup SEGFAULTing the iax sound subsys + Add samplerate conversion to Mic output (still non-working) Adding samplerate conversion and sound output fixed the problems of the Jitter Buffer. It seems it updates the timestamps and causes less empty packets crashing the system. * Removed some debugging output. * Cleanups --- src/iaxvoiplink.cpp | 211 +++++++++++++++++++++++++++++--------------- src/iaxvoiplink.h | 7 ++ 2 files changed, 145 insertions(+), 73 deletions(-) diff --git a/src/iaxvoiplink.cpp b/src/iaxvoiplink.cpp index 481e10c4e4..68897ee96f 100644 --- a/src/iaxvoiplink.cpp +++ b/src/iaxvoiplink.cpp @@ -177,46 +177,108 @@ IAXVoIPLink::getEvent() continue; } - _debug ("Receive IAX Event: %d (0x%x)\n", event->etype, event->etype); + //_debug ("Receive IAX Event: %d (0x%x)\n", event->etype, event->etype); call = iaxFindCallBySession(event->session); if (call) { - _debug(" - We've got an associated call, handle call event\n"); + //_debug(" - We've got an associated call, handle call event\n"); iaxHandleCallEvent(event, call); } else if (event->session && event->session == _regSession) { - - _debug(" - We've got an associated REGISTRATION session, handle registration process\n"); + + //_debug(" - We've got an associated REGISTRATION session, handle registration process\n"); // in iaxclient, there is many session handling, here, only one iaxHandleRegReply(event); - + } else { - - _debug (" - We've got some other event, deal with them alone.\n"); + + //_debug (" - We've got some other event, deal with them alone.\n"); iaxHandlePrecallEvent(event); - + } - + iax_event_free(event); } - // Woah, we should do that in another thread, which will always send out stuff.. - // send sound here - if(_currentCall && audiolayer) { - int samples = audiolayer->canGetMic(); - if (samples != 0) { - //int datalen = audiolayer->getMic(_sendDataEncoded, samples); - //_debug("iax_send_voice(%p, %d, ,%d, %d)\n", _currentCall->getSession(), _currentCall->getFormat(), datalen, samples); - //if ( iax_send_voice(_currentCall->getSession(), _currentCall->getFormat(), (char*)_sendDataEncoded, datalen, samples) == -1) { - // // error sending voice - //} + _mutexIAX.leaveMutex(); + + // Send sound here + if (_currentCall && audiolayer) { + AudioCodec* audiocodec = _currentCall->getAudioCodec(); + + // we have to get 20ms of data from the mic *20/1000 = /50 + // rate/50 shall be lower than IAX__20S_48KHZ_MAX + int maxBytesToGet = audiolayer->getSampleRate()/50*sizeof(SFLDataFormat); + + // available bytes inside ringbuffer + int availBytesFromMic = audiolayer->canGetMic(); + + // take the lowest + int bytesAvail = (availBytesFromMic < maxBytesToGet) ? availBytesFromMic : maxBytesToGet; + //_debug("available = %d, maxBytesToGet = %d\n", availBytesFromMic, maxBytesToGet); + + // Get bytes from micRingBuffer to data_from_mic + int nbSample = audiolayer->getMic(_dataAudioLayer, bytesAvail) / sizeof(SFLDataFormat); + int16* toIAX = NULL; + if (audiolayer->getSampleRate() != audiocodec->getClockRate() && nbSample) { + SRC_DATA src_data; +#ifdef DATAFORMAT_IS_FLOAT + src_data.data_in = _dataAudioLayer; +#else + src_short_to_float_array(_dataAudioLayer, _floatBuffer48000, nbSample); + src_data.data_in = _floatBuffer48000; +#endif + + double factord = (double) audiocodec->getClockRate() / audiolayer->getSampleRate(); + + src_data.src_ratio = factord; + src_data.input_frames = nbSample; + src_data.output_frames = (int) floor(factord * nbSample); + src_data.data_out = _floatBuffer8000; + src_data.end_of_input = 0; /* More data to come */ + + src_process(_src_state_mic, &src_data); + + nbSample = src_data.output_frames_gen; + + src_float_to_short_array (_floatBuffer8000, _intBuffer8000, nbSample); + toIAX = _intBuffer8000; + } else { +#ifdef DATAFORMAT_IS_FLOAT + // convert _receiveDataDecoded to float inside _receiveData + src_float_to_short_array(_dataAudioLayer, _intBuffer8000, nbSample); + toIAX = _intBuffer8000; + //if (nbSample > IAX__20S_8KHZ_MAX) { _debug("Alert from mic, nbSample %d is bigger than expected %d\n", nbSample, IAX__20S_8KHZ_MAX); } +#else + toIAX = _dataAudioLayer; // int to int +#endif + } + + if ( nbSample < (IAX__20S_8KHZ_MAX - 10) ) { // if only 10 is missing, it's ok + // fill end with 0... + //_debug("begin: %p, nbSample: %d\n", toIAX, nbSample); + //_debug("has to fill: %d chars at %p\n", (IAX__20S_8KHZ_MAX-nbSample)*sizeof(int16), toIAX + nbSample); + memset(toIAX + nbSample, 0, (IAX__20S_8KHZ_MAX-nbSample)*sizeof(int16)); + nbSample = IAX__20S_8KHZ_MAX; } + //_debug("AR: Nb sample: %d int, [0]=%d [1]=%d [2]=%d\n", nbSample, toIAX[0], toIAX[1], toIAX[2]); + + // for the mono: range = 0 to IAX_FRAME2SEND * sizeof(int16) + int compSize = audiocodec->codecEncode(_sendDataEncoded, toIAX, nbSample*sizeof(int16)); + + // Send it out! + _mutexIAX.enterMutex(); + // Make sure the session and the call still exists. + if (_currentCall->getSession()) { + if ( iax_send_voice(_currentCall->getSession(), _currentCall->getFormat(), (unsigned char*)_sendDataEncoded, compSize, nbSample) == -1) { + _debug("IAX: Error sending voice data.\n"); + } + } + _mutexIAX.leaveMutex(); } - // unlock mutex here - _mutexIAX.leaveMutex(); //iaxRefreshRegistrations(); // thread wait 5 millisecond @@ -498,7 +560,7 @@ IAXVoIPLink::iaxHandleCallEvent(iax_event* event, IAXCall* call) case IAX_EVENT_HANGUP: Manager::instance().peerHungupCall(id); if (Manager::instance().isCurrentCall(id)) { - _currentCall = 0; + _currentCall = NULL; audiolayer->stopStream(); // stop audio } @@ -509,16 +571,14 @@ IAXVoIPLink::iaxHandleCallEvent(iax_event* event, IAXCall* call) Manager::instance().peerHungupCall(id); if (Manager::instance().isCurrentCall(id)) { // stop audio - _currentCall = 0; + _currentCall = NULL; audiolayer->stopStream(); } removeCall(id); break; case IAX_EVENT_ACCEPT: - // accept - // - + // Call accepted over there by the computer, not the user yet. if (event->ies.format) { call->setFormat(event->ies.format); } @@ -540,8 +600,7 @@ IAXVoIPLink::iaxHandleCallEvent(iax_event* event, IAXCall* call) audiolayer->startStream(); // start audio here? } else { - // deja connecté - // ? + // deja connecté ? } break; @@ -554,26 +613,62 @@ IAXVoIPLink::iaxHandleCallEvent(iax_event* event, IAXCall* call) break; case IAX_EVENT_VOICE: + iaxHandleVoiceEvent(event, call); + break; + + case IAX_EVENT_TEXT: + break; + + case IAX_EVENT_RINGA: + break; + + case IAX_EVENT_PONG: + break; + + case IAX_EVENT_URL: + break; + + // case IAX_EVENT_CNG: ?? + // break; + + case IAX_EVENT_TIMEOUT: + break; + + case IAX_EVENT_TRANSFER: + break; + + default: + _debug("Unknown event type: %d\n", event->etype); + + } +} + + +/* Handle audio event, VOICE packet received */ +void +IAXVoIPLink::iaxHandleVoiceEvent(iax_event* event, IAXCall* call) +{ + // If we receive datalen == 0, some things of the jitter buffer in libiax2/iax.c + // were triggered + if (!event->datalen) { + // Skip this empty packet. + //_debug("IAX: Skipping empty jitter-buffer interpolated packet\n"); + return; + } + if (audiolayer) { AudioCodec* audiocodec = call->getAudioCodec(); - if (!audiocodec) { - // libiax2 stores the voiceformat in the 'subclass' element. - if (event->subclass) { - // Set the format, with the first voice packet - call->setFormat(event->subclass); - audiocodec = call->getAudioCodec(); - } else { - // Send a VNAK, because they sent a Mini packet before - // a full VOICE packet (with the format inside) - _debug("IAX: sending VNAK, received mini packet before full VOICE packet."); - iax_vnak(event->session); - break; - } + // On-the-fly codec changing (normally, when we receive a full packet) + // as per http://tools.ietf.org/id/draft-guy-iax-03.txt + // - subclass holds the voiceformat property. + if (event->subclass && event->subclass != call->getFormat()) { + call->setFormat(event->subclass); + audiocodec = call->getAudioCodec(); } - _debug("Receive: len=%d, format=%d, _receiveDataDecoded=%p\n", event->datalen, call->getFormat(), _receiveDataDecoded); + unsigned char* data = (unsigned char*)event->data; unsigned int size = event->datalen; @@ -582,14 +677,11 @@ IAXVoIPLink::iaxHandleCallEvent(iax_event* event, IAXCall* call) size = IAX__20S_8KHZ_MAX; } - // On pourrait ajuster le codec dynamiquement ici, comme c'est fait dans SIP. - // à moins que IAX ne permette pas de changer le codec à chaque paquet. - int expandedSize = audiocodec->codecDecode(_receiveDataDecoded, data, size); int nbInt16 = expandedSize/sizeof(int16); if (nbInt16 > IAX__20S_8KHZ_MAX) { - _debug("We have decoded a IAX VOICE packet larger than expected: %s VS %s. Cropping.\n", nbInt16, IAX__20S_8KHZ_MAX); + _debug("We have decoded an IAX VOICE packet larger than expected: %s VS %s. Cropping.\n", nbInt16, IAX__20S_8KHZ_MAX); nbInt16 = IAX__20S_8KHZ_MAX; } @@ -635,33 +727,6 @@ IAXVoIPLink::iaxHandleCallEvent(iax_event* event, IAXCall* call) } else { _debug("IAX: incoming audio, but no sound card open"); } - break; - - case IAX_EVENT_TEXT: - break; - - case IAX_EVENT_RINGA: - break; - - case IAX_EVENT_PONG: - break; - - case IAX_EVENT_URL: - break; - - // case IAX_EVENT_CNG: ?? - // break; - - case IAX_EVENT_TIMEOUT: - break; - - case IAX_EVENT_TRANSFER: - break; - - default: - _debug("Unknown event type: %d\n", event->etype); - - } } diff --git a/src/iaxvoiplink.h b/src/iaxvoiplink.h index 481f9f9083..4910b5702f 100644 --- a/src/iaxvoiplink.h +++ b/src/iaxvoiplink.h @@ -105,6 +105,13 @@ private: */ void iaxHandleCallEvent(iax_event* event, IAXCall* call); + /** + * Handle the VOICE events specifically + * @param event The iax_event containing the IAX_EVENT_VOICE + * @param call The associated IAXCall + */ + void iaxHandleVoiceEvent(iax_event* event, IAXCall* call); + /** * Handle IAX Registration Reply event * @param event An iax_event pointer -- GitLab