From 39ff61f8038890a24b1ffe83374061eceeb9714e Mon Sep 17 00:00:00 2001 From: Simon Morlat Date: Mon, 11 Jan 2010 18:40:04 +0100 Subject: [PATCH] SAL (signaling abstraction layer) in progress. --- linphone/coreapi/Makefile.am | 3 + linphone/coreapi/offeranswer.c | 142 ++++++++++++++++++++++++ linphone/coreapi/offeranswer.h | 47 ++++++++ linphone/coreapi/sal.h | 104 ++++++++++++++++++ linphone/coreapi/sal_eXosip2.c | 153 ++++++++++++++++++++++++++ linphone/coreapi/sal_eXosip2_sdp.c | 170 +++++++++++++++++++++++++++++ 6 files changed, 619 insertions(+) create mode 100644 linphone/coreapi/offeranswer.c create mode 100644 linphone/coreapi/offeranswer.h create mode 100644 linphone/coreapi/sal.h create mode 100644 linphone/coreapi/sal_eXosip2.c create mode 100644 linphone/coreapi/sal_eXosip2_sdp.c diff --git a/linphone/coreapi/Makefile.am b/linphone/coreapi/Makefile.am index c0a28ade3..7c3827f29 100644 --- a/linphone/coreapi/Makefile.am +++ b/linphone/coreapi/Makefile.am @@ -16,6 +16,9 @@ lib_LTLIBRARIES=liblinphone.la liblinphone_la_SOURCES=\ linphonecore.c linphonecore.h private.h\ exevents.c exevents.h \ + offeranswer.c offeranswer.h\ + sal_eXosip2.c sal.h \ + sal_eXosip2_sdp.c \ misc.c \ address.c \ enum.c enum.h \ diff --git a/linphone/coreapi/offeranswer.c b/linphone/coreapi/offeranswer.c new file mode 100644 index 000000000..03c134143 --- /dev/null +++ b/linphone/coreapi/offeranswer.c @@ -0,0 +1,142 @@ +/* +linphone +Copyright (C) 2010 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 "sal.h" +#include "offeranswer.h" + + +static PayloadType * find_payload_type_best_match(const MSList *l, const PayloadType *refpt){ + PayloadType *pt; + char value[10]; + const MSList *elem; + PayloadType *candidate=NULL; + + for (elem=l;elem!=NULL;elem=elem->next){ + pt=(PayloadType*)elem->data; + if (strcasecmp(pt->mime_type,refpt->mime_type)==0 && pt->clock_rate==refpt->clock_rate){ + candidate=pt; + /*good candidate, check fmtp for H264 */ + if (strcasecmp(pt->mime_type,"H264")==0){ + if (pt->recv_fmtp!=NULL && refpt->recv_fmtp!=NULL){ + int mode1=0,mode2=0; + if (fmtp_get_value(pt->recv_fmtp,"packetization-mode",value,sizeof(value))){ + mode1=atoi(value); + } + if (fmtp_get_value(refpt->recv_fmtp,"packetization-mode",value,sizeof(value))){ + mode2=atoi(value); + } + if (mode1==mode2) + break; /*exact match */ + } + }else break; + } + } + return candidate; +} + +static MSList *match_payloads(const MSList *local, const MSList *remote){ + const MSList *e2; + MSList *res=NULL; + PayloadType *matched; + for(e2=remote;e2!=NULL;e2=e2->next){ + PayloadType *p2=(PayloadType*)e2->data; + matched=find_payload_type_best_match(local,p2); + if (matched){ + matched=payload_type_clone(matched); + if (p2->recv_fmtp) + payload_type_set_send_fmtp(matched,p2->recv_fmtp); + res=ms_list_append(res,matched); + payload_type_set_number(matched,payload_type_get_number(p2)); + }else{ + ms_message("No match for %s/%i",p2->mime_type,p2->clock_rate); + } + } + return res; +} + +static bool_t only_telephone_event(const MSList *l){ + for(;l!=NULL;l=l->next){ + PayloadType *p=(PayloadType*)l->data; + if (strcasecmp(p->mime_type,"telephone-event")!=0){ + return FALSE; + } + } + return TRUE; +} + +static void initiate_outgoing(const SalStreamDescription *local_offer, + const SalStreamDescription *remote_answer, + SalStreamDescription *result){ + result->payloads=match_payloads(local_offer->payloads,remote_answer->payloads); + if (result->payloads && !only_telephone_event(result->payloads)){ + result->port=remote_answer->port; + result->bandwidth=remote_answer->bandwidth; + result->ptime=remote_answer->ptime; + }else{ + result->port=0; + } +} + + +static void initiate_incoming(const SalStreamDescription *local_cap, + const SalStreamDescription *remote_offer, + SalStreamDescription *result){ + result->payloads=match_payloads(local_cap->payloads,remote_offer->payloads); + if (result->payloads && !only_telephone_event(result->payloads)){ + result->port=remote_offer->port; + result->bandwidth=remote_offer->bandwidth; + result->ptime=remote_offer->ptime; + }else{ + result->port=0; + } +} + +/** + * Returns a media description to run the streams with, based on a local offer + * and the returned response (remote). +**/ +int offer_answer_initiate_outgoing(const SalMediaDescription *local_offer, + const SalMediaDescription *remote_answer, + SalMediaDescription *result){ + int i; + for(i=0;instreams;++i){ + initiate_outgoing(&local_offer->streams[i],&remote_answer->streams[i],&result->streams[i]); + } + result->nstreams=local_offer->nstreams; + strcpy(result->addr,remote_answer->addr); + return 0; +} + +/** + * Returns a media description to run the streams with, based on the local capabilities and + * and the received offer. + * The returned media description is an answer and should be sent to the offerer. +**/ +int offer_answer_initiate_incoming(const SalMediaDescription *local_capabilities, + const SalMediaDescription *remote_offer, + SalMediaDescription *result){ + int i; + for(i=0;instreams;++i){ + initiate_incoming(&local_capabilities->streams[i],&remote_offer->streams[i],&result->streams[i]); + } + result->nstreams=local_capabilities->nstreams; + strcpy(result->addr,remote_offer->addr); + return 0; +} + diff --git a/linphone/coreapi/offeranswer.h b/linphone/coreapi/offeranswer.h new file mode 100644 index 000000000..079f41c96 --- /dev/null +++ b/linphone/coreapi/offeranswer.h @@ -0,0 +1,47 @@ +/* +linphone +Copyright (C) 2010 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. +*/ + +#ifndef offeranswer_h +#define offeranswer_h + +/** + This header files defines the SDP offer answer API. + It can be used by implementations of SAL directly. +**/ + + +/** + * Returns a media description to run the streams with, based on a local offer + * and the returned response (remote). +**/ +int offer_answer_initiate_outgoing(const SalMediaDescription *local_offer, + const SalMediaDescription *remote_answer, + SalMediaDescription *result); + +/** + * Returns a media description to run the streams with, based on the local capabilities and + * and the received offer. + * The returned media description is an answer and should be sent to the offerer. +**/ +int offer_answer_initiate_incoming(const SalMediaDescription *local_capabilities, + const SalMediaDescription *remote_offer, + SalMediaDescription *result); + +#endif + diff --git a/linphone/coreapi/sal.h b/linphone/coreapi/sal.h new file mode 100644 index 000000000..12fd32bde --- /dev/null +++ b/linphone/coreapi/sal.h @@ -0,0 +1,104 @@ +/* +linphone +Copyright (C) 2010 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. +*/ + +/** + This header files defines the Signaling Abstraction Layer. + The purpose of this layer is too allow experiment different call signaling + protocols and implementations under linphone, for example SIP, JINGLE... +**/ + +#ifndef sal_h +#define sal_h + +#include "mediastreamer2/mscommon.h" + +struct Sal; + +typedef struct Sal Sal; + +struct SalOp; + +typedef struct SalOp SalOp; + +Sal * sal_init(); +void sal_uninit(Sal* sal); + +typedef enum { + SAL_TRANSPORT_DATAGRAM, + SAL_TRANSPORT_STREAM +}SalTransport; + +typedef enum { + SAL_AUDIO, + SAL_VIDEO, + SAL_OTHER +} SalStreamType; + +typedef struct SalStreamDescription{ + SalStreamType type; + int port; + MSList *payloads; //user_data=(void*)((long)n); +#define payload_type_get_number(pt) ((int)(long)(pt)->user_data) + +#endif diff --git a/linphone/coreapi/sal_eXosip2.c b/linphone/coreapi/sal_eXosip2.c new file mode 100644 index 000000000..be95a0661 --- /dev/null +++ b/linphone/coreapi/sal_eXosip2.c @@ -0,0 +1,153 @@ +/* +linphone +Copyright (C) 2010 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 "sal.h" +#include + +extern char *media_description_to_sdp(const SalMediaDescription *sal); + +struct Sal{ + int running; + int session_expires; +}; + +struct SalOp{ + int cid; + int did; + int tid; + osip_message_t *request; +}; + +static SalOp * sal_op_new(){ + SalOp *op=ms_new(SalOp,1); + op->cid=op->did=op->tid=-1; + op->request=NULL; + return op; +} + +void sal_op_release(SalOp *op){ + ms_free(op); +} + +Sal * sal_init(){ + eXosip_init(); + return ms_new0(Sal,1); +} + +void sal_uninit(Sal* sal){ + eXosip_quit(); + ms_free(sal); +} + + +int sal_listen_port(Sal *ctx, const char *addr, int port, SalTransport tr, int is_secure){ + int err; + bool_t ipv6; + int proto=IPPROTO_UDP; + + if (ctx->running) eXosip_quit(); + eXosip_init(); + err=0; + eXosip_set_option(13,&err); /*13=EXOSIP_OPT_SRV_WITH_NAPTR, as it is an enum value, we can't use it unless we are sure of the + version of eXosip, which is not the case*/ + /*see if it looks like an IPv6 address*/ + ipv6=strchr(addr,':')!=NULL; + eXosip_enable_ipv6(ipv6); + + if (tr!=SAL_TRANSPORT_DATAGRAM || is_secure){ + ms_fatal("SIP over TCP or TLS or DTLS is not supported yet."); + return -1; + } + + err=eXosip_listen_addr(proto, addr, port, ipv6 ? PF_INET6 : PF_INET, 0); + return err; +} + +void sal_set_user_agent(Sal *ctx, const char *user_agent){ + eXosip_set_user_agent(user_agent); +} + +void sal_use_session_timers(Sal *ctx, int expires){ + ctx->session_expires=expires; +} + +SalOp * sal_call_create(Sal *sal, const char *from, const char *to, const char *route, const char *contact){ + int err; + SalOp *op; + osip_message_t *invite=NULL; + err=eXosip_call_build_initial_invite(&invite,to,from, + route,"Phone call"); + if (err!=0){ + ms_error("Could not create call."); + return NULL; + } + if (contact) + osip_message_set_contact(invite,contact); + if (sal->session_expires!=0){ + osip_message_set_header(invite, "Session-expires", "200"); + osip_message_set_supported(invite, "timer"); + } + op=sal_op_new(); + op->request=invite; + return op; +} + +static void set_sdp(osip_message_t *sip, const SalMediaDescription *desc){ + int sdplen; + char clen[10]; + char *sdp=media_description_to_sdp(desc); + + if (sdp==NULL) { + ms_error("Fail to print sdp message !"); + return; + } + sdplen=strlen(sdp); + snprintf(clen,sizeof(clen),"%i",sdplen); + osip_message_set_body(sip,sdp,sdplen); + osip_message_set_content_type(sip,"application/sdp"); + osip_message_set_content_length(sip,clen); + osip_free(sdp); +} + +int sal_call_set_local_media_description(SalOp *h, const SalMediaDescription *desc){ + set_sdp(h->request,desc); + return 0; +} + +int sal_call(SalOp *h){ + int err; + eXosip_lock(); + err=eXosip_call_send_initial_invite(h->request); + eXosip_unlock(); + h->cid=err; + if (err<0){ + ms_error("Fail to send invite !"); + return -1; + } + return 0; +} + +int sal_call_accept(SalOp*h); +int sal_call_get_final_media_description(SalOp *h, SalMediaDescription *result); +int sal_call_terminate(SalOp *h); + +int sal_iterate(Sal *sal); + +SalOp *sal_register_create(Sal *ctx, const char *from, const char *contact, int expires); +int sal_register(SalOp *h); diff --git a/linphone/coreapi/sal_eXosip2_sdp.c b/linphone/coreapi/sal_eXosip2_sdp.c new file mode 100644 index 000000000..e679621b6 --- /dev/null +++ b/linphone/coreapi/sal_eXosip2_sdp.c @@ -0,0 +1,170 @@ +/* +linphone +Copyright (C) 2010 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 "ortp/b64.h" +#include "sal.h" +#include + +#define keywordcmp(key,b) strncmp(key,b,sizeof(key)) + +#ifdef FOR_LATER + +static char *make_relay_session_id(const char *username, const char *relay){ + /*ideally this should be a hash of the parameters with a random part*/ + char tmp[128]; + int s1=(int)random(); + int s2=(int)random(); + long long int res=((long long int)s1)<<32 | (long long int) s2; + void *src=&res; + b64_encode(src, sizeof(long long int), tmp, sizeof(tmp)); + return osip_strdup(tmp); +} + + +static void add_relay_info(sdp_message_t *sdp, int mline, const char *relay, const char *relay_session_id){ + + if (relay) sdp_message_a_attribute_add(sdp, mline, + osip_strdup ("relay-addr"),osip_strdup(relay)); + if (relay_session_id) sdp_message_a_attribute_add(sdp, mline, + osip_strdup ("relay-session-id"), osip_strdup(relay_session_id)); +} + +#endif + +char * int_2char(int a){ + char *p=osip_malloc(16); + snprintf(p,16,"%i",a); + return p; +} + +/* return the value of attr "field" for payload pt at line pos (field=rtpmap,fmtp...)*/ +char *sdp_message_a_attr_value_get_with_pt(sdp_message_t *sdp,int pos,int pt,const char *field) +{ + int i,tmppt=0,scanned=0; + char *tmp; + sdp_attribute_t *attr; + for (i=0;(attr=sdp_message_attribute_get(sdp,pos,i))!=NULL;i++){ + if (keywordcmp(field,attr->a_att_field)==0 && attr->a_att_value!=NULL){ + int nb = sscanf(attr->a_att_value,"%i %n",&tmppt,&scanned); + /* the return value may depend on how %n is interpreted by the libc: see manpage*/ + if (nb == 1 || nb==2 ){ + if (pt==tmppt){ + tmp=attr->a_att_value+scanned; + if (strlen(tmp)>0) + return tmp; + } + }else ms_warning("sdp has a strange a= line (%s) nb=%i",attr->a_att_value,nb); + } + } + return NULL; +} + +/* return the value of attr "field" */ +char *sdp_message_a_attr_value_get(sdp_message_t *sdp,int pos,const char *field) +{ + int i; + sdp_attribute_t *attr; + for (i=0;(attr=sdp_message_attribute_get(sdp,pos,i))!=NULL;i++){ + if (keywordcmp(field,attr->a_att_field)==0 && attr->a_att_value!=NULL){ + return attr->a_att_value; + } + } + return NULL; +} + +static int _sdp_message_get_a_ptime(sdp_message_t *sdp, int mline){ + int i,ret; + sdp_attribute_t *attr; + for (i=0;(attr=sdp_message_attribute_get(sdp,mline,i))!=NULL;i++){ + if (keywordcmp("ptime",attr->a_att_field)==0){ + int nb = sscanf(attr->a_att_value,"%i",&ret); + /* the return value may depend on how %n is interpreted by the libc: see manpage*/ + if (nb == 1){ + return ret; + }else ms_warning("sdp has a strange a=ptime line (%s) ",attr->a_att_value); + } + } + return 0; +} + +static sdp_message_t *create_generic_sdp(const SalMediaDescription *desc) +{ + sdp_message_t *local; + int inet6; + + sdp_message_init (&local); + if (strchr(desc->addr,':')!=NULL){ + inet6=1; + }else inet6=0; + sdp_message_v_version_set (local, osip_strdup ("0")); + sdp_message_o_origin_set (local, osip_strdup (desc->username), + osip_strdup ("123456"), osip_strdup ("654321"), + osip_strdup ("IN"), inet6 ? osip_strdup("IP6") : osip_strdup ("IP4"), + osip_strdup (desc->addr)); + sdp_message_s_name_set (local, osip_strdup ("A conversation")); + sdp_message_c_connection_add (local, -1, + osip_strdup ("IN"), inet6 ? osip_strdup ("IP6") : osip_strdup ("IP4"), + osip_strdup (desc->addr), NULL, NULL); + sdp_message_t_time_descr_add (local, osip_strdup ("0"), osip_strdup ("0")); + return local; +} + + + +void add_payload(sdp_message_t *msg, int line, const PayloadType *pt) +{ + char attr[256]; + sdp_message_m_payload_add (msg,line, int_2char (payload_type_get_number(pt))); + snprintf (attr,sizeof(attr),"%i %s", payload_type_get_number(pt), pt->mime_type); + sdp_message_a_attribute_add (msg, line, + osip_strdup ("rtpmap"), osip_strdup(attr)); + + if (pt->recv_fmtp != NULL) + { + snprintf (attr,sizeof(attr),"%i %s", payload_type_get_number(pt),pt->recv_fmtp); + sdp_message_a_attribute_add (msg, line, osip_strdup ("fmtp"), + osip_strdup(attr)); + } +} + + +static void add_line(sdp_message_t *msg, int lineno, const SalStreamDescription *desc){ + const char *mt=desc->type==SAL_AUDIO ? "audio" : "video"; + const MSList *elem; + sdp_message_m_media_add (msg, osip_strdup (mt), + int_2char (desc->port), NULL, + osip_strdup ("RTP/AVP")); + sdp_message_b_bandwidth_add (msg, lineno, osip_strdup ("AS"), + int_2char(desc->bandwidth)); + for(elem=desc->payloads;elem!=NULL;elem=elem->next){ + add_payload(msg, lineno, (PayloadType*)elem->data); + } +} + +char *media_description_to_sdp(const SalMediaDescription *desc){ + int i; + char *tmp; + sdp_message_t *msg=create_generic_sdp(desc); + for(i=0;instreams;++i){ + add_line(msg,i,&desc->streams[i]); + } + sdp_message_to_str(msg,&tmp); + return tmp; +}