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"
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){
31 static PayloadType * find_payload_type_best_match(const MSList *l, const PayloadType *refpt){
35 PayloadType *candidate=NULL;
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){
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){
48 if (fmtp_get_value(pt->recv_fmtp,"packetization-mode",value,sizeof(value))){
51 if (fmtp_get_value(refpt->recv_fmtp,"packetization-mode",value,sizeof(value))){
55 break; /*exact match */
63 static MSList *match_payloads(const MSList *local, const MSList *remote, bool_t reading_response, bool_t one_matching_codec){
67 bool_t found_codec=FALSE;
69 for(e2=remote;e2!=NULL;e2=e2->next){
70 PayloadType *p2=(PayloadType*)e2->data;
71 matched=find_payload_type_best_match(local,p2);
74 int local_number=payload_type_get_number(matched);
75 int remote_number=payload_type_get_number(p2);
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;
85 newp=payload_type_clone(matched);
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);
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
100 newp=payload_type_clone(newp);
101 payload_type_set_number(newp,local_number);
102 res=ms_list_append(res,newp);
105 ms_message("No match for %s/%i",p2->mime_type,p2->clock_rate);
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;
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)){
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);
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) {
134 for(i=0; i<SAL_CRYPTO_ALGO_MAX; i++) {
135 if (remote[i].algo == 0)
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 */
144 strncpy(result->master_key, local[j].master_key, 41);
145 result->tag = remote[i].tag;
146 *choosen_local_tag = local[j].tag;
148 /* We received an answer to our SDP crypto proposal. Copy matching algo remote master key to result, and memorize local tag */
150 strncpy(result->master_key, remote[i].master_key, 41);
151 result->tag = local[j].tag;
152 *choosen_local_tag = local[j].tag;
154 result->master_key[40] = '\0';
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;
173 if (answered==SalStreamInactive){
174 res=SalStreamInactive;
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;
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;
198 res=SalStreamInactive;
199 }else res=SalStreamInactive;
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);
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;
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;
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;
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;
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));
264 * Returns a media description to run the streams with, based on a local offer
265 * and the returned response (remote).
267 int offer_answer_initiate_outgoing(const SalMediaDescription *local_offer,
268 const SalMediaDescription *remote_answer,
269 SalMediaDescription *result){
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);
278 initiate_outgoing(ls,rs,&result->streams[j]);
281 else ms_warning("No matching stream for %i",i);
284 result->bandwidth=remote_answer->bandwidth;
285 strcpy(result->addr,remote_answer->addr);
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.
294 int offer_answer_initiate_incoming(const SalMediaDescription *local_capabilities,
295 const SalMediaDescription *remote_offer,
296 SalMediaDescription *result, bool_t one_matching_codec){
298 const SalStreamDescription *ls=NULL,*rs;
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);
308 }else ms_warning("Unknown protocol for mline %i, declining",i);
310 initiate_incoming(ls,rs,&result->streams[i],one_matching_codec);
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);
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;