]> sjero.net Git - linphone/blob - coreapi/offeranswer.c
Merge branch 'message'
[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         for(;l!=NULL;l=l->next){
25                 PayloadType *p=(PayloadType*)l->data;
26                 if (strcasecmp(p->mime_type,"telephone-event")!=0){
27                         return FALSE;
28                 }
29         }
30         return TRUE;
31 }
32
33 static PayloadType * find_payload_type_best_match(const MSList *l, const PayloadType *refpt){
34         PayloadType *pt;
35         char value[10];
36         const MSList *elem;
37         PayloadType *candidate=NULL;
38
39         for (elem=l;elem!=NULL;elem=elem->next){
40                 pt=(PayloadType*)elem->data;
41                 /* the compare between G729 and G729A is for some stupid uncompliant phone*/
42                 if ( (strcasecmp(pt->mime_type,refpt->mime_type)==0  ||
43                     (strcasecmp(pt->mime_type, "G729") == 0 && strcasecmp(refpt->mime_type, "G729A") == 0 ))
44                         && pt->clock_rate==refpt->clock_rate){
45                         candidate=pt;
46                         /*good candidate, check fmtp for H264 */
47                         if (strcasecmp(pt->mime_type,"H264")==0){
48                                 if (pt->recv_fmtp!=NULL && refpt->recv_fmtp!=NULL){
49                                         int mode1=0,mode2=0;
50                                         if (fmtp_get_value(pt->recv_fmtp,"packetization-mode",value,sizeof(value))){
51                                                 mode1=atoi(value);
52                                         }
53                                         if (fmtp_get_value(refpt->recv_fmtp,"packetization-mode",value,sizeof(value))){
54                                                 mode2=atoi(value);
55                                         }
56                                         if (mode1==mode2)
57                                             break; /*exact match */
58                                 }
59                         }else break;
60                 }
61         }
62         return candidate;
63 }
64
65 static MSList *match_payloads(const MSList *local, const MSList *remote, bool_t reading_response, bool_t one_matching_codec){
66         const MSList *e2,*e1;
67         MSList *res=NULL;
68         PayloadType *matched;
69         bool_t found_codec=FALSE;
70         
71         for(e2=remote;e2!=NULL;e2=e2->next){
72                 PayloadType *p2=(PayloadType*)e2->data;
73                 matched=find_payload_type_best_match(local,p2);
74                 if (matched){
75                         PayloadType *newp;
76                         int local_number=payload_type_get_number(matched);
77                         int remote_number=payload_type_get_number(p2);
78
79                         if (one_matching_codec){
80                                 if (strcasecmp(matched->mime_type,"telephone-event")!=0){
81                                         if (found_codec){/* we have found a real codec already*/
82                                                 continue; /*this codec won't be added*/
83                                         }else found_codec=TRUE;
84                                 }
85                         }
86                         
87                         newp=payload_type_clone(matched);
88                         if (p2->send_fmtp)
89                                 payload_type_set_send_fmtp(newp,p2->send_fmtp);
90                         newp->flags|=PAYLOAD_TYPE_FLAG_CAN_RECV|PAYLOAD_TYPE_FLAG_CAN_SEND;
91                         res=ms_list_append(res,newp);
92                         /* we should use the remote numbering even when parsing a response */
93                         payload_type_set_number(newp,remote_number);
94                         if (reading_response && remote_number!=local_number){
95                                 ms_warning("For payload type %s, proposed number was %i but the remote phone answered %i",
96                                           newp->mime_type, local_number, remote_number);
97                                 /*
98                                  We must add this payload type with our local numbering in order to be able to receive it.
99                                  Indeed despite we must sent with the remote numbering, we must be able to receive with
100                                  our local one.
101                                 */
102                                 newp=payload_type_clone(newp);
103                                 payload_type_set_number(newp,local_number);
104                                 res=ms_list_append(res,newp);
105                         }
106                 }else{
107                         ms_message("No match for %s/%i",p2->mime_type,p2->clock_rate);
108                 }
109         }
110         if (reading_response){
111                 /* add remaning local payload as CAN_RECV only so that if we are in front of a non-compliant equipment we are still able to decode the RTP stream*/
112                 for(e1=local;e1!=NULL;e1=e1->next){
113                         PayloadType *p1=(PayloadType*)e1->data;
114                         bool_t found=FALSE;
115                         for(e2=res;e2!=NULL;e2=e2->next){
116                                 PayloadType *p2=(PayloadType*)e2->data;
117                                 if (payload_type_get_number(p2)==payload_type_get_number(p1)){
118                                         found=TRUE;
119                                         break;
120                                 }
121                         }
122                         if (!found){
123                                 ms_message("Adding %s/%i for compatibility, just in case.",p1->mime_type,p1->clock_rate);
124                                 p1=payload_type_clone(p1);
125                                 p1->flags|=PAYLOAD_TYPE_FLAG_CAN_RECV;
126                                 res=ms_list_append(res,p1);
127                         }
128                 }
129         }
130         return res;
131 }
132
133 static bool_t match_crypto_algo(const SalSrtpCryptoAlgo* local, const SalSrtpCryptoAlgo* remote, 
134         SalSrtpCryptoAlgo* result, unsigned int* choosen_local_tag, bool_t use_local_key) {
135         int i,j;
136         for(i=0; i<SAL_CRYPTO_ALGO_MAX; i++) {
137                 if (remote[i].algo == 0)
138                         break;
139
140         /* Look for a local enabled crypto algo that matches one of the proposed by remote */
141                 for(j=0; j<SAL_CRYPTO_ALGO_MAX; j++) {
142                         if (remote[i].algo == local[j].algo) {
143                                 result->algo = remote[i].algo;
144             /* We're answering an SDP offer. Supply our master key, associated with the remote supplied tag */
145                                 if (use_local_key) {
146                                         strncpy(result->master_key, local[j].master_key, 41);
147                                         result->tag = remote[i].tag;
148                     *choosen_local_tag = local[j].tag;
149                                 }
150                                 /* We received an answer to our SDP crypto proposal. Copy matching algo remote master key to result, and memorize local tag */
151             else {
152                                         strncpy(result->master_key, remote[i].master_key, 41);
153                                         result->tag = local[j].tag;
154                     *choosen_local_tag = local[j].tag;
155                                 }
156                                 result->master_key[40] = '\0';
157                                 return TRUE;
158                         }
159                 }
160         }
161         return FALSE;
162 }
163
164
165
166 static SalStreamDir compute_dir_outgoing(SalStreamDir local, SalStreamDir answered){
167         SalStreamDir res=local;
168         if (local==SalStreamSendRecv){
169                 if (answered==SalStreamRecvOnly){
170                         res=SalStreamSendOnly;
171                 }else if (answered==SalStreamSendOnly){
172                         res=SalStreamRecvOnly;
173                 }
174         }
175         if (answered==SalStreamInactive){
176                 res=SalStreamInactive;
177         }
178         return res;
179 }
180
181 static SalStreamDir compute_dir_incoming(SalStreamDir local, SalStreamDir offered){
182         SalStreamDir res=SalStreamSendRecv;
183         if (local==SalStreamSendRecv){
184                 if (offered==SalStreamSendOnly)
185                         res=SalStreamRecvOnly;
186                 else if (offered==SalStreamRecvOnly)
187                         res=SalStreamSendOnly;
188                 else if (offered==SalStreamInactive)
189                         res=SalStreamInactive;
190                 else
191                         res=SalStreamSendRecv;
192         }else if (local==SalStreamSendOnly){
193                 if (offered==SalStreamRecvOnly || offered==SalStreamSendRecv)
194                         res=SalStreamSendOnly;
195                 else res=SalStreamInactive;
196         }else if (local==SalStreamRecvOnly){
197                 if (offered==SalStreamSendOnly || offered==SalStreamSendRecv)
198                         res=SalStreamRecvOnly;
199                 else
200                         res=SalStreamInactive;
201         }else res=SalStreamInactive;
202         return res;
203 }
204
205 static void initiate_outgoing(const SalStreamDescription *local_offer,
206                                         const SalStreamDescription *remote_answer,
207                                         SalStreamDescription *result){
208         if (remote_answer->rtp_port!=0)
209                 result->payloads=match_payloads(local_offer->payloads,remote_answer->payloads,TRUE,FALSE);
210         result->proto=remote_answer->proto;
211         result->type=local_offer->type;
212         result->dir=compute_dir_outgoing(local_offer->dir,remote_answer->dir);
213
214         if (result->payloads && !only_telephone_event(result->payloads)){
215                 strcpy(result->rtp_addr,remote_answer->rtp_addr);
216                 strcpy(result->rtcp_addr,remote_answer->rtcp_addr);
217                 result->rtp_port=remote_answer->rtp_port;
218                 result->rtcp_port=remote_answer->rtcp_port;
219                 result->bandwidth=remote_answer->bandwidth;
220                 result->ptime=remote_answer->ptime;
221         }else{
222                 result->rtp_port=0;
223         }
224         if (result->proto == SalProtoRtpSavp) {
225                 /* verify crypto algo */
226                 memset(result->crypto, 0, sizeof(result->crypto));
227                 if (!match_crypto_algo(local_offer->crypto, remote_answer->crypto, &result->crypto[0], &result->crypto_local_tag, FALSE))
228                         result->rtp_port = 0;
229         }
230 }
231
232
233 static void initiate_incoming(const SalStreamDescription *local_cap,
234                                         const SalStreamDescription *remote_offer,
235                                         SalStreamDescription *result, bool_t one_matching_codec){
236         result->payloads=match_payloads(local_cap->payloads,remote_offer->payloads, FALSE, one_matching_codec);
237         result->proto=remote_offer->proto;
238         result->type=local_cap->type;
239         result->dir=compute_dir_incoming(local_cap->dir,remote_offer->dir);
240         if (result->payloads && !only_telephone_event(result->payloads) && (remote_offer->rtp_port!=0 || remote_offer->rtp_port==SalStreamSendOnly)){
241                 strcpy(result->rtp_addr,local_cap->rtp_addr);
242                 strcpy(result->rtcp_addr,local_cap->rtcp_addr);
243                 memcpy(result->candidates,local_cap->candidates,sizeof(result->candidates));
244                 result->rtp_port=local_cap->rtp_port;
245                 result->rtcp_port=local_cap->rtcp_port;
246                 result->bandwidth=local_cap->bandwidth;
247                 result->ptime=local_cap->ptime;
248         }else{
249                 result->rtp_port=0;
250         }
251         if (result->proto == SalProtoRtpSavp) {
252                 /* select crypto algo */
253                 memset(result->crypto, 0, sizeof(result->crypto));
254                 if (!match_crypto_algo(local_cap->crypto, remote_offer->crypto, &result->crypto[0], &result->crypto_local_tag, TRUE))
255                         result->rtp_port = 0;
256                 
257         }
258         strcpy(result->ice_pwd, local_cap->ice_pwd);
259         strcpy(result->ice_ufrag, local_cap->ice_ufrag);
260         result->ice_mismatch = local_cap->ice_mismatch;
261         result->ice_completed = local_cap->ice_completed;
262         memcpy(result->ice_candidates, local_cap->ice_candidates, sizeof(result->ice_candidates));
263         memcpy(result->ice_remote_candidates, local_cap->ice_remote_candidates, sizeof(result->ice_remote_candidates));
264 }
265
266 /**
267  * Returns a media description to run the streams with, based on a local offer
268  * and the returned response (remote).
269 **/
270 int offer_answer_initiate_outgoing(const SalMediaDescription *local_offer,
271                                                                         const SalMediaDescription *remote_answer,
272                                                         SalMediaDescription *result){
273         int i,j;
274     
275         const SalStreamDescription *ls,*rs;
276         for(i=0,j=0;i<local_offer->nstreams;++i){
277                 ms_message("Processing for stream %i",i);
278                 ls=&local_offer->streams[i];
279                 rs=sal_media_description_find_stream((SalMediaDescription*)remote_answer,ls->proto,ls->type);
280         if (rs) {
281                         initiate_outgoing(ls,rs,&result->streams[j]);
282                         ++j;
283                 }
284                 else ms_warning("No matching stream for %i",i);
285         }
286         result->nstreams=j;
287         result->bandwidth=remote_answer->bandwidth;
288         strcpy(result->addr,remote_answer->addr);
289         return 0;
290 }
291
292 /**
293  * Returns a media description to run the streams with, based on the local capabilities and
294  * and the received offer.
295  * The returned media description is an answer and should be sent to the offerer.
296 **/
297 int offer_answer_initiate_incoming(const SalMediaDescription *local_capabilities,
298                                                 const SalMediaDescription *remote_offer,
299                                         SalMediaDescription *result, bool_t one_matching_codec){
300         int i;
301         const SalStreamDescription *ls=NULL,*rs;
302                                                         
303         for(i=0;i<remote_offer->nstreams;++i){
304                 rs=&remote_offer->streams[i];
305                 if (rs->proto!=SalProtoUnknown){
306                         ls=sal_media_description_find_stream((SalMediaDescription*)local_capabilities,rs->proto,rs->type);
307                         /* if matching failed, and remote proposes Avp only, ask for local Savp streams */ 
308                         if (!ls && rs->proto == SalProtoRtpAvp) {
309                                 ls=sal_media_description_find_stream((SalMediaDescription*)local_capabilities,SalProtoRtpSavp,rs->type);
310                         }
311                 }else ms_warning("Unknown protocol for mline %i, declining",i);
312                 if (ls){
313                         initiate_incoming(ls,rs,&result->streams[i],one_matching_codec);
314                 }
315                 else {
316                         /* create an inactive stream for the answer, as there where no matching stream a local capability */
317                         result->streams[i].dir=SalStreamInactive;
318                         result->streams[i].rtp_port=0;
319                         result->streams[i].type=rs->type;
320                         result->streams[i].proto=rs->proto;
321                         if (rs->type==SalOther){
322                                 strncpy(result->streams[i].typeother,rs->typeother,sizeof(rs->typeother)-1);
323                         }
324                 }
325         }
326         result->nstreams=i;
327         strcpy(result->username, local_capabilities->username);
328         strcpy(result->addr,local_capabilities->addr);
329         result->bandwidth=local_capabilities->bandwidth;
330         result->session_ver=local_capabilities->session_ver;
331         result->session_id=local_capabilities->session_id;
332         strcpy(result->ice_pwd, local_capabilities->ice_pwd);
333         strcpy(result->ice_ufrag, local_capabilities->ice_ufrag);
334         result->ice_lite = local_capabilities->ice_lite;
335         result->ice_completed = local_capabilities->ice_completed;
336         return 0;
337 }