]> sjero.net Git - linphone/blob - coreapi/offeranswer.c
fix incorrect rtp ports with stun discovery, for incoming calls only
[linphone] / coreapi / offeranswer.c
1 /*
2 linphone
3 Copyright (C) 2010  Simon MORLAT (simon.morlat@free.fr)
4
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either version 2
8 of the License, or (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18 */
19
20 #include "sal.h"
21 #include "offeranswer.h"
22
23 static bool_t only_telephone_event(const MSList *l){
24         PayloadType *p=(PayloadType*)l->data;
25         if (strcasecmp(p->mime_type,"telephone-event")!=0){
26                 return FALSE;
27         }
28         return TRUE;
29 }
30
31 static PayloadType * find_payload_type_best_match(const MSList *l, const PayloadType *refpt){
32         PayloadType *pt;
33         char value[10];
34         const MSList *elem;
35         PayloadType *candidate=NULL;
36
37         for (elem=l;elem!=NULL;elem=elem->next){
38                 pt=(PayloadType*)elem->data;
39                 /* the compare between G729 and G729A is for some stupid uncompliant phone*/
40                 if ( (strcasecmp(pt->mime_type,refpt->mime_type)==0  ||
41                     (strcasecmp(pt->mime_type, "G729") == 0 && strcasecmp(refpt->mime_type, "G729A") == 0 ))
42                         && pt->clock_rate==refpt->clock_rate){
43                         candidate=pt;
44                         /*good candidate, check fmtp for H264 */
45                         if (strcasecmp(pt->mime_type,"H264")==0){
46                                 if (pt->recv_fmtp!=NULL && refpt->recv_fmtp!=NULL){
47                                         int mode1=0,mode2=0;
48                                         if (fmtp_get_value(pt->recv_fmtp,"packetization-mode",value,sizeof(value))){
49                                                 mode1=atoi(value);
50                                         }
51                                         if (fmtp_get_value(refpt->recv_fmtp,"packetization-mode",value,sizeof(value))){
52                                                 mode2=atoi(value);
53                                         }
54                                         if (mode1==mode2)
55                                             break; /*exact match */
56                                 }
57                         }else break;
58                 }
59         }
60         return candidate;
61 }
62
63 static MSList *match_payloads(const MSList *local, const MSList *remote, bool_t reading_response, bool_t one_matching_codec){
64         const MSList *e2;
65         MSList *res=NULL;
66         PayloadType *matched;
67         bool_t found_codec=FALSE;
68         
69         for(e2=remote;e2!=NULL;e2=e2->next){
70                 PayloadType *p2=(PayloadType*)e2->data;
71                 matched=find_payload_type_best_match(local,p2);
72                 if (matched){
73                         PayloadType *newp;
74                         int local_number=payload_type_get_number(matched);
75                         int remote_number=payload_type_get_number(p2);
76
77                         if (one_matching_codec){
78                                 if (strcasecmp(matched->mime_type,"telephone-event")!=0){
79                                         if (found_codec){/* we have found a real codec already*/
80                                                 continue; /*this codec won't be added*/
81                                         }else found_codec=TRUE;
82                                 }
83                         }
84                         
85                         newp=payload_type_clone(matched);
86                         if (p2->send_fmtp)
87                                 payload_type_set_send_fmtp(newp,p2->send_fmtp);
88                         res=ms_list_append(res,newp);
89                         /* we should use the remote numbering even when parsing a response */
90                         payload_type_set_number(newp,remote_number);
91                         if (reading_response && remote_number!=local_number){
92                                 ms_warning("For payload type %s, proposed number was %i but the remote phone answered %i",
93                                           newp->mime_type, local_number, remote_number);
94                                 /*
95                                  We must add this payload type with our local numbering in order to be able to receive it.
96                                  Indeed despite we must sent with the remote numbering, we must be able to receive with
97                                  our local one.
98                                 */
99                                 newp=payload_type_clone(matched);
100                                 payload_type_set_number(newp,local_number);
101                                 res=ms_list_append(res,newp);
102                         }
103                 }else{
104                         ms_message("No match for %s/%i",p2->mime_type,p2->clock_rate);
105                 }
106         }
107         return res;
108 }
109
110
111
112 static SalStreamDir compute_dir_outgoing(SalStreamDir local, SalStreamDir answered){
113         SalStreamDir res=local;
114         if (local==SalStreamSendRecv){
115                 if (answered==SalStreamRecvOnly){
116                         res=SalStreamSendOnly;
117                 }else if (answered==SalStreamSendOnly){
118                         res=SalStreamRecvOnly;
119                 }
120         }
121         if (answered==SalStreamInactive){
122                 res=SalStreamInactive;
123         }
124         return res;
125 }
126
127 static SalStreamDir compute_dir_incoming(SalStreamDir local, SalStreamDir offered){
128         SalStreamDir res=SalStreamSendRecv;
129         if (local==SalStreamSendRecv){
130                 if (offered==SalStreamSendOnly)
131                         res=SalStreamRecvOnly;
132                 else if (offered==SalStreamRecvOnly)
133                         res=SalStreamSendOnly;
134                 else if (offered==SalStreamInactive)
135                         res=SalStreamInactive;
136                 else
137                         res=SalStreamSendRecv;
138         }else if (local==SalStreamSendOnly){
139                 if (offered==SalStreamRecvOnly || offered==SalStreamSendRecv)
140                         res=SalStreamSendOnly;
141                 else res=SalStreamInactive;
142         }else if (local==SalStreamRecvOnly){
143                 if (offered==SalStreamSendOnly || offered==SalStreamSendRecv)
144                         res=SalStreamRecvOnly;
145                 else
146                         res=SalStreamInactive;
147         }else res=SalStreamInactive;
148         return res;
149 }
150
151 static void initiate_outgoing(const SalStreamDescription *local_offer,
152                                         const SalStreamDescription *remote_answer,
153                                         SalStreamDescription *result){
154         if (remote_answer->port!=0)
155                 result->payloads=match_payloads(local_offer->payloads,remote_answer->payloads,TRUE,FALSE);
156         result->proto=local_offer->proto;
157         result->type=local_offer->type;
158         result->dir=compute_dir_outgoing(local_offer->dir,remote_answer->dir);
159
160         if (result->payloads && !only_telephone_event(result->payloads)){
161                 strcpy(result->addr,remote_answer->addr);
162                 result->port=remote_answer->port;
163                 result->bandwidth=remote_answer->bandwidth;
164                 result->ptime=remote_answer->ptime;
165         }else{
166                 result->port=0;
167         }
168 }
169
170
171 static void initiate_incoming(const SalStreamDescription *local_cap,
172                                         const SalStreamDescription *remote_offer,
173                                         SalStreamDescription *result, bool_t one_matching_codec){
174         result->payloads=match_payloads(local_cap->payloads,remote_offer->payloads, FALSE, one_matching_codec);
175         result->proto=local_cap->proto;
176         result->type=local_cap->type;
177         result->dir=compute_dir_incoming(local_cap->dir,remote_offer->dir);
178         if (result->payloads && !only_telephone_event(result->payloads)){
179                 strcpy(result->addr,local_cap->addr);
180                 memcpy(result->candidates,local_cap->candidates,sizeof(result->candidates));
181                 result->port=local_cap->port;
182                 result->bandwidth=local_cap->bandwidth;
183                 result->ptime=local_cap->ptime; 
184         }else{
185                 result->port=0;
186         }
187 }
188
189 /**
190  * Returns a media description to run the streams with, based on a local offer
191  * and the returned response (remote).
192 **/
193 int offer_answer_initiate_outgoing(const SalMediaDescription *local_offer,
194                                                                         const SalMediaDescription *remote_answer,
195                                                         SalMediaDescription *result){
196     int i,j;
197         const SalStreamDescription *ls,*rs;
198     for(i=0,j=0;i<local_offer->nstreams;++i){
199                 ms_message("Processing for stream %i",i);
200                 ls=&local_offer->streams[i];
201                 rs=sal_media_description_find_stream((SalMediaDescription*)remote_answer,ls->proto,ls->type);
202         if (rs) {
203                         initiate_outgoing(ls,rs,&result->streams[j]);
204                         ++j;
205                 }
206                 else ms_warning("No matching stream for %i",i);
207     }
208         result->nstreams=j;
209         result->bandwidth=remote_answer->bandwidth;
210         strcpy(result->addr,remote_answer->addr);
211         return 0;
212 }
213
214 /**
215  * Returns a media description to run the streams with, based on the local capabilities and
216  * and the received offer.
217  * The returned media description is an answer and should be sent to the offerer.
218 **/
219 int offer_answer_initiate_incoming(const SalMediaDescription *local_capabilities,
220                                                 const SalMediaDescription *remote_offer,
221                                         SalMediaDescription *result, bool_t one_matching_codec){
222     int i;
223         const SalStreamDescription *ls,*rs;
224                                                         
225     for(i=0;i<remote_offer->nstreams;++i){
226                 rs=&remote_offer->streams[i];
227                 ms_message("Processing for stream %i",i);
228                 ls=sal_media_description_find_stream((SalMediaDescription*)local_capabilities,rs->proto,rs->type);
229                 if (ls){
230                 initiate_incoming(ls,rs,&result->streams[i],one_matching_codec);
231                 } else {
232                         /* create an inactive stream for the answer, as there where no matching stream a local capability */
233                         result->streams[i].dir=SalStreamInactive;
234                         result->streams[i].port=0;
235                         result->streams[i].type=rs->type;
236                         if (rs->type==SalOther){
237                                 strncpy(result->streams[i].typeother,rs->typeother,sizeof(rs->typeother)-1);
238                         }
239                 }
240     }
241         result->nstreams=i;
242         strcpy(result->username, local_capabilities->username);
243         strcpy(result->addr,local_capabilities->addr);
244         result->bandwidth=local_capabilities->bandwidth;
245         result->session_ver=local_capabilities->session_ver;
246         result->session_id=local_capabilities->session_id;
247         return 0;
248 }