An error occurred while loading the file. Please try again.
-
Stefan Holmer authored
The error-concealer is plugged in after any motion vectors have been decoded. It tries to estimate any missing motion vectors from the motion vectors of the previous frame. Intra blocks with missing residual are replaced with inter blocks with estimated motion vectors. This feature was developed in a separate sandbox (sandbox/holmer/error-concealment). Change-Id: I5c8917b031078d79dbafd90f6006680e84a23412
d04f8523
/*
linphone
Copyright (C) 2000 Simon MORLAT (simon.morlat@free.fr)
This program 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
of the License, or (at your option) any later version.
This program 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 this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "exevents.h"
#include "linphonecore.h"
#include "private.h"
#include "mediastreamer2/mediastream.h"
#include <eXosip2/eXosip.h>
#include <osipparser2/osip_message.h>
#include <osipparser2/osip_parser.h>
static int linphone_answer_sdp(LinphoneCore *lc, eXosip_event_t *ev, sdp_message_t *sdp);
static bool_t linphone_call_matches_event(LinphoneCall *call, eXosip_event_t *ev){
return call->cid==ev->cid;
}
static void linphone_call_proceeding(LinphoneCore *lc, eXosip_event_t *ev){
if (lc->call==NULL || (lc->call->cid!=-1 && !linphone_call_matches_event(lc->call,ev)) ) {
ms_warning("This call has been canceled: call=%p, call->cid=%i, ev->cid=%i",
lc->call,lc->call->cid,ev->cid);
eXosip_lock();
eXosip_call_terminate(ev->cid,ev->did);
eXosip_unlock();
return;
}
lc->call->cid=ev->cid;
lc->call->did=ev->did;
lc->call->tid=ev->tid;
}
static void linphone_connect_incoming(LinphoneCore *lc){
lc->vtable.show(lc);
lc->vtable.display_status(lc,_("Connected."));
lc->call->state=LCStateAVRunning;
if (lc->ringstream!=NULL){
ring_stop(lc->ringstream);
lc->ringstream=NULL;
}
if (lc->audiostream->ticker!=NULL){
/*case where we accepted early media */
linphone_core_stop_media_streams(lc);
linphone_core_init_media_streams(lc);
}
linphone_core_start_media_streams(lc,lc->call);
}
int linphone_call_accepted(LinphoneCore *lc, eXosip_event_t *ev)
{
LinphoneCall *call=lc->call;
sdp_message_t *sdp;
const char *sdpanswer=NULL;
osip_message_t *msg=NULL;
int err;
7172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
if (call==NULL){
ms_warning("No call to accept.");
return 0;
}
linphone_call_proceeding(lc,ev);
if (!linphone_call_matches_event(lc->call,ev)) return 0;
call->auth_pending=FALSE;
if (call->state==LCStateAVRunning){
return 0; /*already accepted*/
}
linphone_call_init_media_params(call);
sdp=eXosip_get_sdp_info(ev->response);
if (!lc->sip_conf.sdp_200_ack){
err=0;
sdp_context_read_answer(call->sdpctx,sdp);
}else{
/*we receive a 200OK with an sdp offer*/
err=linphone_answer_sdp(lc,ev,sdp);
if (err==0) sdpanswer=call->sdpctx->answerstr;
}
if (err==0){
gstate_new_state(lc, GSTATE_CALL_OUT_CONNECTED, NULL);
linphone_connect_incoming(lc);
}
/*send the ack once streams are started*/
eXosip_call_build_ack(ev->did,&msg);
if (sdpanswer!=NULL) linphone_set_sdp(msg,sdpanswer);
eXosip_call_send_ack(ev->did,msg);
if (err!=0){
/*send a bye*/
ms_error("Incompatible SDP offer received in 200Ok, need to abort the call");
linphone_core_terminate_call(lc,NULL);
}
sdp_message_free(sdp);
return 0;
}
int linphone_call_terminated(LinphoneCore *lc, eXosip_event_t *ev)
{
/*stop ringing if necessary*/
if (lc->call!=NULL){
if (lc->call->cid!=ev->cid){
/* this is not current call */
ms_message("call %i terminated, this was not current call.",ev->cid);
return 0;
}
}
ms_message("Current call terminated...");
if (lc->ringstream!=NULL) {
ring_stop(lc->ringstream);
lc->ringstream=NULL;
}
linphone_core_stop_media_streams(lc);
lc->vtable.show(lc);
lc->vtable.display_status(lc,_("Call terminated."));
gstate_new_state(lc, GSTATE_CALL_END, NULL);
if (lc->vtable.bye_recv!=NULL){
char *from;
osip_from_to_str(ev->request->from,&from);
lc->vtable.bye_recv(lc,from);
osip_free(from);
}
if (lc->call!=NULL){
linphone_call_destroy(lc->call);
lc->call=NULL;
}
return 0;
}
141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
int linphone_call_released(LinphoneCore *lc, int cid){
LinphoneCall *call=lc->call;
if (call!=NULL && call->cid==cid){
linphone_call_destroy(lc->call);
lc->call=NULL;
lc->vtable.display_status(lc,_("Could not reach destination."));
gstate_new_state(lc, GSTATE_CALL_ERROR, NULL);
}
return 0;
}
int linphone_call_failure(LinphoneCore *lc, eXosip_event_t *ev)
{
const char *reason="";
char *msg486=_("User is busy.");
char *msg480=_("User is temporarily unavailable.");
char *msg487=_("Request Cancelled.");
/*char *retrymsg=_("%s. Retry after %i minute(s).");*/
char *msg600=_("User does not want to be disturbed.");
char *msg603=_("Call declined.");
char* tmpmsg=msg486;
int code;
LinphoneCall *call=lc->call;
if (call){
/*check that the faillure is related to this call, not an old one*/
if (!linphone_call_matches_event(call,ev)) {
ms_warning("Failure reported for an old call.");
return 0;
}
}
if (ev->response){
code=osip_message_get_status_code(ev->response);
reason=osip_message_get_reason_phrase(ev->response);
}else code=-110;
lc->vtable.show(lc);
switch(code)
{
case 401:
case 407:
if (lc->call!=NULL)
linphone_process_authentication(lc,ev);
return 0;
break;
case 400:
lc->vtable.display_status(lc,_("Bad request"));
break;
case 404:
lc->vtable.display_status(lc,_("User cannot be found at given address."));
break;
case 415:
lc->vtable.display_status(lc,_("Remote user cannot support any of proposed codecs."));
break;
case 422:
/*ignore: eXosip_automatic_action will do the job of retrying with a greater Session-Expires*/
return 0;
break;
case 480:
tmpmsg=msg480;
case 486:
/*
msg_header_getbyname(msg,"retry-after",0,&retry);
if (retry!=NULL)
{
umsg=g_malloc(strlen(tmpmsg)+strlen(retrymsg)+13);
211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
sprintf(umsg,retrymsg,tmpmsg,atoi(retry->hvalue)/60);
lc->vtable.display_message(lc,umsg);
ms_free(umsg);
}*/
lc->vtable.display_message(lc,tmpmsg);
break;
case 487:
lc->vtable.display_status(lc,msg487);
break;
case 600:
lc->vtable.display_message(lc,msg600);
break;
case 603:
lc->vtable.display_status(lc,msg603);
break;
case -110: /* time out, call leg is lost */
lc->vtable.display_status(lc,_("Timeout."));
break;
case -111:
lc->vtable.display_status(lc,_("Remote host was found but refused connection."));
break;
default:
if (code>0)
{
lc->vtable.display_status(lc,reason);
}
else ms_warning("failure_cb unknown code=%i\n",code);
}
if (lc->ringstream!=NULL) {
ring_stop(lc->ringstream);
lc->ringstream=NULL;
}
linphone_core_stop_media_streams(lc);
if (call!=NULL) {
linphone_call_destroy(call);
gstate_new_state(lc, GSTATE_CALL_ERROR, NULL);
lc->call=NULL;
}
return 0;
}
extern sdp_handler_t linphone_sdphandler;
static int linphone_answer_sdp(LinphoneCore *lc, eXosip_event_t *ev, sdp_message_t *sdp){
int status=200;
sdp_context_t *ctx=NULL;
ctx=lc->call->sdpctx;
/* get the result of the negociation */
sdp_context_get_answer(ctx,sdp);
status=sdp_context_get_status(ctx);
if (status==200){
linphone_core_init_media_streams(lc);
return 0;
}else{
if (status==-1) status=415;
}
return -1;
}
int linphone_inc_new_call(LinphoneCore *lc, eXosip_event_t *ev)
{
sdp_message_t *sdp=NULL;
osip_from_t *from_url=ev->request->from;
char *barmesg;
char *from;
char *to;
int err;
281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
osip_from_to_str(ev->request->from,&from);
osip_to_to_str(ev->request->to,&to);
/* first check if we can answer successfully to this invite */
if (lc->presence_mode!=LINPHONE_STATUS_ONLINE){
ms_message("Not present !! presence mode : %d\n",lc->presence_mode);
eXosip_lock();
if (lc->presence_mode==LINPHONE_STATUS_BUSY)
eXosip_call_send_answer(ev->tid,486,NULL);
else if (lc->presence_mode==LINPHONE_STATUS_AWAY
||lc->presence_mode==LINPHONE_STATUS_BERIGHTBACK
||lc->presence_mode==LINPHONE_STATUS_ONTHEPHONE
||lc->presence_mode==LINPHONE_STATUS_OUTTOLUNCH
||lc->presence_mode==LINPHONE_STATUS_OFFLINE)
eXosip_call_send_answer(ev->tid,480,NULL);
else if (lc->presence_mode==LINPHONE_STATUS_NOT_DISTURB)
eXosip_call_send_answer(ev->tid,480,NULL);
else if (lc->alt_contact!=NULL && lc->presence_mode==LINPHONE_STATUS_MOVED)
{
osip_message_t *msg;
eXosip_call_build_answer(ev->tid,302,&msg);
osip_message_set_contact(msg,lc->alt_contact);
eXosip_call_send_answer(ev->tid,302,msg);
}
else if (lc->alt_contact!=NULL && lc->presence_mode==LINPHONE_STATUS_ALT_SERVICE)
{
osip_message_t *msg;
eXosip_call_build_answer(ev->tid,380,&msg);
osip_message_set_contact(msg,lc->alt_contact);
eXosip_call_send_answer(ev->tid,380,msg);
}
else
eXosip_call_send_answer(ev->tid,486,NULL);
eXosip_unlock();
goto end;
}
if (lc->call!=NULL){/*busy*/
eXosip_lock();
eXosip_call_send_answer(ev->tid,486,NULL);
eXosip_unlock();
goto end;
}
lc->call=linphone_call_new_incoming(lc,linphone_address_new(from),linphone_address_new(to),ev);
sdp=eXosip_get_sdp_info(ev->request);
if (sdp==NULL){
ms_message("No sdp body in invite, 200-ack scheme");
err=0;
}else{
err=linphone_answer_sdp(lc,ev,sdp);
}
if (!err){
char *tmp;
if (from_2char_without_params(from_url,&tmp)!=0){
tmp=ms_strdup("Unknown user");
}
gstate_new_state(lc, GSTATE_CALL_IN_INVITE, tmp);
barmesg=ortp_strdup_printf("%s %s",tmp,_("is contacting you."));
lc->vtable.show(lc);
lc->vtable.display_status(lc,barmesg);
/* play the ring */
if (lc->sound_conf.ring_sndcard!=NULL){
ms_message("Starting local ring...");
lc->ringstream=ring_start(lc->sound_conf.local_ring,2000,lc->sound_conf.ring_sndcard);
}
linphone_call_set_state(lc->call,LCStateRinging);
eXosip_lock();
eXosip_call_send_answer(ev->tid,180,NULL);
351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420
eXosip_unlock();
lc->vtable.inv_recv(lc,tmp);
ms_free(barmesg);
osip_free(tmp);
}else{
ms_error("Error during sdp negociation. ");
eXosip_lock();
eXosip_call_send_answer(ev->tid,415,NULL);
eXosip_unlock();
linphone_call_destroy(lc->call);
lc->call=NULL;
}
end:
osip_free(from);
osip_free(to);
if (sdp) sdp_message_free(sdp);
return 0;
}
void linphone_handle_ack(LinphoneCore *lc, eXosip_event_t *ev){
sdp_message_t *sdp=eXosip_get_sdp_info(ev->ack);
if (sdp){
sdp_context_read_answer(lc->call->sdpctx,sdp);
linphone_connect_incoming(lc);
sdp_message_free(sdp);
}
}
void linphone_handle_reinvite(LinphoneCore *lc, eXosip_event_t *ev){
sdp_message_t *sdp=eXosip_get_sdp_info(ev->request);
sdp_context_t *ctx;
LinphoneCall *call=lc->call;
char *answer;
int status;
if (sdp==NULL){
ms_warning("No sdp in reinvite !");
eXosip_lock();
eXosip_call_send_answer(ev->tid,603,NULL);
eXosip_unlock();
return;
}
ctx=call->sdpctx;
/* get the result of the negociation */
linphone_call_init_media_params(call);
answer=sdp_context_get_answer(ctx,sdp);
status=sdp_context_get_status(ctx);
if (status==200){
osip_message_t *msg=NULL;
linphone_core_stop_media_streams(lc);
linphone_core_init_media_streams(lc);
eXosip_lock();
if (eXosip_call_build_answer(ev->tid,200,&msg)<0){
ms_warning("Reinvite for closed call ?");
eXosip_unlock();
linphone_core_stop_media_streams(lc);
sdp_message_free(sdp);
return ;
}
answer=call->sdpctx->answerstr; /* takes the sdp already computed*/
linphone_set_sdp(msg,answer);
eXosip_call_send_answer(ev->tid,200,msg);
eXosip_unlock();
linphone_core_start_media_streams(lc,call);
}else{
eXosip_lock();
eXosip_call_send_answer(ev->tid,status,NULL);
eXosip_unlock();
}
sdp_message_free(sdp);
421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490
}
void linphone_do_automatic_redirect(LinphoneCore *lc, const char *contact){
char *msg=ortp_strdup_printf(_("Redirected to %s..."),contact);
lc->vtable.display_status(lc,msg);
ms_free(msg);
if (lc->call!=NULL) linphone_call_destroy(lc->call);
lc->call=NULL;
linphone_core_invite(lc,contact);
}
void linphone_call_redirected(LinphoneCore *lc, eXosip_event_t *ev){
int code=osip_message_get_status_code(ev->response);
char *contact=NULL;
osip_contact_t *ct;
osip_message_get_contact(ev->response,0,&ct);
if (ct) osip_contact_to_str(ct,&contact);
switch(code){
case 380:
lc->vtable.display_url(lc,_("User is not reachable at the moment but he invites you\nto contact him using the following alternate resource:"),contact);
if (lc->call!=NULL) linphone_call_destroy(lc->call);
lc->call=NULL;
break;
case 302:
linphone_do_automatic_redirect(lc,contact);
break;
}
if (contact) osip_free(contact);
}
/* these are the SdpHandler callbacks: we are called in to be aware of the content
of the SDP messages exchanged */
int linphone_set_audio_offer(sdp_context_t *ctx)
{
LinphoneCall *call=(LinphoneCall*)sdp_context_get_user_pointer(ctx);
LinphoneCore *lc=call->core;
PayloadType *codec;
MSList *elem;
sdp_payload_t payload;
elem=lc->codecs_conf.audio_codecs;
while(elem!=NULL){
codec=(PayloadType*) elem->data;
if (linphone_core_check_payload_type_usability(lc,codec) && payload_type_enabled(codec)){
sdp_payload_init(&payload);
payload.a_rtpmap=ortp_strdup_printf("%s/%i/1",codec->mime_type,codec->clock_rate);
payload.pt=rtp_profile_get_payload_number_from_rtpmap(lc->local_profile,payload.a_rtpmap);
payload.localport=call->audio_params.natd_port > 0 ?
call->audio_params.natd_port : lc->rtp_conf.audio_rtp_port;
if (strcasecmp(codec->mime_type,"iLBC")==0){
/* prefer the 30 ms mode */
payload.a_fmtp="ptime=30";
}
sdp_context_add_audio_payload(ctx,&payload);
ms_free(payload.a_rtpmap);
}
elem=ms_list_next(elem);
}
/* add telephone-event payload*/
sdp_payload_init(&payload);
payload.pt=rtp_profile_get_payload_number_from_mime(lc->local_profile,"telephone-event");
payload.a_rtpmap="telephone-event/8000";
payload.a_fmtp="0-11";
if (lc->dw_audio_bw>0) payload.b_as_bandwidth=lc->dw_audio_bw;
sdp_context_add_audio_payload(ctx,&payload);
return 0;
}
491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560
static int find_payload_type_number(RtpProfile *prof, PayloadType *pt){
int candidate=-1,i;
PayloadType *it;
for(i=0;i<127;++i){
it=rtp_profile_get_payload(prof,i);
if (it!=NULL && strcasecmp(pt->mime_type,it->mime_type)==0
&& (pt->clock_rate==it->clock_rate || pt->clock_rate<=0) ){
if ( (pt->recv_fmtp && it->recv_fmtp && strcasecmp(pt->recv_fmtp,it->recv_fmtp)==0) ||
(pt->recv_fmtp==NULL && it->recv_fmtp==NULL) ){
/*exact match*/
return i;
}else candidate=i;
}
}
if (candidate==-1) ms_fatal("Should not happen.");
return candidate;
}
static int find_payload_type_number_best_match(RtpProfile *prof, const char *rtpmap, const char *fmtp){
int localpt=rtp_profile_get_payload_number_from_rtpmap(prof,rtpmap);
PayloadType *pt;
char value[10];
if (localpt<0) return -1;
pt=rtp_profile_get_payload(prof,localpt);
if (strcasecmp(pt->mime_type,"H264")==0){
/*hack for H264: need to answer with same packetization-mode*/
PayloadType tmp;
memset(&tmp,0,sizeof(tmp));
tmp.mime_type="H264";
tmp.clock_rate=pt->clock_rate;
if (fmtp && fmtp_get_value(fmtp,"packetization-mode",value,sizeof(value))){
tmp.recv_fmtp=(atoi(value)==1) ? "packetization-mode=1" : NULL;
}
localpt=find_payload_type_number(prof,&tmp);
}
return localpt;
}
int linphone_set_video_offer(sdp_context_t *ctx)
{
LinphoneCall *call=(LinphoneCall*)sdp_context_get_user_pointer(ctx);
LinphoneCore *lc=call->core;
PayloadType *codec;
MSList *elem;
bool_t firsttime=TRUE;
if (!linphone_core_video_enabled(lc)) return -1;
for(elem=lc->codecs_conf.video_codecs;elem!=NULL;elem=ms_list_next(elem)){
codec=(PayloadType*) elem->data;
if (linphone_core_check_payload_type_usability(lc,codec) && payload_type_enabled(codec)){
sdp_payload_t payload;
sdp_payload_init(&payload);
payload.line=1;
payload.a_rtpmap=ortp_strdup_printf("%s/%i",codec->mime_type,codec->clock_rate);
payload.localport=call->video_params.natd_port>0 ?
call->video_params.natd_port : lc->rtp_conf.video_rtp_port;
payload.pt=find_payload_type_number(lc->local_profile,codec);
payload.a_fmtp=codec->recv_fmtp;
if(firsttime){
firsttime=FALSE;
if (lc->dw_video_bw>0)
payload.b_as_bandwidth=lc->dw_video_bw;
}
sdp_context_add_video_payload(ctx,&payload);
ms_free(payload.a_rtpmap);
}
}
return 0;
561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630
}
typedef enum {
Unsupported,
Supported,
SupportedAndValid /* valid= the presence of this codec is enough to make a call */
}SupportLevel;
SupportLevel linphone_payload_is_supported(LinphoneCore *lc, sdp_payload_t *payload,RtpProfile *local_profile,RtpProfile *dialog_profile, bool_t answering, PayloadType **local_payload_type)
{
int localpt;
SupportLevel ret;
if (payload->a_rtpmap!=NULL){
localpt=find_payload_type_number_best_match(local_profile,payload->a_rtpmap,payload->a_fmtp);
}else{
localpt=payload->pt;
ms_warning("payload has no rtpmap.");
}
if (localpt>=0 && localpt <128 ){
/* this payload is understood, but does the user want to use it ?? */
PayloadType *rtppayload;
rtppayload=rtp_profile_get_payload(local_profile,localpt);
if (rtppayload==NULL) {
ms_warning("strange error !!");
return Unsupported;
}
*local_payload_type=rtppayload;
if (strcmp(rtppayload->mime_type,"telephone-event")!=0){
if (answering && !linphone_core_check_payload_type_usability(lc,rtppayload) ){
ms_warning("payload %s is not usable",rtppayload->mime_type);
return Unsupported;
}
if ( !payload_type_enabled(rtppayload)) {
ms_warning("payload %s is not enabled.",rtppayload->mime_type);
return Unsupported;
}
ret=SupportedAndValid;
}else ret=Supported;
if (dialog_profile!=NULL){
int dbw,ubw;
/* this payload is supported in our local rtp profile, so add it to the dialog rtp
profile */
rtppayload=payload_type_clone(rtppayload);
if (rtp_profile_get_payload(dialog_profile,payload->pt)!=NULL){
ms_error("Payload %s type already entered, should not happen !",rtppayload->mime_type);
}
rtp_profile_set_payload(dialog_profile,payload->pt,rtppayload);
/* add to the rtp payload type some other parameters (bandwidth) */
if (rtppayload->type==PAYLOAD_VIDEO){
dbw=lc->dw_video_bw;
ubw=lc->up_video_bw;
}else{
dbw=lc->dw_audio_bw;
ubw=lc->up_audio_bw;
}
if (payload->b_as_bandwidth!=0){
ms_message("Remote bandwidth constraint: %i",payload->b_as_bandwidth);
/*obey to remote bandwidth constraint AND our own upbandwidth constraint*/
rtppayload->normal_bitrate=1000*get_min_bandwidth(
payload->b_as_bandwidth, ubw);
}else{
/*limit to upload bandwidth if exist, else no limit*/
if (ubw>0) rtppayload->normal_bitrate=1000*ubw;
else {
if (rtppayload->type!=PAYLOAD_VIDEO){
rtppayload->normal_bitrate=-1; /*allow speex to use maximum bitrate*/
}
}
}
631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700
if (payload->a_fmtp!=NULL){
payload_type_set_send_fmtp(rtppayload,payload->a_fmtp);
}
payload->a_fmtp=rtppayload->recv_fmtp;
if (payload->a_ptime>0){
char tmp[30];
snprintf(tmp,sizeof(tmp),"ptime=%i",payload->a_ptime);
payload_type_append_send_fmtp(rtppayload,tmp);
ms_message("%s attribute added to fmtp",tmp);
}
}
return ret;
}
return Unsupported;
}
int linphone_accept_audio_offer(sdp_context_t *ctx,sdp_payload_t *payload)
{
RtpProfile *remote_profile;
StreamParams *params;
SupportLevel supported;
LinphoneCall *call=(LinphoneCall*)sdp_context_get_user_pointer(ctx);
LinphoneCore *lc=call->core;
PayloadType *lpt=NULL;
params=&call->audio_params;
remote_profile=call->profile;
/* see if this codec is supported in our local rtp profile*/
supported=linphone_payload_is_supported(lc,payload,lc->local_profile,remote_profile,TRUE,&lpt);
if (supported==Unsupported) {
ms_message("Refusing audio codec %i (%s)",payload->pt,payload->a_rtpmap);
return -1;
}
if (lc->sip_conf.only_one_codec && params->initialized){
ms_message("Only one codec has to be accepted.");
return -1;
}
if (supported==SupportedAndValid) {
if (params->initialized==0){
/* this is the first codec we accept, it is going to be used*/
params->localport=lc->rtp_conf.audio_rtp_port;
payload->localport=params->natd_port>0 ?
params->natd_port : lc->rtp_conf.audio_rtp_port;
params->line=payload->line;
params->pt=payload->pt; /* remember the first payload accepted */
if (payload->relay_host!=NULL){
strncpy(params->remoteaddr,payload->relay_host,sizeof(params->remoteaddr)-1);
params->remoteport=payload->relay_port;
params->remotertcpport=payload->relay_port;
params->relay_session_id=payload->relay_session_id;
}else{
strncpy(params->remoteaddr,payload->c_addr,sizeof(params->remoteaddr)-1);
params->remoteport=payload->remoteport;
params->remotertcpport=payload->remoteport+1;
}
params->initialized=1;
/* we can now update the allocated bandwidth for audio, and then video*/
linphone_core_update_allocated_audio_bandwidth_in_call(lc,lpt);
/* give our download bandwidth constraint*/
payload->b_as_bandwidth=(lc->dw_audio_bw>0) ? lc->dw_audio_bw : 0;
}else{
/* refuse all other audio lines*/
if(params->line!=payload->line) {
ms_message("Only one audio line can be accepted.");
#if !defined(_WIN32_WCE)
abort();
#endif /*_WIN32_WCE*/
return -1;
}
}
701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770
}
return 0;
}
int linphone_accept_video_offer(sdp_context_t *ctx,sdp_payload_t *payload)
{
LinphoneCall *call=(LinphoneCall*)sdp_context_get_user_pointer(ctx);
LinphoneCore *lc=call->core;
RtpProfile *remote_profile;
StreamParams *params;
SupportLevel supported;
PayloadType *lpt=NULL;
if (!linphone_core_video_enabled(lc)) return -1;
if (payload->remoteport==0) {
ms_message("Video stream refused by remote.");
return 0;
}
params=&call->video_params;
remote_profile=call->profile;
/* see if this codec is supported in our local rtp profile*/
supported=linphone_payload_is_supported(lc,payload,lc->local_profile,remote_profile,TRUE,&lpt);
if (supported==Unsupported) {
ms_message("Refusing video codec %i (%s)",payload->pt,payload->a_rtpmap);
return -1;
}
if (lc->sip_conf.only_one_codec && params->initialized){
return -1;
}
if (supported==SupportedAndValid){
if (params->initialized==0){
/* this is the first codec we may accept*/
params->localport=lc->rtp_conf.video_rtp_port;
payload->localport=params->natd_port>0 ? params->natd_port : lc->rtp_conf.video_rtp_port;
params->line=payload->line;
params->pt=payload->pt; /* remember the first payload accepted */
if (payload->relay_host!=NULL){
strncpy(params->remoteaddr,payload->relay_host,sizeof(params->remoteaddr)-1);
params->remoteport=payload->relay_port;
params->remotertcpport=payload->relay_port;
params->relay_session_id=payload->relay_session_id;
}else{
strncpy(params->remoteaddr,payload->c_addr,sizeof(params->remoteaddr)-1);
params->remoteport=payload->remoteport;
params->remotertcpport=params->remoteport+1;
}
params->initialized=1;
payload->b_as_bandwidth=(lc->dw_video_bw>0) ? lc->dw_video_bw : 0;
}else{
/* refuse all other video lines*/
if(params->line!=payload->line) return -1;
}
}
return 0;
}
int linphone_read_audio_answer(sdp_context_t *ctx,sdp_payload_t *payload)
{
LinphoneCall *call=(LinphoneCall*)sdp_context_get_user_pointer(ctx);
LinphoneCore *lc=call->core;
StreamParams *params;
SupportLevel supported;
PayloadType *lpt=NULL;
/* paranoid check: see if this codec is supported in our local rtp profile*/
supported=linphone_payload_is_supported(lc, payload,lc->local_profile,call->profile,FALSE,&lpt);
if (supported==Unsupported) {
ms_warning("This remote sip phone did not answer properly to my sdp offer: rtpmap=%s",payload->a_rtpmap);
771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840
return 0;
}
if (supported==SupportedAndValid){
params=&call->audio_params;
if (params->initialized==0){
/* this is the first codec we accept, this is the one that is going to be used (at least for sending
data.*/
params->localport=lc->rtp_conf.audio_rtp_port;
params->line=payload->line;
params->pt=payload->pt; /* remember the first payload accepted */
if (payload->relay_host!=NULL){
strncpy(params->remoteaddr,payload->relay_host,sizeof(params->remoteaddr)-1);
params->remoteport=payload->relay_port;
params->remotertcpport=payload->relay_port;
params->relay_session_id=payload->relay_session_id;
}else{
strncpy(params->remoteaddr,payload->c_addr,sizeof(params->remoteaddr)-1);
params->remoteport=payload->remoteport;
params->remotertcpport=payload->remoteport+1;
}
params->initialized=1;
/* we can now update the allocated bandwidth for audio, and then video*/
linphone_core_update_allocated_audio_bandwidth_in_call(lc,lpt);
}
}
return 0;
}
int linphone_read_video_answer(sdp_context_t *ctx,sdp_payload_t *payload)
{
LinphoneCall *call=(LinphoneCall*)sdp_context_get_user_pointer(ctx);
LinphoneCore *lc=call->core;
StreamParams *params;
SupportLevel supported;
PayloadType *lpt=NULL;
/* paranoid check: see if this codec is supported in our local rtp profile*/
supported=linphone_payload_is_supported(lc, payload,lc->local_profile,call->profile,FALSE,&lpt);
if (supported==Unsupported) {
ms_warning("This remote sip phone did not answer properly to my sdp offer: rtpmap=%s",payload->a_rtpmap);
return 0;
}
if (supported==SupportedAndValid){
params=&call->video_params;
if (params->initialized==0){
/* this is the first codec we may accept*/
params->localport=lc->rtp_conf.video_rtp_port;
params->line=payload->line;
params->pt=payload->pt; /* remember the first payload accepted */
if (payload->relay_host!=NULL){
strncpy(params->remoteaddr,payload->relay_host,sizeof(params->remoteaddr)-1);
params->remoteport=payload->relay_port;
params->remotertcpport=payload->relay_port;
params->relay_session_id=payload->relay_session_id;
}else{
strncpy(params->remoteaddr,payload->c_addr,sizeof(params->remoteaddr)-1);
params->remoteport=payload->remoteport;
params->remotertcpport=payload->remoteport+1;
}
params->initialized=1;
}
}
return 0;
}
void linphone_call_ringing(LinphoneCore *lc, eXosip_event_t *ev){
sdp_message_t *sdp=eXosip_get_sdp_info(ev->response);
LinphoneCall *call=lc->call;
lc->vtable.display_status(lc,_("Remote ringing."));
841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910
linphone_call_proceeding(lc,ev);
if (call==NULL) return;
if (sdp==NULL){
if (lc->ringstream!=NULL) return; /*already ringing !*/
if (lc->sound_conf.play_sndcard!=NULL){
ms_message("Remote ringing...");
lc->ringstream=ring_start(lc->sound_conf.remote_ring,2000,lc->sound_conf.play_sndcard);
}
}else{
/*accept early media */
StreamParams *audio_params;
if (call==NULL){
ms_error("No call ?");
goto end;
}
if (lc->audiostream->ticker!=NULL){
/*streams already started */
ms_message("Early media already started.");
goto end;
}
audio_params=&call->audio_params;
sdp_context_read_answer(lc->call->sdpctx,sdp);
lc->vtable.show(lc);
lc->vtable.display_status(lc,_("Early media."));
gstate_new_state(lc, GSTATE_CALL_OUT_CONNECTED, NULL);
if (lc->ringstream!=NULL){
ring_stop(lc->ringstream);
lc->ringstream=NULL;
}
ms_message("Doing early media...");
linphone_core_start_media_streams(lc,call);
}
call->state=LCStateRinging;
goto end;
end:
sdp_message_free(sdp);
}
static void linphone_process_media_control_xml(LinphoneCore *lc, eXosip_event_t *ev){
osip_body_t *body=NULL;
osip_message_get_body(ev->request,0,&body);
if (body && body->body!=NULL &&
strstr(body->body,"picture_fast_update")){
osip_message_t *ans=NULL;
ms_message("Receiving VFU request !");
#ifdef VIDEO_ENABLED
if (lc->videostream)
video_stream_send_vfu(lc->videostream);
#endif
eXosip_call_build_answer(ev->tid,200,&ans);
if (ans)
eXosip_call_send_answer(ev->tid,200,ans);
}
}
static void linphone_process_dtmf_relay(LinphoneCore *lc, eXosip_event_t *ev){
osip_body_t *body=NULL;
osip_message_get_body(ev->request,0,&body);
if (body && body->body!=NULL){
osip_message_t *ans=NULL;
const char *name=strstr(body->body,"Signal");
if (name==NULL) name=strstr(body->body,"signal");
if (name==NULL) {
ms_warning("Could not extract the dtmf name from the SIP INFO.");
}else{
char tmp[2];
name+=strlen("signal");
if (sscanf(name," = %1s",tmp)==1){
ms_message("Receiving dtmf %s via SIP INFO.",tmp);
911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980
if (lc->vtable.dtmf_received != NULL)
lc->vtable.dtmf_received(lc, tmp[0]);
}
}
eXosip_call_build_answer(ev->tid,200,&ans);
if (ans)
eXosip_call_send_answer(ev->tid,200,ans);
}
}
void linphone_call_message_new(LinphoneCore *lc, eXosip_event_t *ev){
osip_message_t *ans=NULL;
if (ev->request){
if (MSG_IS_INFO(ev->request)){
osip_content_type_t *ct;
ct=osip_message_get_content_type(ev->request);
if (ct && ct->subtype){
if (strcmp(ct->subtype,"media_control+xml")==0)
linphone_process_media_control_xml(lc,ev);
else if (strcmp(ct->subtype,"dtmf-relay")==0)
linphone_process_dtmf_relay(lc,ev);
else {
ms_message("Unhandled SIP INFO.");
/*send an "Not implemented" answer*/
eXosip_call_build_answer(ev->tid,501,&ans);
if (ans)
eXosip_call_send_answer(ev->tid,501,ans);
}
}else{
/*empty SIP INFO, probably to test we are alive. Send an empty answer*/
eXosip_call_build_answer(ev->tid,200,&ans);
if (ans)
eXosip_call_send_answer(ev->tid,200,ans);
}
}
}else ms_warning("linphone_call_message_new: No request ?");
}
void linphone_registration_faillure(LinphoneCore *lc, eXosip_event_t *ev){
int status_code=0;
char *msg;
const char *reason=NULL;
osip_uri_t *requri=osip_message_get_uri(ev->request);
char *ru;
LinphoneProxyConfig *cfg;
if (ev->response){
status_code=osip_message_get_status_code(ev->response);
reason=osip_message_get_reason_phrase(ev->response);
}
switch(status_code){
case 401:
case 407:
linphone_process_authentication(lc,ev);
break;
default:
cfg=linphone_core_get_proxy_config_from_rid(lc,ev->rid);
/* if contact is up to date, process the failure, otherwise resend a new register with
updated contact first, just in case the faillure is due to incorrect contact */
if (linphone_proxy_config_register_again_with_updated_contact(cfg,ev->request,ev->response))
return; /*we are retrying with an updated contact*/
if (status_code==403) linphone_proxy_config_process_authentication_failure(lc,ev);
osip_uri_to_str(requri,&ru);
msg=ortp_strdup_printf(_("Registration on %s failed: %s"),ru,(reason!=NULL) ? reason : _("no response timeout"));
lc->vtable.display_status(lc,msg);
gstate_new_state(lc, GSTATE_REG_FAILED, msg);
ms_free(msg);
osip_free(ru);
}
981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050
}
void linphone_registration_success(LinphoneCore *lc,eXosip_event_t *ev){
LinphoneProxyConfig *cfg;
osip_uri_t *requri=osip_message_get_uri(ev->request);
char *msg;
char *ru;
osip_header_t *h=NULL;
cfg=linphone_core_get_proxy_config_from_rid(lc,ev->rid);
ms_return_if_fail(cfg!=NULL);
gstate_new_state(lc, GSTATE_REG_OK, NULL);
osip_message_get_expires(ev->request,0,&h);
if (h!=NULL && atoi(h->hvalue)!=0){
cfg->registered=TRUE;
linphone_proxy_config_register_again_with_updated_contact(cfg,ev->request,ev->response);
}else cfg->registered=FALSE;
osip_uri_to_str(requri,&ru);
if (cfg->registered) msg=ms_strdup_printf(_("Registration on %s successful."),ru);
else msg=ms_strdup_printf(_("Unregistration on %s done."),ru);
lc->vtable.display_status(lc,msg);
ms_free(msg);
osip_free(ru);
}
static bool_t comes_from_local_if(osip_message_t *msg){
osip_via_t *via=NULL;
osip_message_get_via(msg,0,&via);
if (via){
const char *host;
host=osip_via_get_host(via);
if (strcmp(host,"127.0.0.1")==0 || strcmp(host,"::1")==0){
osip_generic_param_t *param=NULL;
osip_via_param_get_byname(via,"received",¶m);
if (param==NULL) return TRUE;
if (param->gvalue &&
(strcmp(param->gvalue,"127.0.0.1")==0 || strcmp(param->gvalue,"::1")==0)){
return TRUE;
}
}
}
return FALSE;
}
static void linphone_inc_update(LinphoneCore *lc, eXosip_event_t *ev){
osip_message_t *msg=NULL;
ms_message("Processing incoming UPDATE");
eXosip_lock();
eXosip_message_build_answer(ev->tid,200,&msg);
if (msg!=NULL)
eXosip_message_send_answer(ev->tid,200,msg);
eXosip_unlock();
}
static void linphone_other_request(LinphoneCore *lc, eXosip_event_t *ev){
ms_message("in linphone_other_request");
if (ev->request==NULL) return;
if (strcmp(ev->request->sip_method,"MESSAGE")==0){
linphone_core_text_received(lc,ev);
eXosip_message_send_answer(ev->tid,200,NULL);
}else if (strcmp(ev->request->sip_method,"OPTIONS")==0){
osip_message_t *options=NULL;
eXosip_options_build_answer(ev->tid,200,&options);
osip_message_set_allow(options,"INVITE, ACK, BYE, CANCEL, OPTIONS, MESSAGE, SUBSCRIBE, NOTIFY, INFO");
osip_message_set_accept(options,"application/sdp");
eXosip_options_send_answer(ev->tid,200,options);
}else if (strcmp(ev->request->sip_method,"WAKEUP")==0
&& comes_from_local_if(ev->request)) {
1051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120
eXosip_message_send_answer(ev->tid,200,NULL);
ms_message("Receiving WAKEUP request !");
if (lc->vtable.show)
lc->vtable.show(lc);
}else if (strncmp(ev->request->sip_method, "REFER", 5) == 0){
ms_message("Receiving REFER request !");
if (comes_from_local_if(ev->request)) {
osip_header_t *h=NULL;
osip_message_header_get_byname(ev->request,"Refer-To",0,&h);
eXosip_message_send_answer(ev->tid,200,NULL);
if (h){
if (lc->vtable.refer_received)
lc->vtable.refer_received(lc,h->hvalue);
}
}else ms_warning("Ignored REFER not coming from this local loopback interface.");
}else if (strncmp(ev->request->sip_method, "UPDATE", 6) == 0){
linphone_inc_update(lc,ev);
}else {
char *tmp=NULL;
size_t msglen=0;
osip_message_to_str(ev->request,&tmp,&msglen);
if (tmp){
ms_message("Unsupported request received:\n%s",tmp);
osip_free(tmp);
}
/*answer with a 501 Not implemented*/
eXosip_message_send_answer(ev->tid,501,NULL);
}
}
void linphone_core_process_event(LinphoneCore *lc,eXosip_event_t *ev)
{
switch(ev->type){
case EXOSIP_CALL_ANSWERED:
ms_message("CALL_ANSWERED\n");
linphone_call_accepted(lc,ev);
linphone_authentication_ok(lc,ev);
break;
case EXOSIP_CALL_CLOSED:
case EXOSIP_CALL_CANCELLED:
ms_message("CALL_CLOSED or CANCELLED\n");
linphone_call_terminated(lc,ev);
break;
case EXOSIP_CALL_TIMEOUT:
case EXOSIP_CALL_NOANSWER:
ms_message("CALL_TIMEOUT or NOANSWER\n");
linphone_call_failure(lc,ev);
break;
case EXOSIP_CALL_REQUESTFAILURE:
case EXOSIP_CALL_GLOBALFAILURE:
case EXOSIP_CALL_SERVERFAILURE:
ms_message("CALL_REQUESTFAILURE or GLOBALFAILURE or SERVERFAILURE\n");
linphone_call_failure(lc,ev);
break;
case EXOSIP_CALL_INVITE:
ms_message("CALL_NEW\n");
/* CALL_NEW is used twice in qos mode :
* when you receive invite (textinfo = "With QoS" or "Without QoS")
* and when you receive update (textinfo = "New Call") */
linphone_inc_new_call(lc,ev);
break;
case EXOSIP_CALL_REINVITE:
linphone_handle_reinvite(lc,ev);
break;
case EXOSIP_CALL_ACK:
ms_message("CALL_ACK");
linphone_handle_ack(lc,ev);
break;
case EXOSIP_CALL_REDIRECTED:
1121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187
ms_message("CALL_REDIRECTED");
linphone_call_redirected(lc,ev);
break;
case EXOSIP_CALL_PROCEEDING:
ms_message("CALL_PROCEEDING");
linphone_call_proceeding(lc,ev);
break;
case EXOSIP_CALL_RINGING:
ms_message("CALL_RINGING");
linphone_call_ringing(lc,ev);
break;
case EXOSIP_CALL_MESSAGE_NEW:
ms_message("EXOSIP_CALL_MESSAGE_NEW");
linphone_call_message_new(lc,ev);
break;
case EXOSIP_CALL_MESSAGE_REQUESTFAILURE:
if (ev->did<0 && ev->response &&
(ev->response->status_code==407 || ev->response->status_code==401)){
eXosip_default_action(ev);
}
break;
case EXOSIP_IN_SUBSCRIPTION_NEW:
ms_message("CALL_SUBSCRIPTION_NEW or UPDATE");
linphone_subscription_new(lc,ev);
break;
case EXOSIP_SUBSCRIPTION_UPDATE:
break;
case EXOSIP_SUBSCRIPTION_NOTIFY:
ms_message("CALL_SUBSCRIPTION_NOTIFY");
linphone_notify_recv(lc,ev);
break;
case EXOSIP_SUBSCRIPTION_ANSWERED:
ms_message("EXOSIP_SUBSCRIPTION_ANSWERED, ev->sid=%i\n",ev->sid);
linphone_subscription_answered(lc,ev);
break;
case EXOSIP_SUBSCRIPTION_CLOSED:
ms_message("EXOSIP_SUBSCRIPTION_CLOSED\n");
linphone_subscription_closed(lc,ev);
break;
case EXOSIP_CALL_RELEASED:
ms_message("CALL_RELEASED\n");
linphone_call_released(lc, ev->cid);
break;
case EXOSIP_REGISTRATION_FAILURE:
ms_message("REGISTRATION_FAILURE\n");
linphone_registration_faillure(lc,ev);
break;
case EXOSIP_REGISTRATION_SUCCESS:
linphone_authentication_ok(lc,ev);
linphone_registration_success(lc,ev);
break;
case EXOSIP_MESSAGE_NEW:
linphone_other_request(lc,ev);
break;
case EXOSIP_MESSAGE_REQUESTFAILURE:
if (ev->response && (ev->response->status_code == 407 || ev->response->status_code == 401)){
/*the user is expected to have registered to the proxy, thus password is known*/
eXosip_default_action(ev);
}
break;
default:
ms_message("Unhandled exosip event !");
break;
}
eXosip_event_free(ev);
}