]> sjero.net Git - linphone/blob - coreapi/offeranswer.c
baf84287ad8387535823b09b5ab67ed7323b35d0
[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
24 static PayloadType * find_payload_type_best_match(const MSList *l, const PayloadType *refpt){
25         PayloadType *pt;
26         char value[10];
27         const MSList *elem;
28         PayloadType *candidate=NULL;
29
30         for (elem=l;elem!=NULL;elem=elem->next){
31                 pt=(PayloadType*)elem->data;
32                 /* the compare between G729 and G729A is for some stupid uncompliant phone*/
33                 if ( (strcasecmp(pt->mime_type,refpt->mime_type)==0  ||
34                     (strcasecmp(pt->mime_type, "G729") == 0 && strcasecmp(refpt->mime_type, "G729A") == 0 ))
35                         && pt->clock_rate==refpt->clock_rate){
36                         candidate=pt;
37                         /*good candidate, check fmtp for H264 */
38                         if (strcasecmp(pt->mime_type,"H264")==0){
39                                 if (pt->recv_fmtp!=NULL && refpt->recv_fmtp!=NULL){
40                                         int mode1=0,mode2=0;
41                                         if (fmtp_get_value(pt->recv_fmtp,"packetization-mode",value,sizeof(value))){
42                                                 mode1=atoi(value);
43                                         }
44                                         if (fmtp_get_value(refpt->recv_fmtp,"packetization-mode",value,sizeof(value))){
45                                                 mode2=atoi(value);
46                                         }
47                                         if (mode1==mode2)
48                                             break; /*exact match */
49                                 }
50                         }else break;
51                 }
52         }
53         return candidate;
54 }
55
56 static MSList *match_payloads(const MSList *local, const MSList *remote, bool_t reading_response){
57         const MSList *e2;
58         MSList *res=NULL;
59         PayloadType *matched;
60         for(e2=remote;e2!=NULL;e2=e2->next){
61                 PayloadType *p2=(PayloadType*)e2->data;
62                 matched=find_payload_type_best_match(local,p2);
63                 if (matched){
64                         PayloadType *newp;
65                         int local_number=payload_type_get_number(matched);
66                         int remote_number=payload_type_get_number(p2);
67                         
68                         newp=payload_type_clone(matched);
69                         if (p2->send_fmtp)
70                                 payload_type_set_send_fmtp(newp,p2->send_fmtp);
71                         res=ms_list_append(res,newp);
72                         /* we should use the remote numbering even when parsing a response */
73                         payload_type_set_number(newp,remote_number);
74                         if (reading_response && remote_number!=local_number){
75                                 ms_warning("For payload type %s, proposed number was %i but the remote phone answered %i",
76                                           newp->mime_type, local_number, remote_number);
77                                 /*
78                                  We must add this payload type with our local numbering in order to be able to receive it.
79                                  Indeed despite we must sent with the remote numbering, we must be able to receive with
80                                  our local one.
81                                 */
82                                 newp=payload_type_clone(matched);
83                                 payload_type_set_number(newp,local_number);
84                                 res=ms_list_append(res,newp);
85                         }
86                 }else{
87                         ms_message("No match for %s/%i",p2->mime_type,p2->clock_rate);
88                 }
89         }
90         return res;
91 }
92
93 static bool_t only_telephone_event(const MSList *l){
94         PayloadType *p=(PayloadType*)l->data;
95         if (strcasecmp(p->mime_type,"telephone-event")!=0){
96                 return FALSE;
97         }
98         return TRUE;
99 }
100
101 static SalStreamDir compute_dir(SalStreamDir local, SalStreamDir answered){
102         SalStreamDir res=local;
103         if (local==SalStreamSendRecv){
104                 if (answered==SalStreamRecvOnly){
105                         res=SalStreamSendOnly;
106                 }else if (answered==SalStreamSendOnly){
107                         res=SalStreamRecvOnly;
108                 }
109         }
110         if (answered==SalStreamInactive){
111                 res=SalStreamInactive;
112         }
113         return res;
114 }
115
116 static void initiate_outgoing(const SalStreamDescription *local_offer,
117                                         const SalStreamDescription *remote_answer,
118                                         SalStreamDescription *result){
119         if (remote_answer->port!=0)
120                 result->payloads=match_payloads(local_offer->payloads,remote_answer->payloads,TRUE);
121         result->proto=local_offer->proto;
122         result->type=local_offer->type;
123         result->dir=compute_dir(local_offer->dir,remote_answer->dir);
124
125         if (result->payloads && !only_telephone_event(result->payloads)){
126                 strcpy(result->addr,remote_answer->addr);
127                 result->port=remote_answer->port;
128                 result->bandwidth=remote_answer->bandwidth;
129                 result->ptime=remote_answer->ptime;
130         }else{
131                 result->port=0;
132         }
133 }
134
135
136 static void initiate_incoming(const SalStreamDescription *local_cap,
137                                         const SalStreamDescription *remote_offer,
138                                         SalStreamDescription *result){
139         result->payloads=match_payloads(local_cap->payloads,remote_offer->payloads, FALSE);
140         result->proto=local_cap->proto;
141         result->type=local_cap->type;
142         if (remote_offer->dir==SalStreamSendOnly)
143                 result->dir=SalStreamRecvOnly;
144         else if (remote_offer->dir==SalStreamRecvOnly){
145                 result->dir=SalStreamSendOnly;
146         }else if (remote_offer->dir==SalStreamInactive){
147                 result->dir=SalStreamInactive;
148         }else result->dir=SalStreamSendRecv;
149         if (result->payloads && !only_telephone_event(result->payloads)){
150                 strcpy(result->addr,local_cap->addr);
151                 result->port=local_cap->port;
152                 result->bandwidth=local_cap->bandwidth;
153                 result->ptime=local_cap->ptime;         
154         }else{
155                 result->port=0;
156         }
157 }
158
159 /**
160  * Returns a media description to run the streams with, based on a local offer
161  * and the returned response (remote).
162 **/
163 int offer_answer_initiate_outgoing(const SalMediaDescription *local_offer,
164                                                                         const SalMediaDescription *remote_answer,
165                                                         SalMediaDescription *result){
166     int i,j;
167         const SalStreamDescription *ls,*rs;
168     for(i=0,j=0;i<local_offer->nstreams;++i){
169                 ms_message("Processing for stream %i",i);
170                 ls=&local_offer->streams[i];
171                 rs=sal_media_description_find_stream((SalMediaDescription*)remote_answer,ls->proto,ls->type);
172         if (rs) {
173                         initiate_outgoing(ls,rs,&result->streams[j]);
174                         ++j;
175                 }
176                 else ms_warning("No matching stream for %i",i);
177     }
178         result->nstreams=j;
179         strcpy(result->addr,remote_answer->addr);
180         return 0;
181 }
182
183 /**
184  * Returns a media description to run the streams with, based on the local capabilities and
185  * and the received offer.
186  * The returned media description is an answer and should be sent to the offerer.
187 **/
188 int offer_answer_initiate_incoming(const SalMediaDescription *local_capabilities,
189                                                 const SalMediaDescription *remote_offer,
190                                         SalMediaDescription *result){
191     int i,j;
192         const SalStreamDescription *ls,*rs;
193                                                         
194     for(i=0,j=0;i<remote_offer->nstreams;++i){
195                 rs=&remote_offer->streams[i];
196                 ms_message("Processing for stream %i",i);
197                 ls=sal_media_description_find_stream((SalMediaDescription*)local_capabilities,rs->proto,rs->type);
198                 if (ls){
199                 initiate_incoming(ls,rs,&result->streams[j]);
200                         ++j;
201                 }
202     }
203         result->nstreams=j;
204         strcpy(result->username, local_capabilities->username);
205         strcpy(result->addr,local_capabilities->addr);
206         return 0;
207 }
208