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 for(;l!=NULL;l=l->next){
25 PayloadType *p=(PayloadType*)l->data;
26 if (strcasecmp(p->mime_type,"telephone-event")!=0){
33 static PayloadType * find_payload_type_best_match(const MSList *l, const PayloadType *refpt){
37 PayloadType *candidate=NULL;
39 for (elem=l;elem!=NULL;elem=elem->next){
40 pt=(PayloadType*)elem->data;
41 /* the compare between G729 and G729A is for some stupid uncompliant phone*/
42 if ( (strcasecmp(pt->mime_type,refpt->mime_type)==0 ||
43 (strcasecmp(pt->mime_type, "G729") == 0 && strcasecmp(refpt->mime_type, "G729A") == 0 ))
44 && pt->clock_rate==refpt->clock_rate){
46 /*good candidate, check fmtp for H264 */
47 if (strcasecmp(pt->mime_type,"H264")==0){
48 if (pt->recv_fmtp!=NULL && refpt->recv_fmtp!=NULL){
50 if (fmtp_get_value(pt->recv_fmtp,"packetization-mode",value,sizeof(value))){
53 if (fmtp_get_value(refpt->recv_fmtp,"packetization-mode",value,sizeof(value))){
57 break; /*exact match */
65 static MSList *match_payloads(const MSList *local, const MSList *remote, bool_t reading_response, bool_t one_matching_codec){
69 bool_t found_codec=FALSE;
71 for(e2=remote;e2!=NULL;e2=e2->next){
72 PayloadType *p2=(PayloadType*)e2->data;
73 matched=find_payload_type_best_match(local,p2);
76 int local_number=payload_type_get_number(matched);
77 int remote_number=payload_type_get_number(p2);
79 if (one_matching_codec){
80 if (strcasecmp(matched->mime_type,"telephone-event")!=0){
81 if (found_codec){/* we have found a real codec already*/
82 continue; /*this codec won't be added*/
83 }else found_codec=TRUE;
87 newp=payload_type_clone(matched);
89 payload_type_set_send_fmtp(newp,p2->send_fmtp);
90 newp->flags|=PAYLOAD_TYPE_FLAG_CAN_RECV|PAYLOAD_TYPE_FLAG_CAN_SEND;
91 res=ms_list_append(res,newp);
92 /* we should use the remote numbering even when parsing a response */
93 payload_type_set_number(newp,remote_number);
94 if (reading_response && remote_number!=local_number){
95 ms_warning("For payload type %s, proposed number was %i but the remote phone answered %i",
96 newp->mime_type, local_number, remote_number);
98 We must add this payload type with our local numbering in order to be able to receive it.
99 Indeed despite we must sent with the remote numbering, we must be able to receive with
102 newp=payload_type_clone(newp);
103 payload_type_set_number(newp,local_number);
104 res=ms_list_append(res,newp);
107 ms_message("No match for %s/%i",p2->mime_type,p2->clock_rate);
110 if (reading_response){
111 /* 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*/
112 for(e1=local;e1!=NULL;e1=e1->next){
113 PayloadType *p1=(PayloadType*)e1->data;
115 for(e2=res;e2!=NULL;e2=e2->next){
116 PayloadType *p2=(PayloadType*)e2->data;
117 if (payload_type_get_number(p2)==payload_type_get_number(p1)){
123 ms_message("Adding %s/%i for compatibility, just in case.",p1->mime_type,p1->clock_rate);
124 p1=payload_type_clone(p1);
125 p1->flags|=PAYLOAD_TYPE_FLAG_CAN_RECV;
126 res=ms_list_append(res,p1);
133 static bool_t match_crypto_algo(const SalSrtpCryptoAlgo* local, const SalSrtpCryptoAlgo* remote,
134 SalSrtpCryptoAlgo* result, unsigned int* choosen_local_tag, bool_t use_local_key) {
136 for(i=0; i<SAL_CRYPTO_ALGO_MAX; i++) {
137 if (remote[i].algo == 0)
140 /* Look for a local enabled crypto algo that matches one of the proposed by remote */
141 for(j=0; j<SAL_CRYPTO_ALGO_MAX; j++) {
142 if (remote[i].algo == local[j].algo) {
143 result->algo = remote[i].algo;
144 /* We're answering an SDP offer. Supply our master key, associated with the remote supplied tag */
146 strncpy(result->master_key, local[j].master_key, 41);
147 result->tag = remote[i].tag;
148 *choosen_local_tag = local[j].tag;
150 /* We received an answer to our SDP crypto proposal. Copy matching algo remote master key to result, and memorize local tag */
152 strncpy(result->master_key, remote[i].master_key, 41);
153 result->tag = local[j].tag;
154 *choosen_local_tag = local[j].tag;
156 result->master_key[40] = '\0';
166 static SalStreamDir compute_dir_outgoing(SalStreamDir local, SalStreamDir answered){
167 SalStreamDir res=local;
168 if (local==SalStreamSendRecv){
169 if (answered==SalStreamRecvOnly){
170 res=SalStreamSendOnly;
171 }else if (answered==SalStreamSendOnly){
172 res=SalStreamRecvOnly;
175 if (answered==SalStreamInactive){
176 res=SalStreamInactive;
181 static SalStreamDir compute_dir_incoming(SalStreamDir local, SalStreamDir offered){
182 SalStreamDir res=SalStreamSendRecv;
183 if (local==SalStreamSendRecv){
184 if (offered==SalStreamSendOnly)
185 res=SalStreamRecvOnly;
186 else if (offered==SalStreamRecvOnly)
187 res=SalStreamSendOnly;
188 else if (offered==SalStreamInactive)
189 res=SalStreamInactive;
191 res=SalStreamSendRecv;
192 }else if (local==SalStreamSendOnly){
193 if (offered==SalStreamRecvOnly || offered==SalStreamSendRecv)
194 res=SalStreamSendOnly;
195 else res=SalStreamInactive;
196 }else if (local==SalStreamRecvOnly){
197 if (offered==SalStreamSendOnly || offered==SalStreamSendRecv)
198 res=SalStreamRecvOnly;
200 res=SalStreamInactive;
201 }else res=SalStreamInactive;
205 static void initiate_outgoing(const SalStreamDescription *local_offer,
206 const SalStreamDescription *remote_answer,
207 SalStreamDescription *result){
208 if (remote_answer->rtp_port!=0)
209 result->payloads=match_payloads(local_offer->payloads,remote_answer->payloads,TRUE,FALSE);
210 result->proto=remote_answer->proto;
211 result->type=local_offer->type;
212 result->dir=compute_dir_outgoing(local_offer->dir,remote_answer->dir);
214 if (result->payloads && !only_telephone_event(result->payloads)){
215 strcpy(result->rtp_addr,remote_answer->rtp_addr);
216 strcpy(result->rtcp_addr,remote_answer->rtcp_addr);
217 result->rtp_port=remote_answer->rtp_port;
218 result->rtcp_port=remote_answer->rtcp_port;
219 result->bandwidth=remote_answer->bandwidth;
220 result->ptime=remote_answer->ptime;
224 if (result->proto == SalProtoRtpSavp) {
225 /* verify crypto algo */
226 memset(result->crypto, 0, sizeof(result->crypto));
227 if (!match_crypto_algo(local_offer->crypto, remote_answer->crypto, &result->crypto[0], &result->crypto_local_tag, FALSE))
228 result->rtp_port = 0;
233 static void initiate_incoming(const SalStreamDescription *local_cap,
234 const SalStreamDescription *remote_offer,
235 SalStreamDescription *result, bool_t one_matching_codec){
236 result->payloads=match_payloads(local_cap->payloads,remote_offer->payloads, FALSE, one_matching_codec);
237 result->proto=remote_offer->proto;
238 result->type=local_cap->type;
239 result->dir=compute_dir_incoming(local_cap->dir,remote_offer->dir);
240 if (result->payloads && !only_telephone_event(result->payloads) && (remote_offer->rtp_port!=0 || remote_offer->rtp_port==SalStreamSendOnly)){
241 strcpy(result->rtp_addr,local_cap->rtp_addr);
242 strcpy(result->rtcp_addr,local_cap->rtcp_addr);
243 result->rtp_port=local_cap->rtp_port;
244 result->rtcp_port=local_cap->rtcp_port;
245 result->bandwidth=local_cap->bandwidth;
246 result->ptime=local_cap->ptime;
250 if (result->proto == SalProtoRtpSavp) {
251 /* select crypto algo */
252 memset(result->crypto, 0, sizeof(result->crypto));
253 if (!match_crypto_algo(local_cap->crypto, remote_offer->crypto, &result->crypto[0], &result->crypto_local_tag, TRUE))
254 result->rtp_port = 0;
257 strcpy(result->ice_pwd, local_cap->ice_pwd);
258 strcpy(result->ice_ufrag, local_cap->ice_ufrag);
259 result->ice_mismatch = local_cap->ice_mismatch;
260 result->ice_completed = local_cap->ice_completed;
261 memcpy(result->ice_candidates, local_cap->ice_candidates, sizeof(result->ice_candidates));
262 memcpy(result->ice_remote_candidates, local_cap->ice_remote_candidates, sizeof(result->ice_remote_candidates));
266 * Returns a media description to run the streams with, based on a local offer
267 * and the returned response (remote).
269 int offer_answer_initiate_outgoing(const SalMediaDescription *local_offer,
270 const SalMediaDescription *remote_answer,
271 SalMediaDescription *result){
274 const SalStreamDescription *ls,*rs;
275 for(i=0,j=0;i<local_offer->nstreams;++i){
276 ms_message("Processing for stream %i",i);
277 ls=&local_offer->streams[i];
278 rs=sal_media_description_find_stream((SalMediaDescription*)remote_answer,ls->proto,ls->type);
280 initiate_outgoing(ls,rs,&result->streams[j]);
283 else ms_warning("No matching stream for %i",i);
286 result->bandwidth=remote_answer->bandwidth;
287 strcpy(result->addr,remote_answer->addr);
292 * Returns a media description to run the streams with, based on the local capabilities and
293 * and the received offer.
294 * The returned media description is an answer and should be sent to the offerer.
296 int offer_answer_initiate_incoming(const SalMediaDescription *local_capabilities,
297 const SalMediaDescription *remote_offer,
298 SalMediaDescription *result, bool_t one_matching_codec){
300 const SalStreamDescription *ls=NULL,*rs;
302 for(i=0;i<remote_offer->nstreams;++i){
303 rs=&remote_offer->streams[i];
304 if (rs->proto!=SalProtoUnknown){
305 ls=sal_media_description_find_stream((SalMediaDescription*)local_capabilities,rs->proto,rs->type);
306 /* if matching failed, and remote proposes Avp only, ask for local Savp streams */
307 if (!ls && rs->proto == SalProtoRtpAvp) {
308 ls=sal_media_description_find_stream((SalMediaDescription*)local_capabilities,SalProtoRtpSavp,rs->type);
310 }else ms_warning("Unknown protocol for mline %i, declining",i);
312 initiate_incoming(ls,rs,&result->streams[i],one_matching_codec);
315 /* create an inactive stream for the answer, as there where no matching stream a local capability */
316 result->streams[i].dir=SalStreamInactive;
317 result->streams[i].rtp_port=0;
318 result->streams[i].type=rs->type;
319 result->streams[i].proto=rs->proto;
320 if (rs->type==SalOther){
321 strncpy(result->streams[i].typeother,rs->typeother,sizeof(rs->typeother)-1);
326 strcpy(result->username, local_capabilities->username);
327 strcpy(result->addr,local_capabilities->addr);
328 result->bandwidth=local_capabilities->bandwidth;
329 result->session_ver=local_capabilities->session_ver;
330 result->session_id=local_capabilities->session_id;
331 strcpy(result->ice_pwd, local_capabilities->ice_pwd);
332 strcpy(result->ice_ufrag, local_capabilities->ice_ufrag);
333 result->ice_lite = local_capabilities->ice_lite;
334 result->ice_completed = local_capabilities->ice_completed;