www.pudn.com > exosip.rar > sound-ortp.c
/* * josua - Jack's open sip User Agent * * Copyright (C) 2002,2003 Aymeric Moizard* * This 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 2, * or (at your option) any later version. * * This 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 dpkg; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "jcalls.h" #ifdef ORTP_SUPPORT #include #include #include #include #include # define TIMEVAL_TO_TIMESPEC(tv, ts) { \ (ts)->tv_sec = (tv)->tv_sec; \ (ts)->tv_nsec = (tv)->tv_usec * 1000; \ } # define TIMESPEC_TO_TIMEVAL(tv, ts) { \ (tv)->tv_sec = (ts)->tv_sec; \ (tv)->tv_usec = (ts)->tv_nsec / 1000; \ #define AUDIO_DEVICE "/dev/dsp" static int min_size = 0; #ifdef SPEEX_SUPPORT void * os_sound_start_thread (void *_ca) { call_t *ca = (call_t *) _ca; char data_in[160]; short sp_data_in_s[640]; float sp_data_in_f[640]; int have_more; int timestamp = 0; int i; while (ca->enable_audio != -1) { memset (data_in, 0, 160); /* receive ONE packet */ i = rtp_session_recv_with_ts (ca->rtp_session, data_in, 160, timestamp, &have_more); speex_bits_reset (&ca->dec_speex_bits); speex_bits_read_from (&ca->dec_speex_bits, data_in, i); while (1) { int k; k = speex_decode (ca->speex_dec, &ca->dec_speex_bits, sp_data_in_f); if (k == -2) { exit (0); } else if (k == -1) { break; } else if (k == 0) { int j; for (j = 0; j < ca->speex_fsize; j++) { if (sp_data_in_f[j] > 32767) sp_data_in_f[j] = 32767; if (sp_data_in_f[j] < -32767) sp_data_in_f[j] = -32767; sp_data_in_s[j] = (short) sp_data_in_f[j]; } write (ca->fd, sp_data_in_s, ca->speex_fsize); } } timestamp += 160; } return NULL; } #else #define MINIMIZE_COPYING 1 #define BLOCKING_MODE 1 void dbgbpt1 () { } void dbgbpt2 () { } static void tvsub (register struct timeval *out, register struct timeval *in) { out->tv_usec -= in->tv_usec; while (out->tv_usec < 0) { --out->tv_sec; out->tv_usec += 1000000; } out->tv_sec -= in->tv_sec; } void * os_sound_start_thread (void *_ca) { call_t *ca = (call_t *) _ca; char data_in[512]; char data_in_dec[1024]; int have_more = 0; int timestamp = 0; int i; mblk_t *mp; struct timeval pkt_in_time, pkt_out_time, sleeptime; struct timespec sleepns; OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_INFO2, NULL, "oRTP thread started\n")); while (ca->enable_audio != -1) { #if !MINIMIZE_COPYING do { memset (data_in, 0, 160); i = rtp_session_recv_with_ts (ca->rtp_session, data_in, 160, timestamp, &have_more); if (i > 0) { if (ca->payload == 8) /* A-Law */ alaw_dec (data_in, data_in_dec, 160); if (ca->payload == 0) /* Mu-Law */ mulaw_dec (data_in, data_in_dec, 160); write (ca->fd, data_in_dec, i * 2); } } while (have_more); #else /* MINIMIZE_COPYING */ if ((mp = rtp_session_recvm_with_ts (ca->rtp_session, timestamp)) != NULL) { int len = mp->b_cont->b_wptr - mp->b_cont->b_rptr; gettimeofday (&pkt_in_time, 0); if (ca->enable_audio != -1) { if (ca->payload == 8) /* A-Law */ alaw_dec (mp->b_cont->b_rptr, data_in_dec, len); if (ca->payload == 0) /* Mu-Law */ mulaw_dec (mp->b_cont->b_rptr, data_in_dec, len); write (ca->fd, data_in_dec, len * 2); } if (ca->enable_audio == -1) dbgbpt1 (); freemsg (mp); gettimeofday (&pkt_out_time, 0); /* compute the time we spent processing the packet */ tvsub (&pkt_out_time, &pkt_in_time); } #endif sleeptime.tv_usec = 10000; sleeptime.tv_sec = 0; tvsub (&sleeptime, &pkt_out_time); TIMEVAL_TO_TIMESPEC (&sleeptime, &sleepns); #if !BLOCKING_MODE if (ca->enable_audio != -1) nanosleep (&sleepns, 0); #endif timestamp += 160; } OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_INFO2, NULL, "rtp reading thread stopped\n")); dbgbpt2 (); return NULL; } #endif #ifdef SPEEX_SUPPORT void * os_sound_start_out_thread (void *_ca) { call_t *ca = (call_t *) _ca; char data_out[10000]; short sp_data_out_s[640]; float sp_data_out_f[640]; int timestamp = 0; int i; while (ca->enable_audio != -1) { int k; speex_bits_reset (&ca->speex_bits); for (k = 0; k < ca->speex_nb_packet; k++) { int j; i = read (ca->fd, sp_data_out_s, ca->speex_fsize * sizeof (short)); if (i > 0) { for (j = 0; j < ca->speex_fsize; j++) { /* convert to float */ sp_data_out_f[j] = sp_data_out_s[j]; } speex_encode (ca->speex_enc, sp_data_out_f, &ca->speex_bits); } } speex_bits_insert_terminator (&ca->speex_bits); /* convert to char */ i = speex_bits_write (&ca->speex_bits, data_out, sizeof (data_out)); rtp_session_send_with_ts (ca->rtp_session, data_out, i, timestamp); timestamp += i; } return NULL; } #else /* not with speex */ void * os_sound_start_out_thread (void *_ca) { call_t *ca = (call_t *) _ca; char data_out[1000]; char data_out_enc[1000]; int timestamp = 0; int i; int min_size = 160; OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_INFO2, NULL, "rtp writing thread started\n")); while (ca->enable_audio != -1) { memset (data_out, 0, min_size * 2); i = read (ca->fd, data_out, min_size * 2); if (ca->local_sendrecv == _SENDRECV || ca->local_sendrecv == _RECVONLY) { } else { if (i < min_size * 2) { memset (data_out, 0, min_size * 2); OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_INFO2, NULL, "restarting: %i\n", i)); close (ca->fd); /* send a wav file! */ ca->fd = open (ca->wav_file, O_RDONLY); if (ca->fd < 0) { OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_INFO2, NULL, "Could not open wav file: %s\n", ca->wav_file)); ca->fd = -1; break; } i = read (ca->fd, data_out, min_size * 2); } } if (i > 0) { if (ca->payload == 8) /* A-Law */ alaw_enc (data_out, data_out_enc, i); if (ca->payload == 0) /* Mu-Law */ mulaw_enc (data_out, data_out_enc, i); rtp_session_send_with_ts (ca->rtp_session, data_out_enc, i / 2, timestamp); timestamp = timestamp + (i / 2); } } OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_INFO2, NULL, "rtp writing thread stopped\n")); return NULL; } #endif int os_sound_init () { ortp_init (); ortp_scheduler_init (); ortp_set_debug_file ("oRTP", NULL); #ifdef SPEEX_SUPPORT rtp_profile_set_payload (&av_profile, 115, &lpc1015); rtp_profile_set_payload (&av_profile, 110, &speex_nb); rtp_profile_set_payload (&av_profile, 111, &speex_wb); #endif return 0; } int os_sound_start (call_t * ca, int port) { int p, cond; int bits = 16; int stereo = 0; /* 0 is mono */ int rate = 8000; int blocksize = 512; char localip[128]; eXosip_guess_localip (AF_INET, localip, 128); if (port == 0) return -1; if (ca->local_sendrecv == _SENDRECV || ca->local_sendrecv == _RECVONLY) { ca->fd = open (AUDIO_DEVICE, O_RDWR | O_NONBLOCK); if (ca->fd < 0) return -EWOULDBLOCK; fcntl (ca->fd, F_SETFL, fcntl (ca->fd, F_GETFL) & ~O_NONBLOCK); ioctl (ca->fd, SNDCTL_DSP_RESET, 0); p = bits; /* 16 bits */ ioctl (ca->fd, SNDCTL_DSP_SAMPLESIZE, &p); p = stereo; /* number of channels */ ioctl (ca->fd, SNDCTL_DSP_CHANNELS, &p); p = AFMT_S16_NE; /* choose LE or BE (endian) */ ioctl (ca->fd, SNDCTL_DSP_SETFMT, &p); p = rate; /* rate in khz */ ioctl (ca->fd, SNDCTL_DSP_SPEED, &p); ioctl (ca->fd, SNDCTL_DSP_GETBLKSIZE, &min_size); if (min_size > blocksize) { cond = 1; p = min_size / blocksize; while (cond) { int i = ioctl (ca->fd, SNDCTL_DSP_SUBDIVIDE, &p); /* printf("SUB_DIVIDE said error=%i,errno=%i\n",i,errno); */ if ((i == 0) || (p == 1)) cond = 0; else p = p / 2; } } ioctl (ca->fd, SNDCTL_DSP_GETBLKSIZE, &min_size); if (min_size > blocksize) { OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_ERROR, NULL, "dsp block size set to %i.", min_size)); close (ca->fd); return -1; } else { /* no need to access the card with less latency than needed */ min_size = blocksize; } OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_INFO2, NULL, "blocksize = %i\n", min_size)); } else { /* send a wav file! */ ca->fd = open (ca->wav_file, O_RDONLY); if (ca->fd < 0) { OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_INFO2, NULL, "Could not open wav file: %s\n", ca->wav_file)); return -1; } } #ifdef SPEEX_SUPPORT { float vbr_qual; int value; int quality; ca->speex_enc = speex_encoder_init (&speex_nb_mode); /* 8kHz */ /* 16kHz speex_enc = speex_encoder_init(&speex_wb_mode); */ /* 32kHz speex_enc = speex_encoder_init(&speex_uwb_mode); */ ca->speex_dec = speex_decoder_init (&speex_nb_mode); value = 1; speex_decoder_ctl (ca->speex_dec, SPEEX_SET_ENH, &value); quality = 8; /* 15kb */ speex_encoder_ctl (ca->speex_enc, SPEEX_SET_QUALITY, &quality); /* ou bien le bit rate: value = 15000; // 15kb speex_encoder_ctl(ca->speex_enc, SPEEX_SET_BITRATE, &value); */ /* silence suppression (VAD) value = 1; // 15kb speex_encoder_ctl(ca->speex_enc, SPEEX_SET_VAD, &value); Discontinuous transmission (DTX) value = 1; // 15kb speex_encoder_ctl(ca->speex_enc, SPEEX_SET_DTX, &value); Variable Bit Rate (VBR) value = 1; // 15kb speex_encoder_ctl(ca->speex_enc, SPEEX_SET_VBR, &value); vbr_qual = 5,0; // between 0 and 10 speex_encoder_ctl(ca->speex_enc, SPEEX_SET_VBR_QUALITY, &vbr_qual); Average bit rate: (ABR) value = 15000; // 15kb speex_encoder_ctl(ca->speex_enc, SPEEX_SET_ABR, &value); */ speex_encoder_ctl (ca->speex_enc, SPEEX_GET_FRAME_SIZE, &ca->speex_fsize); ca->speex_nb_packet = 1; speex_bits_init (&(ca->speex_bits)); speex_bits_init (&(ca->dec_speex_bits)); } #endif if (ca->local_sendrecv == _SENDRECV) { ca->rtp_session = rtp_session_new (RTP_SESSION_SENDRECV); OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_INFO2, NULL, "starting sendrecv session\n")); } else if (ca->local_sendrecv == _SENDONLY) { ca->rtp_session = rtp_session_new (RTP_SESSION_SENDONLY); OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_INFO2, NULL, "starting sendonly session\n")); } else { ca->rtp_session = rtp_session_new (RTP_SESSION_RECVONLY); OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_INFO2, NULL, "starting recvonly session\n")); } rtp_session_set_scheduling_mode (ca->rtp_session, 1); /* yes */ rtp_session_set_blocking_mode (ca->rtp_session, 1); rtp_session_set_profile (ca->rtp_session, &av_profile); rtp_session_set_jitter_compensation (ca->rtp_session, 60); rtp_session_set_local_addr (ca->rtp_session, localip, port); rtp_session_set_remote_addr (ca->rtp_session, ca->remote_sdp_audio_ip, ca->remote_sdp_audio_port); rtp_session_set_payload_type (ca->rtp_session, ca->payload); rtp_session_signal_connect (ca->rtp_session, "telephone-event", (RtpCallback) rcv_telephone_event, ca); /* enter a loop (thread?) to send AUDIO data with rtp_session_send_with_ts(ca->rtp_session, data, data_length, timestamp); */ ca->enable_audio = 1; if (ca->local_sendrecv == _SENDRECV || ca->local_sendrecv == _RECVONLY) { ca->audio_thread = osip_thread_create (20000, os_sound_start_thread, ca); } if (ca->local_sendrecv == _SENDRECV || ca->local_sendrecv == _SENDONLY) { ca->out_audio_thread = osip_thread_create (20000, os_sound_start_out_thread, ca); } return 0; } void os_sound_close (call_t * ca) { if (ca->rtp_session != NULL) { ca->enable_audio = -1; osip_thread_join (ca->audio_thread); osip_free (ca->audio_thread); ca->audio_thread = NULL; osip_thread_join (ca->out_audio_thread); osip_free (ca->out_audio_thread); ca->out_audio_thread = NULL; rtp_session_signal_disconnect_by_callback (ca->rtp_session, "telephone-event", (RtpCallback) rcv_telephone_event); rtp_session_destroy (ca->rtp_session); } #ifdef SPEEX_SUPPORT speex_bits_destroy (&ca->speex_bits); speex_bits_destroy (&ca->dec_speex_bits); speex_encoder_destroy (&ca->speex_enc); speex_decoder_destroy (&ca->speex_dec); #endif close (ca->fd); /* close the sound card */ } #endif