static void handle_video_event(struct iax_event *e, int callNo) { struct iaxc_call *call; if ( callNo < 0 ) return; if ( e->datalen == 0 ) { iaxci_usermsg(IAXC_STATUS, "Received 0-size packet. Unable to decode."); return; } call = &calls[callNo]; if ( callNo != selected_call ) { /* drop video for unselected call? */ return; } if ( call->vformat ) { if ( video_recv_video(call, selected_call, e->data, e->datalen, e->ts, call->vformat) < 0 ) { iaxci_usermsg(IAXC_STATUS, "Bad or incomplete video packet. Unable to decode."); return; } } }
static THREADFUNCDECL(main_proc_thread_func) { const int sleep_ms = 5; const int counts_per_second = 1000 / sleep_ms; static int refresh_registration_count = 0; static int audio_error_count = 0; static int audio_error_state = 0; THREADFUNCRET(ret); /* Increase Priority */ iaxci_prioboostbegin(); while ( !main_proc_thread_flag ) { get_iaxc_lock(); service_network(); if ( !test_mode && (!audio_error_state || audio_error_count++ % counts_per_second == 0) ) { /* There are cases when service audio fails such * as when there is no audio devices present in * the system. In these cases, only call * service_audio() once per second until it * succeeds. */ if ( (audio_error_state = service_audio()) ) { iaxci_usermsg(IAXC_NOTICE, "failed to service audio"); if ( audio_error_count / counts_per_second == 5 ) iaxci_usermsg(IAXC_TEXT_TYPE_FATALERROR, "cannot open audio device" " after several tries"); } } // Check registration refresh once a second if ( refresh_registration_count++ > counts_per_second ) { iaxc_refresh_registrations(); refresh_registration_count = 0; } put_iaxc_lock(); iaxc_millisleep(sleep_ms); } /* Decrease priority */ iaxci_prioboostend(); main_proc_thread_flag = -1; return ret; }
static void service_network() { struct iax_event *e = 0; int callNo; struct iaxc_registration *reg; while ( (e = iax_get_event(0)) ) { #ifdef WIN32 iaxc_millisleep(0); //fd: #endif // first, see if this is an event for one of our calls. callNo = iaxc_find_call_by_session(e->session); if ( e->etype == IAX_EVENT_NULL ) { // Should we do something here? // Right now we do nothing, just go with the flow // and let the event be deallocated. } else if ( callNo >= 0 ) { iaxc_handle_network_event(e, callNo); } else if ( (reg = iaxc_find_registration_by_session(e->session)) != NULL ) { iaxc_handle_regreply(e,reg); } else if ( e->etype == IAX_EVENT_REGACK || e->etype == IAX_EVENT_REGREJ ) { iaxci_usermsg(IAXC_ERROR, "Unexpected registration reply"); } else if ( e->etype == IAX_EVENT_REGREQ ) { iaxci_usermsg(IAXC_ERROR, "Registration requested by someone, but we don't understand!"); } else if ( e->etype == IAX_EVENT_CONNECT ) { iaxc_handle_connect(e); } else if ( e->etype == IAX_EVENT_TIMEOUT ) { iaxci_usermsg(IAXC_STATUS, "Timeout for a non-existant session. Dropping", e->etype); } else { iaxci_usermsg(IAXC_STATUS, "Event (type %d) for a non-existant session. Dropping", e->etype); } iax_event_free(e); } }
void iaxci_do_audio_callback(int callNo, unsigned int ts, int source, int encoded, int format, int size, unsigned char *data) { iaxc_event e; e.type = IAXC_EVENT_AUDIO; e.ev.audio.ts = ts; e.ev.audio.encoded = encoded; assert(source == IAXC_SOURCE_REMOTE || source == IAXC_SOURCE_LOCAL); e.ev.audio.source = source; e.ev.audio.size = size; e.ev.audio.callNo = callNo; e.ev.audio.format = format; e.ev.audio.data = (unsigned char *)malloc(size); if ( !e.ev.audio.data ) { iaxci_usermsg(IAXC_ERROR, "failed to allocate memory for audio event"); return; } memcpy(e.ev.audio.data, data, size); iaxci_post_event(e); }
static void iaxc_refresh_registrations() { struct iaxc_registration *cur; struct timeval now; now = iax_tvnow(); for ( cur = registrations; cur != NULL; cur = cur->next ) { // If there is less than three seconds before the registration is about // to expire, renew it. if ( iaxci_usecdiff(&now, &cur->last) > (cur->refresh - 3) * 1000 *1000 ) { if ( cur->session != NULL ) { iax_destroy( cur->session ); } cur->session = iax_session_new(); if ( !cur->session ) { iaxci_usermsg(IAXC_ERROR, "Can't make new registration session"); return; } iax_register(cur->session, cur->host, cur->user, cur->pass, cur->refresh); cur->last = now; } } }
static void iaxc_dump_one_call(int callNo) { if ( callNo < 0 ) return; if ( calls[callNo].state == IAXC_CALL_STATE_FREE ) return; iax_hangup(calls[callNo].session,"Dumped Call"); iaxci_usermsg(IAXC_STATUS, "Hanging up call %d", callNo); iaxc_clear_call(callNo); }
static void jb_warnf(const char *fmt, ...) { va_list args; char buf[1024]; va_start(args, fmt); vsnprintf(buf, 1024, fmt, args); va_end(args); iaxci_usermsg(IAXC_NOTICE, buf); }
EXPORT int iaxc_register_ex(const char * user, const char * pass, const char * host, int refresh) { struct iaxc_registration *newreg; newreg = (struct iaxc_registration *)malloc(sizeof (struct iaxc_registration)); if ( !newreg ) { iaxci_usermsg(IAXC_ERROR, "Can't make new registration"); return -1; } get_iaxc_lock(); newreg->session = iax_session_new(); if ( !newreg->session ) { iaxci_usermsg(IAXC_ERROR, "Can't make new registration session"); put_iaxc_lock(); return -1; } newreg->last = iax_tvnow(); newreg->refresh = refresh; strncpy(newreg->host, host, 256); strncpy(newreg->user, user, 256); strncpy(newreg->pass, pass, 256); /* send out the initial registration with refresh seconds */ iax_register(newreg->session, host, user, pass, refresh); /* add it to the list; */ newreg->id = ++next_registration_id; newreg->next = registrations; registrations = newreg; put_iaxc_lock(); return newreg->id; }
/* XXX Locking?? Start/stop audio?? */ EXPORT int iaxc_select_call(int callNo) { // continue if already selected? //if ( callNo == selected_call ) return; if ( callNo >= max_calls ) { iaxci_usermsg(IAXC_ERROR, "Error: tried to select out_of_range call %d", callNo); return -1; } // callNo < 0 means no call selected (i.e. all on hold) if ( callNo < 0 ) { if ( selected_call >= 0 ) { calls[selected_call].state &= ~IAXC_CALL_STATE_SELECTED; } selected_call = callNo; return 0; } // de-select and notify the old call if not also the new call if ( callNo != selected_call ) { if ( selected_call >= 0 ) { calls[selected_call].state &= ~IAXC_CALL_STATE_SELECTED; iaxci_do_state_callback(selected_call); } selected_call = callNo; calls[selected_call].state |= IAXC_CALL_STATE_SELECTED; } // if it's an incoming call, and ringing, answer it. if ( !(calls[selected_call].state & IAXC_CALL_STATE_OUTGOING) && (calls[selected_call].state & IAXC_CALL_STATE_RINGING) ) { iaxc_answer_call(selected_call); } else { // otherwise just update state (answer does this for us) iaxci_do_state_callback(selected_call); } return 0; }
static void iaxc_handle_connect(struct iax_event * e) { #ifdef USE_VIDEO int video_format_capability; int video_format_preferred; #endif int video_format = 0; int format = 0; int callno; callno = iaxc_first_free_call(); if ( callno < 0 ) { iaxci_usermsg(IAXC_STATUS, "%i \n Incoming Call, but no appearances", callno); // XXX Reject this call!, or just ignore? //iax_reject(e->session, "Too many calls, we're busy!"); iax_accept(e->session, audio_format_preferred & e->ies.capability); iax_busy(e->session); return; } /* negotiate codec */ /* first, try _their_ preferred format */ format = audio_format_capability & e->ies.format; if ( !format ) { /* then, try our preferred format */ format = audio_format_preferred & e->ies.capability; } if ( !format ) { /* finally, see if we have one in common */ format = audio_format_capability & e->ies.capability; /* now choose amongst these, if we got one */ if ( format ) { format = iaxc_choose_codec(format); } } if ( !format ) { iax_reject(e->session, "Could not negotiate common codec"); return; } #ifdef USE_VIDEO iaxc_video_format_get_cap(&video_format_preferred, &video_format_capability); /* first, see if they even want video */ video_format = (e->ies.format & IAXC_VIDEO_FORMAT_MASK); if ( video_format ) { /* next, try _their_ preferred format */ video_format &= video_format_capability; if ( !video_format ) { /* then, try our preferred format */ video_format = video_format_preferred & (e->ies.capability & IAXC_VIDEO_FORMAT_MASK); } if ( !video_format ) { /* finally, see if we have one in common */ video_format = video_format_capability & (e->ies.capability & IAXC_VIDEO_FORMAT_MASK); /* now choose amongst these, if we got one */ if ( video_format ) { video_format = iaxc_choose_codec(video_format); } } /* All video negotiations failed, then warn */ if ( !video_format ) { iaxci_usermsg(IAXC_NOTICE, "Notice: could not negotiate common video codec"); iaxci_usermsg(IAXC_NOTICE, "Notice: switching to audio-only call"); } } #endif /* USE_VIDEO */ calls[callno].vformat = video_format; calls[callno].format = format; if ( e->ies.called_number ) strncpy(calls[callno].local, e->ies.called_number, IAXC_EVENT_BUFSIZ); else strncpy(calls[callno].local, "unknown", IAXC_EVENT_BUFSIZ); if ( e->ies.called_context ) strncpy(calls[callno].local_context, e->ies.called_context, IAXC_EVENT_BUFSIZ); else strncpy(calls[callno].local_context, "", IAXC_EVENT_BUFSIZ); if ( e->ies.calling_number ) strncpy(calls[callno].remote, e->ies.calling_number, IAXC_EVENT_BUFSIZ); else strncpy(calls[callno].remote, "unknown", IAXC_EVENT_BUFSIZ); if ( e->ies.calling_name ) strncpy(calls[callno].remote_name, e->ies.calling_name, IAXC_EVENT_BUFSIZ); else strncpy(calls[callno].remote_name, "unknown", IAXC_EVENT_BUFSIZ); iaxc_note_activity(callno); iaxci_usermsg(IAXC_STATUS, "Call from (%s)", calls[callno].remote); codec_destroy( callno ); calls[callno].session = e->session; calls[callno].state = IAXC_CALL_STATE_ACTIVE|IAXC_CALL_STATE_RINGING; iax_accept(calls[callno].session, format | video_format); iax_ring_announce(calls[callno].session); iaxci_do_state_callback(callno); iaxci_usermsg(IAXC_STATUS, "Incoming call on line %d", callno); }
EXPORT int iaxc_initialize(int num_calls) { int i; int port; os_init(); setup_jb_output(); MUTEXINIT(&iaxc_lock); MUTEXINIT(&event_queue_lock); iaxc_set_audio_prefs(0); if ( iaxc_recvfrom != (iaxc_recvfrom_t)recvfrom ) iax_set_networking(iaxc_sendto, iaxc_recvfrom); /* Note that iax_init() only sets up the receive port when the * sendto/recvfrom functions have not been replaced. We need * to call iaxc_init in either case because there is other * initialization beyond the socket setup that needs to be done. */ if ( (port = iax_init(source_udp_port)) < 0 ) { iaxci_usermsg(IAXC_ERROR, "Fatal error: failed to initialize iax with port %d", port); return -1; } if ( iaxc_recvfrom == (iaxc_recvfrom_t)recvfrom ) iaxci_bound_port = port; else iaxci_bound_port = -1; /* tweak the jitterbuffer settings */ iax_set_jb_target_extra( jb_target_extra ); max_calls = num_calls; /* initialize calls */ if ( max_calls <= 0 ) max_calls = 1; /* 0 == Default? */ /* calloc zeroes for us */ calls = (struct iaxc_call *)calloc(sizeof(struct iaxc_call), max_calls); if ( !calls ) { iaxci_usermsg(IAXC_ERROR, "Fatal error: can't allocate memory"); return -1; } selected_call = -1; for ( i = 0; i < max_calls; i++ ) { strncpy(calls[i].callerid_name, DEFAULT_CALLERID_NAME, IAXC_EVENT_BUFSIZ); strncpy(calls[i].callerid_number, DEFAULT_CALLERID_NUMBER, IAXC_EVENT_BUFSIZ); } if ( !test_mode ) { #ifndef AUDIO_ALSA if ( pa_initialize(&audio_driver, 8000) ) { iaxci_usermsg(IAXC_ERROR, "failed pa_initialize"); return -1; } #else /* TODO: It is unknown whether this stuff for direct access to * alsa should be left in iaxclient. We're leaving it in here for * the time being, but unless it becomes clear that someone cares * about having it, it will be removed. Also note that portaudio * is capable of using alsa. This is another reason why this * direct alsa access may be unneeded. */ if ( alsa_initialize(&audio_driver, 8000) ) return -1; #endif } #ifdef USE_VIDEO if ( video_initialize() ) iaxci_usermsg(IAXC_ERROR, "iaxc_initialize: cannot initialize video!\n"); #endif /* Default audio format capabilities */ audio_format_capability = IAXC_FORMAT_ULAW | IAXC_FORMAT_ALAW | #ifdef CODEC_GSM IAXC_FORMAT_GSM | #endif IAXC_FORMAT_SPEEX; audio_format_preferred = IAXC_FORMAT_SPEEX; return 0; }
EXPORT int iaxc_initialize(int num_calls) { printf("ESTOY IAXC 0\n"); int i; int port; os_init(); setup_jb_output(); MUTEXINIT(&iaxc_lock); MUTEXINIT(&event_queue_lock); iaxc_set_audio_prefs(0); if ( iaxc_recvfrom != (iaxc_recvfrom_t)recvfrom ) iax_set_networking(iaxc_sendto, iaxc_recvfrom); /* Note that iax_init() only sets up the receive port when the * sendto/recvfrom functions have not been replaced. We need * to call iaxc_init in either case because there is other * initialization beyond the socket setup that needs to be done. */ if ( (port = iax_init(source_udp_port)) < 0 ) { iaxci_usermsg(IAXC_ERROR, "Fatal error: failed to initialize iax with port %d", port); return -1; } if ( iaxc_recvfrom == (iaxc_recvfrom_t)recvfrom ) iaxci_bound_port = port; else iaxci_bound_port = -1; /* tweak the jitterbuffer settings */ iax_set_jb_target_extra( jb_target_extra ); max_calls = num_calls; /* initialize calls */ if ( max_calls <= 0 ) max_calls = 1; /* 0 == Default? */ /* calloc zeroes for us */ calls = (struct iaxc_call *)calloc(sizeof(struct iaxc_call), max_calls); if ( !calls ) { iaxci_usermsg(IAXC_ERROR, "Fatal error: can't allocate memory"); return -1; } selected_call = -1; for ( i = 0; i < max_calls; i++ ) { strncpy(calls[i].callerid_name, DEFAULT_CALLERID_NAME, IAXC_EVENT_BUFSIZ); strncpy(calls[i].callerid_number, DEFAULT_CALLERID_NUMBER, IAXC_EVENT_BUFSIZ); } printf("ESTOY IAXC\n"); if ( alsa_initialize(&audio_driver, 8000) ) { iaxci_usermsg(IAXC_ERROR, "failed alsa_initialize"); return -1; } #ifdef USE_VIDEO if ( video_initialize() ) iaxci_usermsg(IAXC_ERROR, "iaxc_initialize: cannot initialize video!\n"); #endif /* Default audio format capabilities */ audio_format_capability = IAXC_FORMAT_ULAW | IAXC_FORMAT_ALAW | #ifdef CODEC_GSM IAXC_FORMAT_GSM | #endif IAXC_FORMAT_SPEEX; audio_format_preferred = IAXC_FORMAT_SPEEX; return 0; }
// Post Events back to clients void iaxci_post_event(iaxc_event e) { if ( e.type == 0 ) { iaxci_usermsg(IAXC_ERROR, "Error: something posted to us an invalid event"); return; } if ( MUTEXTRYLOCK(&iaxc_lock) ) { iaxc_event **tail; /* We could not obtain the lock. Queue the event. */ MUTEXLOCK(&event_queue_lock); tail = &event_queue; e.next = NULL; while ( *tail ) tail = &(*tail)->next; *tail = (iaxc_event *)malloc(sizeof(iaxc_event)); memcpy(*tail, &e, sizeof(iaxc_event)); MUTEXUNLOCK(&event_queue_lock); return; } /* TODO: This is not the best. Since we were able to get the * lock, we decide that it is okay to go ahead and do the * callback to the application. This is really nasty because * it gives the appearance of serialized callbacks, but in * reality, we could callback an application multiple times * simultaneously. So, as things stand, an application must * do some locking in their callback function to make it * reentrant. Barf. More ideally, iaxclient would guarantee * serialized callbacks to the application. */ MUTEXUNLOCK(&iaxc_lock); if ( iaxc_event_callback ) { int rv; rv = iaxc_event_callback(e); if ( e.type == IAXC_EVENT_VIDEO ) { /* We can free the frame data once it is off the * event queue and has been processed by the client. */ free(e.ev.video.data); } else if ( e.type == IAXC_EVENT_AUDIO ) { free(e.ev.audio.data); } if ( rv < 0 ) default_message_callback( "IAXCLIENT: BIG PROBLEM, event callback returned failure!"); // > 0 means processed if ( rv > 0 ) return; // else, fall through to "defaults" } switch ( e.type ) { case IAXC_EVENT_TEXT: default_message_callback(e.ev.text.message); // others we just ignore too return; } }
static void handle_audio_event(struct iax_event *e, int callNo) { int total_consumed = 0; short fr[4096]; const int fr_samples = sizeof(fr) / sizeof(short); int samples, format; #ifdef WIN32 int cycles_max = 100; //fd: #endif struct iaxc_call *call; if ( callNo < 0 ) return; call = &calls[callNo]; if ( callNo != selected_call ) { /* drop audio for unselected call? */ return; } if ( audio_prefs & IAXC_AUDIO_PREF_RECV_DISABLE) { /* just drop silently incoming audio frames */ return; } samples = fr_samples; format = call->format & IAXC_AUDIO_FORMAT_MASK; do { int bytes_decoded; int mainbuf_delta = fr_samples - samples; bytes_decoded = audio_decode_audio(call, fr, e->data + total_consumed, e->datalen - total_consumed, format, &samples); if ( bytes_decoded < 0 ) { iaxci_usermsg(IAXC_STATUS, "Bad or incomplete voice packet. Unable to decode. dropping"); return; } /* Pass encoded audio back to the app if required */ if ( audio_prefs & IAXC_AUDIO_PREF_RECV_REMOTE_ENCODED ) iaxci_do_audio_callback(callNo, e->ts, IAXC_SOURCE_REMOTE, 1, format & IAXC_AUDIO_FORMAT_MASK, e->datalen - total_consumed, e->data + total_consumed); #ifdef WIN32 //fd: start: for some reason it loops here. Try to avoid it cycles_max--; if ( cycles_max < 0 ) { iaxc_millisleep(0); } //fd: end #endif total_consumed += bytes_decoded; if ( audio_prefs & IAXC_AUDIO_PREF_RECV_REMOTE_RAW ) { // audio_decode_audio returns the number of samples. // We are using 16 bit samples, so we need to double // the number to obtain the size in bytes. // format will also be 0 since this is raw audio int size = (fr_samples - samples - mainbuf_delta) * 2; iaxci_do_audio_callback(callNo, e->ts, IAXC_SOURCE_REMOTE, 0, 0, size, (unsigned char *)fr); } if ( iaxci_audio_output_mode ) continue; if (!test_mode) audio_driver.output(&audio_driver, fr, fr_samples - samples - mainbuf_delta); } while ( total_consumed < e->datalen ); }
static void iaxc_handle_network_event(struct iax_event *e, int callNo) { if ( callNo < 0 ) return; iaxc_note_activity(callNo); switch ( e->etype ) { case IAX_EVENT_NULL: break; case IAX_EVENT_HANGUP: iaxci_usermsg(IAXC_STATUS, "Call disconnected by remote"); // XXX does the session go away now? iaxc_clear_call(callNo); break; case IAX_EVENT_REJECT: iaxci_usermsg(IAXC_STATUS, "Call rejected by remote"); iaxc_clear_call(callNo); break; case IAX_EVENT_ACCEPT: calls[callNo].format = e->ies.format & IAXC_AUDIO_FORMAT_MASK; calls[callNo].vformat = e->ies.format & IAXC_VIDEO_FORMAT_MASK; if ( !(e->ies.format & IAXC_VIDEO_FORMAT_MASK) ) { iaxci_usermsg(IAXC_NOTICE, "Failed video codec negotiation."); } iaxci_usermsg(IAXC_STATUS,"Call %d accepted", callNo); break; case IAX_EVENT_ANSWER: calls[callNo].state &= ~IAXC_CALL_STATE_RINGING; calls[callNo].state |= IAXC_CALL_STATE_COMPLETE; iaxci_do_state_callback(callNo); iaxci_usermsg(IAXC_STATUS,"Call %d answered", callNo); //iaxc_answer_call(callNo); // notify the user? break; case IAX_EVENT_BUSY: calls[callNo].state &= ~IAXC_CALL_STATE_RINGING; calls[callNo].state |= IAXC_CALL_STATE_BUSY; iaxci_do_state_callback(callNo); iaxci_usermsg(IAXC_STATUS, "Call %d busy", callNo); break; case IAX_EVENT_VOICE: handle_audio_event(e, callNo); if ( (calls[callNo].state & IAXC_CALL_STATE_OUTGOING) && (calls[callNo].state & IAXC_CALL_STATE_RINGING) ) { calls[callNo].state &= ~IAXC_CALL_STATE_RINGING; calls[callNo].state |= IAXC_CALL_STATE_COMPLETE; iaxci_do_state_callback(callNo); iaxci_usermsg(IAXC_STATUS,"Call %d progress", callNo); } break; #ifdef USE_VIDEO case IAX_EVENT_VIDEO: handle_video_event(e, callNo); break; #endif case IAX_EVENT_TEXT: handle_text_event(e, callNo); break; case IAX_EVENT_RINGA: calls[callNo].state |= IAXC_CALL_STATE_RINGING; iaxci_do_state_callback(callNo); iaxci_usermsg(IAXC_STATUS,"Call %d ringing", callNo); break; case IAX_EVENT_PONG: generate_netstat_event(callNo); break; case IAX_EVENT_URL: handle_url_event(e, callNo); break; case IAX_EVENT_CNG: /* ignore? */ break; case IAX_EVENT_TIMEOUT: iax_hangup(e->session, "Call timed out"); iaxci_usermsg(IAXC_STATUS, "Call %d timed out.", callNo); iaxc_clear_call(callNo); break; case IAX_EVENT_TRANSFER: calls[callNo].state |= IAXC_CALL_STATE_TRANSFER; iaxci_do_state_callback(callNo); iaxci_usermsg(IAXC_STATUS,"Call %d transfer released", callNo); break; case IAX_EVENT_DTMF: iaxci_do_dtmf_callback(callNo,e->subclass); iaxci_usermsg(IAXC_STATUS, "DTMF digit %c received", e->subclass); break; default: iaxci_usermsg(IAXC_STATUS, "Unknown event: %d for call %d", e->etype, callNo); break; } }
static int service_audio() { /* TODO: maybe we shouldn't allocate 8kB on the stack here. */ short buf [4096]; int want_send_audio = selected_call >= 0 && ((calls[selected_call].state & IAXC_CALL_STATE_OUTGOING) || (calls[selected_call].state & IAXC_CALL_STATE_COMPLETE)); //&& !(audio_prefs & IAXC_AUDIO_PREF_SEND_DISABLE); int want_local_audio = (audio_prefs & IAXC_AUDIO_PREF_RECV_LOCAL_RAW) || (audio_prefs & IAXC_AUDIO_PREF_RECV_LOCAL_ENCODED); //fprintf(stdout, "service_audio() => want_send_audio=%d/want_local_audio=%d\n", want_send_audio, want_local_audio); if ( want_local_audio || want_send_audio ) { for ( ;; ) { int to_read; int cmin; audio_driver.start(&audio_driver); /* use codec minimum if higher */ cmin = want_send_audio && calls[selected_call].encoder ? calls[selected_call].encoder->minimum_frame_size : 1; to_read = cmin > minimum_outgoing_framesize ? cmin : minimum_outgoing_framesize; /* Round up to the next multiple */ if ( to_read % cmin ) to_read += cmin - (to_read % cmin); if ( to_read > (int)(sizeof(buf) / sizeof(short)) ) { fprintf(stderr, "internal error: to_read > sizeof(buf)\n"); exit(1); } /* Currently pa gives us either all the bits we ask for or none */ if ( audio_driver.input(&audio_driver, buf, &to_read) ) { iaxci_usermsg(IAXC_ERROR, "ERROR reading audio\n"); break; } /* Frame was not available */ if ( !to_read ) break; if ( audio_prefs & IAXC_AUDIO_PREF_RECV_LOCAL_RAW ) iaxci_do_audio_callback(selected_call, 0, IAXC_SOURCE_LOCAL, 0, 0, to_read * 2, (unsigned char *)buf); if (( want_send_audio ) && (!(audio_prefs & IAXC_AUDIO_PREF_SEND_DISABLE))) audio_send_encoded_audio(&calls[selected_call], selected_call, buf, calls[selected_call].format & IAXC_AUDIO_FORMAT_MASK, to_read); } } else { static int i = 0; audio_driver.stop(&audio_driver); /*! \deprecated Q: Why do we continuously send IAXC_EVENT_LEVELS events when there is no selected call? A: So that certain users of iaxclient do not have to reset their vu meters when a call ends -- they can just count on getting level callbacks. This is a bit of a hack so any applications relying on this behavior should maybe be changed. */ if ( i++ % 50 == 0 ) iaxci_do_levels_callback(AUDIO_ENCODE_SILENCE_DB, AUDIO_ENCODE_SILENCE_DB); } return 0; }
EXPORT int iaxc_call_ex(const char *num, const char* callerid_name, const char* callerid_number, int video) { int video_format_capability = 0; int video_format_preferred = 0; int callNo = -1; struct iax_session *newsession; char *ext = strstr(num, "/"); get_iaxc_lock(); // if no call is selected, get a new appearance if ( selected_call < 0 ) { callNo = iaxc_first_free_call(); } else { // use selected call if not active, otherwise, get a new appearance if ( calls[selected_call].state & IAXC_CALL_STATE_ACTIVE ) { callNo = iaxc_first_free_call(); } else { callNo = selected_call; } } if ( callNo < 0 ) { iaxci_usermsg(IAXC_STATUS, "No free call appearances"); goto iaxc_call_bail; } newsession = iax_session_new(); if ( !newsession ) { iaxci_usermsg(IAXC_ERROR, "Can't make new session"); goto iaxc_call_bail; } calls[callNo].session = newsession; codec_destroy( callNo ); if ( ext ) { strncpy(calls[callNo].remote_name, num, IAXC_EVENT_BUFSIZ); strncpy(calls[callNo].remote, ++ext, IAXC_EVENT_BUFSIZ); } else { strncpy(calls[callNo].remote_name, num, IAXC_EVENT_BUFSIZ); strncpy(calls[callNo].remote, "" , IAXC_EVENT_BUFSIZ); } if ( callerid_number != NULL ) strncpy(calls[callNo].callerid_number, callerid_number, IAXC_EVENT_BUFSIZ); if ( callerid_name != NULL ) strncpy(calls[callNo].callerid_name, callerid_name, IAXC_EVENT_BUFSIZ); strncpy(calls[callNo].local , calls[callNo].callerid_name, IAXC_EVENT_BUFSIZ); strncpy(calls[callNo].local_context, "default", IAXC_EVENT_BUFSIZ); calls[callNo].state = IAXC_CALL_STATE_ACTIVE | IAXC_CALL_STATE_OUTGOING; /* reset activity and ping "timers" */ iaxc_note_activity(callNo); calls[callNo].last_ping = calls[callNo].last_activity; #ifdef USE_VIDEO if ( video ) iaxc_video_format_get_cap(&video_format_preferred, &video_format_capability); #endif iaxci_usermsg(IAXC_NOTICE, "Originating an %s call", video_format_preferred ? "audio+video" : "audio only"); iax_call(calls[callNo].session, calls[callNo].callerid_number, calls[callNo].callerid_name, num, NULL, 0, audio_format_preferred | video_format_preferred, audio_format_capability | video_format_capability); // does state stuff also iaxc_select_call(callNo); iaxc_call_bail: put_iaxc_lock(); return callNo; }
struct iaxc_video_codec *codec_video_ffmpeg_new(int format, int w, int h, int framerate, int bitrate, int fragsize) { struct encoder_ctx *e; struct decoder_ctx *d; AVCodec *codec; int ff_enc_id, ff_dec_id; char *name; struct iaxc_video_codec *c = calloc(sizeof(struct iaxc_video_codec), 1); if (!c) { fprintf(stderr, "codec_ffmpeg: failed to allocate video context\n"); return NULL; } avcodec_init(); avcodec_register_all(); c->format = format; c->width = w; c->height = h; c->framerate = framerate; c->bitrate = bitrate; /* TODO: Is a fragsize of zero valid? If so, there's a divide * by zero error to contend with. */ c->fragsize = fragsize; c->encode = encode; c->decode = decode_iaxc_slice; c->destroy = destroy; c->encstate = calloc(sizeof(struct encoder_ctx), 1); if (!c->encstate) goto bail; e = c->encstate; e->avctx = avcodec_alloc_context(); if (!e->avctx) goto bail; e->picture = avcodec_alloc_frame(); if (!e->picture) goto bail; /* The idea here is that the encoded frame that will land in this * buffer will be no larger than the size of an uncompressed 32-bit * rgb frame. * * TODO: Is this assumption really valid? */ e->frame_buf_len = w * h * 4; e->frame_buf = malloc(e->frame_buf_len); if (!e->frame_buf) goto bail; c->decstate = calloc(sizeof(struct decoder_ctx), 1); if (!c->decstate) goto bail; d = c->decstate; d->avctx = avcodec_alloc_context(); if (!d->avctx) goto bail; d->picture = avcodec_alloc_frame(); if (!d->picture) goto bail; d->frame_buf_len = e->frame_buf_len; d->frame_buf = malloc(d->frame_buf_len); if (!d->frame_buf) goto bail; e->slice_header.version = 0; srandom(time(0)); e->slice_header.source_id = random() & 0xffff; e->avctx->time_base.num = 1; e->avctx->time_base.den = framerate; e->avctx->width = w; e->avctx->height = h; e->avctx->bit_rate = bitrate; /* This determines how often i-frames are sent */ e->avctx->gop_size = framerate * 3; e->avctx->pix_fmt = PIX_FMT_YUV420P; e->avctx->has_b_frames = 0; e->avctx->mb_qmin = e->avctx->qmin = 10; e->avctx->mb_qmax = e->avctx->qmax = 10; e->avctx->lmin = 2 * FF_QP2LAMBDA; e->avctx->lmax = 10 * FF_QP2LAMBDA; e->avctx->global_quality = FF_QP2LAMBDA * 2; e->avctx->qblur = 0.5; e->avctx->global_quality = 10; e->avctx->flags |= CODEC_FLAG_PSNR; e->avctx->flags |= CODEC_FLAG_QSCALE; e->avctx->mb_decision = FF_MB_DECISION_SIMPLE; ff_enc_id = ff_dec_id = map_iaxc_codec_to_avcodec(format); /* Note, when fragsize is used (non-zero) ffmpeg will use a "best * effort" strategy: the fragment size will be fragsize +/- 20% */ switch (format) { case IAXC_FORMAT_H261: /* TODO: H261 only works with specific resolutions. */ name = "H.261"; break; case IAXC_FORMAT_H263: /* TODO: H263 only works with specific resolutions. */ name = "H.263"; e->avctx->flags |= CODEC_FLAG_AC_PRED; if (fragsize) { c->decode = decode_rtp_slice; e->avctx->rtp_payload_size = fragsize; e->avctx->flags |= CODEC_FLAG_TRUNCATED | CODEC_FLAG2_STRICT_GOP; e->avctx->rtp_callback = encode_rtp_callback; d->avctx->flags |= CODEC_FLAG_TRUNCATED; } break; case IAXC_FORMAT_H263_PLUS: /* Although the encoder is CODEC_ID_H263P, the decoder * is the regular h.263, so we handle this special case * here. */ ff_dec_id = CODEC_ID_H263; name = "H.263+"; e->avctx->flags |= CODEC_FLAG_AC_PRED; if (fragsize) { c->decode = decode_rtp_slice; e->avctx->rtp_payload_size = fragsize; e->avctx->flags |= CODEC_FLAG_TRUNCATED | CODEC_FLAG_H263P_SLICE_STRUCT | CODEC_FLAG2_STRICT_GOP | CODEC_FLAG2_LOCAL_HEADER; e->avctx->rtp_callback = encode_rtp_callback; d->avctx->flags |= CODEC_FLAG_TRUNCATED; } break; case IAXC_FORMAT_MPEG4: name = "MPEG4"; c->decode = decode_rtp_slice; e->avctx->rtp_payload_size = fragsize; e->avctx->rtp_callback = encode_rtp_callback; e->avctx->flags |= CODEC_FLAG_TRUNCATED | CODEC_FLAG_H263P_SLICE_STRUCT | CODEC_FLAG2_STRICT_GOP | CODEC_FLAG2_LOCAL_HEADER; d->avctx->flags |= CODEC_FLAG_TRUNCATED; break; case IAXC_FORMAT_H264: name = "H.264"; /* * Encoder flags */ /* Headers are not repeated */ /* e->avctx->flags |= CODEC_FLAG_GLOBAL_HEADER; */ /* Slower, less blocky */ /* e->avctx->flags |= CODEC_FLAG_LOOP_FILTER; */ e->avctx->flags |= CODEC_FLAG_PASS1; /* e->avctx->flags |= CODEC_FLAG_PASS2; */ /* Compute psnr values at encode-time (avctx->error[]) */ /* e->avctx->flags |= CODEC_FLAG_PSNR; */ /* e->avctx->flags2 |= CODEC_FLAG2_8X8DCT; */ /* Access Unit Delimiters */ e->avctx->flags2 |= CODEC_FLAG2_AUD; /* Allow b-frames to be used as reference */ /* e->avctx->flags2 |= CODEC_FLAG2_BPYRAMID; */ /* b-frame rate distortion optimization */ /* e->avctx->flags2 |= CODEC_FLAG2_BRDO; */ /* e->avctx->flags2 |= CODEC_FLAG2_FASTPSKIP; */ /* Multiple references per partition */ /* e->avctx->flags2 |= CODEC_FLAG2_MIXED_REFS; */ /* Weighted biprediction for b-frames */ /* e->avctx->flags2 |= CODEC_FLAG2_WPRED; */ /* * Decoder flags */ /* Do not draw edges */ /* d->avctx->flags |= CODEC_FLAG_EMU_EDGE; */ /* Decode grayscale only */ /* d->avctx->flags |= CODEC_FLAG_GRAY; */ /* d->avctx->flags |= CODEC_FLAG_LOW_DELAY; */ /* Allow input bitstream to be randomly truncated */ /* d->avctx->flags |= CODEC_FLAG_TRUNCATED; */ /* Allow out-of-spec speed tricks */ /* d->avctx->flags2 |= CODEC_FLAG2_FAST; */ break; case IAXC_FORMAT_THEORA: /* TODO: ffmpeg only has a theora decoder. Until it has * an encoder also, we cannot use ffmpeg for theora. */ name = "Theora"; break; default: fprintf(stderr, "codec_ffmpeg: unsupported format (0x%08x)\n", format); goto bail; } strcpy(c->name, "ffmpeg-"); strncat(c->name, name, sizeof(c->name)); /* Get the codecs */ codec = avcodec_find_encoder(ff_enc_id); if (!codec) { iaxci_usermsg(IAXC_TEXT_TYPE_ERROR, "codec_ffmpeg: cannot find encoder %d\n", ff_enc_id); goto bail; } if (avcodec_open(e->avctx, codec)) { iaxci_usermsg(IAXC_TEXT_TYPE_ERROR, "codec_ffmpeg: cannot open encoder %s\n", name); goto bail; } codec = avcodec_find_decoder(ff_dec_id); if (!codec) { iaxci_usermsg(IAXC_TEXT_TYPE_ERROR, "codec_ffmpeg: cannot find decoder %d\n", ff_dec_id); goto bail; } if (avcodec_open(d->avctx, codec)) { iaxci_usermsg(IAXC_TEXT_TYPE_ERROR, "codec_ffmpeg: cannot open decoder %s\n", name); goto bail; } { enum PixelFormat fmts[] = { PIX_FMT_YUV420P, -1 }; if (d->avctx->get_format(d->avctx, fmts) != PIX_FMT_YUV420P) { iaxci_usermsg(IAXC_TEXT_TYPE_ERROR, "codec_ffmpeg: cannot set decode format to YUV420P\n"); goto bail; } } return c; bail: destroy(c); return 0; }
EXPORT int iaxc_call_ex(const char *num, const char* callerid_name, const char* callerid_number, int video) { int video_format_capability = 0; int video_format_preferred = 0; int callNo = -1; struct iax_session *newsession; char *ext = strstr(num, "/"); get_iaxc_lock(); // if no call is selected, get a new appearance if ( selected_call < 0 ) { callNo = iaxc_first_free_call(); } else { // use selected call if not active, otherwise, get a new appearance if ( calls[selected_call].state & IAXC_CALL_STATE_ACTIVE ) { callNo = iaxc_first_free_call(); } else { callNo = selected_call; } } if ( callNo < 0 ) { iaxci_usermsg(IAXC_STATUS, "No free call appearances"); goto iaxc_call_bail; } newsession = iax_session_new(); if ( !newsession ) { iaxci_usermsg(IAXC_ERROR, "Can't make new session"); goto iaxc_call_bail; } calls[callNo].session = newsession; codec_destroy( callNo ); /* When the ACCEPT comes back from the other-end, these formats * are set. Whether the format is set or not determines whether * we are in the Linked state (see the iax2 rfc). * These will have already been cleared by iaxc_clear_call(), * but we reset them anyway just to be pedantic. */ calls[callNo].format = 0; calls[callNo].vformat = 0; if ( ext ) { strncpy(calls[callNo].remote_name, num, IAXC_EVENT_BUFSIZ); strncpy(calls[callNo].remote, ++ext, IAXC_EVENT_BUFSIZ); } else { strncpy(calls[callNo].remote_name, num, IAXC_EVENT_BUFSIZ); strncpy(calls[callNo].remote, "" , IAXC_EVENT_BUFSIZ); } if ( callerid_number != NULL ) strncpy(calls[callNo].callerid_number, callerid_number, IAXC_EVENT_BUFSIZ); if ( callerid_name != NULL ) strncpy(calls[callNo].callerid_name, callerid_name, IAXC_EVENT_BUFSIZ); strncpy(calls[callNo].local , calls[callNo].callerid_name, IAXC_EVENT_BUFSIZ); strncpy(calls[callNo].local_context, "default", IAXC_EVENT_BUFSIZ); calls[callNo].state = IAXC_CALL_STATE_OUTGOING; /* reset activity and ping "timers" */ iaxc_note_activity(callNo); calls[callNo].last_ping = calls[callNo].last_activity; #ifdef USE_VIDEO if ( video ) iaxc_video_format_get_cap(&video_format_preferred, &video_format_capability); #endif iaxci_usermsg(IAXC_NOTICE, "Originating an %s call", video_format_preferred ? "audio+video" : "audio only"); iax_call(calls[callNo].session, calls[callNo].callerid_number, calls[callNo].callerid_name, num, NULL, 0, audio_format_preferred | video_format_preferred, audio_format_capability | video_format_capability); // does state stuff also iaxc_select_call(callNo); iaxc_call_bail: put_iaxc_lock(); return callNo; }