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