]> sjero.net Git - linphone/blob - coreapi/offeranswer.c
Handle conversion between media description and ice session.
[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,*e1;
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                         newp->flags|=PAYLOAD_TYPE_FLAG_CAN_RECV|PAYLOAD_TYPE_FLAG_CAN_SEND;
89                         res=ms_list_append(res,newp);
90                         /* we should use the remote numbering even when parsing a response */
91                         payload_type_set_number(newp,remote_number);
92                         if (reading_response && remote_number!=local_number){
93                                 ms_warning("For payload type %s, proposed number was %i but the remote phone answered %i",
94                                           newp->mime_type, local_number, remote_number);
95                                 /*
96                                  We must add this payload type with our local numbering in order to be able to receive it.
97                                  Indeed despite we must sent with the remote numbering, we must be able to receive with
98                                  our local one.
99                                 */
100                                 newp=payload_type_clone(newp);
101                                 payload_type_set_number(newp,local_number);
102                                 res=ms_list_append(res,newp);
103                         }
104                 }else{
105                         ms_message("No match for %s/%i",p2->mime_type,p2->clock_rate);
106                 }
107         }
108         if (reading_response){
109                 /* 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*/
110                 for(e1=local;e1!=NULL;e1=e1->next){
111                         PayloadType *p1=(PayloadType*)e1->data;
112                         bool_t found=FALSE;
113                         for(e2=res;e2!=NULL;e2=e2->next){
114                                 PayloadType *p2=(PayloadType*)e2->data;
115                                 if (payload_type_get_number(p2)==payload_type_get_number(p1)){
116                                         found=TRUE;
117                                         break;
118                                 }
119                         }
120                         if (!found){
121                                 ms_message("Adding %s/%i for compatibility, just in case.",p1->mime_type,p1->clock_rate);
122                                 p1=payload_type_clone(p1);
123                                 p1->flags|=PAYLOAD_TYPE_FLAG_CAN_RECV;
124                                 res=ms_list_append(res,p1);
125                         }
126                 }
127         }
128         return res;
129 }
130
131 static bool_t match_crypto_algo(const SalSrtpCryptoAlgo* local, const SalSrtpCryptoAlgo* remote, 
132         SalSrtpCryptoAlgo* result, unsigned int* choosen_local_tag, bool_t use_local_key) {
133         int i,j;
134         for(i=0; i<SAL_CRYPTO_ALGO_MAX; i++) {
135                 if (remote[i].algo == 0)
136                         break;
137
138         /* Look for a local enabled crypto algo that matches one of the proposed by remote */
139                 for(j=0; j<SAL_CRYPTO_ALGO_MAX; j++) {
140                         if (remote[i].algo == local[j].algo) {
141                                 result->algo = remote[i].algo;
142             /* We're answering an SDP offer. Supply our master key, associated with the remote supplied tag */
143                                 if (use_local_key) {
144                                         strncpy(result->master_key, local[j].master_key, 41);
145                                         result->tag = remote[i].tag;
146                     *choosen_local_tag = local[j].tag;
147                                 }
148                                 /* We received an answer to our SDP crypto proposal. Copy matching algo remote master key to result, and memorize local tag */
149             else {
150                                         strncpy(result->master_key, remote[i].master_key, 41);
151                                         result->tag = local[j].tag;
152                     *choosen_local_tag = local[j].tag;
153                                 }
154                                 result->master_key[40] = '\0';
155                                 return TRUE;
156                         }
157                 }
158         }
159         return FALSE;
160 }
161
162
163
164 static SalStreamDir compute_dir_outgoing(SalStreamDir local, SalStreamDir answered){
165         SalStreamDir res=local;
166         if (local==SalStreamSendRecv){
167                 if (answered==SalStreamRecvOnly){
168                         res=SalStreamSendOnly;
169                 }else if (answered==SalStreamSendOnly){
170                         res=SalStreamRecvOnly;
171                 }
172         }
173         if (answered==SalStreamInactive){
174                 res=SalStreamInactive;
175         }
176         return res;
177 }
178
179 static SalStreamDir compute_dir_incoming(SalStreamDir local, SalStreamDir offered){
180         SalStreamDir res=SalStreamSendRecv;
181         if (local==SalStreamSendRecv){
182                 if (offered==SalStreamSendOnly)
183                         res=SalStreamRecvOnly;
184                 else if (offered==SalStreamRecvOnly)
185                         res=SalStreamSendOnly;
186                 else if (offered==SalStreamInactive)
187                         res=SalStreamInactive;
188                 else
189                         res=SalStreamSendRecv;
190         }else if (local==SalStreamSendOnly){
191                 if (offered==SalStreamRecvOnly || offered==SalStreamSendRecv)
192                         res=SalStreamSendOnly;
193                 else res=SalStreamInactive;
194         }else if (local==SalStreamRecvOnly){
195                 if (offered==SalStreamSendOnly || offered==SalStreamSendRecv)
196                         res=SalStreamRecvOnly;
197                 else
198                         res=SalStreamInactive;
199         }else res=SalStreamInactive;
200         return res;
201 }
202
203 static void initiate_outgoing(const SalStreamDescription *local_offer,
204                                         const SalStreamDescription *remote_answer,
205                                         SalStreamDescription *result){
206         if (remote_answer->rtp_port!=0)
207                 result->payloads=match_payloads(local_offer->payloads,remote_answer->payloads,TRUE,FALSE);
208         result->proto=remote_answer->proto;
209         result->type=local_offer->type;
210         result->dir=compute_dir_outgoing(local_offer->dir,remote_answer->dir);
211
212         if (result->payloads && !only_telephone_event(result->payloads)){
213                 strcpy(result->rtp_addr,remote_answer->rtp_addr);
214                 strcpy(result->rtcp_addr,remote_answer->rtcp_addr);
215                 result->rtp_port=remote_answer->rtp_port;
216                 result->rtcp_port=remote_answer->rtcp_port;
217                 result->bandwidth=remote_answer->bandwidth;
218                 result->ptime=remote_answer->ptime;
219         }else{
220                 result->rtp_port=0;
221         }
222         if (result->proto == SalProtoRtpSavp) {
223                 /* verify crypto algo */
224                 memset(result->crypto, 0, sizeof(result->crypto));
225                 if (!match_crypto_algo(local_offer->crypto, remote_answer->crypto, &result->crypto[0], &result->crypto_local_tag, FALSE))
226                         result->rtp_port = 0;
227         }
228 }
229
230
231 static void initiate_incoming(const SalStreamDescription *local_cap,
232                                         const SalStreamDescription *remote_offer,
233                                         SalStreamDescription *result, bool_t one_matching_codec){
234         result->payloads=match_payloads(local_cap->payloads,remote_offer->payloads, FALSE, one_matching_codec);
235         result->proto=remote_offer->proto;
236         result->type=local_cap->type;
237         result->dir=compute_dir_incoming(local_cap->dir,remote_offer->dir);
238         if (result->payloads && !only_telephone_event(result->payloads) && (remote_offer->rtp_port!=0 || remote_offer->rtp_port==SalStreamSendOnly)){
239                 strcpy(result->rtp_addr,local_cap->rtp_addr);
240                 strcpy(result->rtcp_addr,local_cap->rtcp_addr);
241                 memcpy(result->candidates,local_cap->candidates,sizeof(result->candidates));
242                 result->rtp_port=local_cap->rtp_port;
243                 result->rtcp_port=local_cap->rtcp_port;
244                 result->bandwidth=local_cap->bandwidth;
245                 result->ptime=local_cap->ptime;
246         }else{
247                 result->rtp_port=0;
248         }
249         if (result->proto == SalProtoRtpSavp) {
250                 /* select crypto algo */
251                 memset(result->crypto, 0, sizeof(result->crypto));
252                 if (!match_crypto_algo(local_cap->crypto, remote_offer->crypto, &result->crypto[0], &result->crypto_local_tag, TRUE))
253                         result->rtp_port = 0;
254                 
255         }
256         strcpy(result->ice_pwd, local_cap->ice_pwd);
257         strcpy(result->ice_ufrag, local_cap->ice_ufrag);
258         result->ice_mismatch = local_cap->ice_mismatch;
259         memcpy(result->ice_candidates, local_cap->ice_candidates, sizeof(result->ice_candidates));
260         memcpy(result->ice_remote_candidates, local_cap->ice_remote_candidates, sizeof(result->ice_remote_candidates));
261 }
262
263 /**
264  * Returns a media description to run the streams with, based on a local offer
265  * and the returned response (remote).
266 **/
267 int offer_answer_initiate_outgoing(const SalMediaDescription *local_offer,
268                                                                         const SalMediaDescription *remote_answer,
269                                                         SalMediaDescription *result){
270         int i,j;
271     
272         const SalStreamDescription *ls,*rs;
273         for(i=0,j=0;i<local_offer->nstreams;++i){
274                 ms_message("Processing for stream %i",i);
275                 ls=&local_offer->streams[i];
276                 rs=sal_media_description_find_stream((SalMediaDescription*)remote_answer,ls->proto,ls->type);
277         if (rs) {
278                         initiate_outgoing(ls,rs,&result->streams[j]);
279                         ++j;
280                 }
281                 else ms_warning("No matching stream for %i",i);
282         }
283         result->nstreams=j;
284         result->bandwidth=remote_answer->bandwidth;
285         strcpy(result->addr,remote_answer->addr);
286         return 0;
287 }
288
289 /**
290  * Returns a media description to run the streams with, based on the local capabilities and
291  * and the received offer.
292  * The returned media description is an answer and should be sent to the offerer.
293 **/
294 int offer_answer_initiate_incoming(const SalMediaDescription *local_capabilities,
295                                                 const SalMediaDescription *remote_offer,
296                                         SalMediaDescription *result, bool_t one_matching_codec){
297         int i;
298         const SalStreamDescription *ls=NULL,*rs;
299                                                         
300         for(i=0;i<remote_offer->nstreams;++i){
301                 rs=&remote_offer->streams[i];
302                 if (rs->proto!=SalProtoUnknown){
303                         ls=sal_media_description_find_stream((SalMediaDescription*)local_capabilities,rs->proto,rs->type);
304                         /* if matching failed, and remote proposes Avp only, ask for local Savp streams */ 
305                         if (!ls && rs->proto == SalProtoRtpAvp) {
306                                 ls=sal_media_description_find_stream((SalMediaDescription*)local_capabilities,SalProtoRtpSavp,rs->type);
307                         }
308                 }else ms_warning("Unknown protocol for mline %i, declining",i);
309                 if (ls){
310                         initiate_incoming(ls,rs,&result->streams[i],one_matching_codec);
311                 }
312                 else {
313                         /* create an inactive stream for the answer, as there where no matching stream a local capability */
314                         result->streams[i].dir=SalStreamInactive;
315                         result->streams[i].rtp_port=0;
316                         result->streams[i].type=rs->type;
317                         result->streams[i].proto=rs->proto;
318                         if (rs->type==SalOther){
319                                 strncpy(result->streams[i].typeother,rs->typeother,sizeof(rs->typeother)-1);
320                         }
321                 }
322         }
323         result->nstreams=i;
324         strcpy(result->username, local_capabilities->username);
325         strcpy(result->addr,local_capabilities->addr);
326         result->bandwidth=local_capabilities->bandwidth;
327         result->session_ver=local_capabilities->session_ver;
328         result->session_id=local_capabilities->session_id;
329         strcpy(result->ice_pwd, local_capabilities->ice_pwd);
330         strcpy(result->ice_ufrag, local_capabilities->ice_ufrag);
331         result->ice_lite = local_capabilities->ice_lite;
332         result->ice_completed = local_capabilities->ice_lite;
333         return 0;
334 }