3 Copyright (C) 2010 Simon MORLAT (simon.morlat@free.fr)
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.
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.
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.
21 #include "offeranswer.h"
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){
34 static PayloadType * find_payload_type_best_match(const MSList *l, const PayloadType *refpt){
38 PayloadType *candidate=NULL;
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){
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){
51 if (fmtp_get_value(pt->recv_fmtp,"packetization-mode",value,sizeof(value))){
54 if (fmtp_get_value(refpt->recv_fmtp,"packetization-mode",value,sizeof(value))){
58 break; /*exact match */
66 static MSList *match_payloads(const MSList *local, const MSList *remote, bool_t reading_response, bool_t one_matching_codec){
70 bool_t found_codec=FALSE;
72 for(e2=remote;e2!=NULL;e2=e2->next){
73 PayloadType *p2=(PayloadType*)e2->data;
74 matched=find_payload_type_best_match(local,p2);
77 int local_number=payload_type_get_number(matched);
78 int remote_number=payload_type_get_number(p2);
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;
88 newp=payload_type_clone(matched);
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);
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
103 newp=payload_type_clone(newp);
104 payload_type_set_number(newp,local_number);
105 res=ms_list_append(res,newp);
108 ms_message("No match for %s/%i",p2->mime_type,p2->clock_rate);
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;
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)){
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);
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) {
137 for(i=0; i<SAL_CRYPTO_ALGO_MAX; i++) {
138 if (remote[i].algo == 0)
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 */
147 strncpy(result->master_key, local[j].master_key, 41);
148 result->tag = remote[i].tag;
149 *choosen_local_tag = local[j].tag;
151 /* We received an answer to our SDP crypto proposal. Copy matching algo remote master key to result, and memorize local tag */
153 strncpy(result->master_key, remote[i].master_key, 41);
154 result->tag = local[j].tag;
155 *choosen_local_tag = local[j].tag;
157 result->master_key[40] = '\0';
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;
176 if (answered==SalStreamInactive){
177 res=SalStreamInactive;
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;
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;
201 res=SalStreamInactive;
202 }else res=SalStreamInactive;
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);
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;
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;
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;
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;
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));
267 * Returns a media description to run the streams with, based on a local offer
268 * and the returned response (remote).
270 int offer_answer_initiate_outgoing(const SalMediaDescription *local_offer,
271 const SalMediaDescription *remote_answer,
272 SalMediaDescription *result){
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);
281 initiate_outgoing(ls,rs,&result->streams[j]);
284 else ms_warning("No matching stream for %i",i);
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);
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.
298 int offer_answer_initiate_incoming(const SalMediaDescription *local_capabilities,
299 const SalMediaDescription *remote_offer,
300 SalMediaDescription *result, bool_t one_matching_codec){
302 const SalStreamDescription *ls=NULL,*rs;
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);
313 }else ms_warning("Unknown protocol for mline %i, declining",i);
315 initiate_incoming(ls,rs,&result->streams[i],one_matching_codec);
316 if (result->streams[i].rtp_port!=0) result->n_active_streams++;
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);
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;