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, bool_t use_local_key) {
134 for(i=0; i<SAL_CRYPTO_ALGO_MAX; i++) {
135 if (remote[i].algo == 0)
138 for(j=0; j<SAL_CRYPTO_ALGO_MAX; j++) {
139 if (remote[i].algo == local[j].algo) {
140 result->algo = remote[i].algo;
142 strncpy(result->master_key, local[j].master_key, 41);
143 result->tag = local[j].tag;
145 strncpy(result->master_key, remote[i].master_key, 41);
146 result->tag = remote[i].tag;
148 result->master_key[40] = '\0';
158 static SalStreamDir compute_dir_outgoing(SalStreamDir local, SalStreamDir answered){
159 SalStreamDir res=local;
160 if (local==SalStreamSendRecv){
161 if (answered==SalStreamRecvOnly){
162 res=SalStreamSendOnly;
163 }else if (answered==SalStreamSendOnly){
164 res=SalStreamRecvOnly;
167 if (answered==SalStreamInactive){
168 res=SalStreamInactive;
173 static SalStreamDir compute_dir_incoming(SalStreamDir local, SalStreamDir offered){
174 SalStreamDir res=SalStreamSendRecv;
175 if (local==SalStreamSendRecv){
176 if (offered==SalStreamSendOnly)
177 res=SalStreamRecvOnly;
178 else if (offered==SalStreamRecvOnly)
179 res=SalStreamSendOnly;
180 else if (offered==SalStreamInactive)
181 res=SalStreamInactive;
183 res=SalStreamSendRecv;
184 }else if (local==SalStreamSendOnly){
185 if (offered==SalStreamRecvOnly || offered==SalStreamSendRecv)
186 res=SalStreamSendOnly;
187 else res=SalStreamInactive;
188 }else if (local==SalStreamRecvOnly){
189 if (offered==SalStreamSendOnly || offered==SalStreamSendRecv)
190 res=SalStreamRecvOnly;
192 res=SalStreamInactive;
193 }else res=SalStreamInactive;
197 static void initiate_outgoing(const SalStreamDescription *local_offer,
198 const SalStreamDescription *remote_answer,
199 SalStreamDescription *result){
200 if (remote_answer->port!=0)
201 result->payloads=match_payloads(local_offer->payloads,remote_answer->payloads,TRUE,FALSE);
202 result->proto=remote_answer->proto;
203 result->type=local_offer->type;
204 result->dir=compute_dir_outgoing(local_offer->dir,remote_answer->dir);
206 if (result->payloads && !only_telephone_event(result->payloads)){
207 strcpy(result->addr,remote_answer->addr);
208 result->port=remote_answer->port;
209 result->bandwidth=remote_answer->bandwidth;
210 result->ptime=remote_answer->ptime;
214 if (result->proto == SalProtoRtpSavp) {
215 /* verify crypto algo */
216 memset(result->crypto, 0, sizeof(result->crypto));
217 if (!match_crypto_algo(local_offer->crypto, remote_answer->crypto, &result->crypto[0], FALSE))
223 static void initiate_incoming(const SalStreamDescription *local_cap,
224 const SalStreamDescription *remote_offer,
225 SalStreamDescription *result, bool_t one_matching_codec){
226 result->payloads=match_payloads(local_cap->payloads,remote_offer->payloads, FALSE, one_matching_codec);
227 result->proto=remote_offer->proto;
228 result->type=local_cap->type;
229 result->dir=compute_dir_incoming(local_cap->dir,remote_offer->dir);
230 if (result->payloads && !only_telephone_event(result->payloads)){
231 strcpy(result->addr,local_cap->addr);
232 memcpy(result->candidates,local_cap->candidates,sizeof(result->candidates));
233 result->port=local_cap->port;
234 result->bandwidth=local_cap->bandwidth;
235 result->ptime=local_cap->ptime;
239 if (result->proto == SalProtoRtpSavp) {
240 /* select crypto algo */
241 memset(result->crypto, 0, sizeof(result->crypto));
242 if (!match_crypto_algo(local_cap->crypto, remote_offer->crypto, &result->crypto[0], TRUE))
249 * Returns a media description to run the streams with, based on a local offer
250 * and the returned response (remote).
252 int offer_answer_initiate_outgoing(const SalMediaDescription *local_offer,
253 const SalMediaDescription *remote_answer,
254 SalMediaDescription *result){
257 const SalStreamDescription *ls,*rs;
258 for(i=0,j=0;i<local_offer->nstreams;++i){
259 ms_message("Processing for stream %i",i);
260 ls=&local_offer->streams[i];
261 rs=sal_media_description_find_stream((SalMediaDescription*)remote_answer,ls->proto,ls->type);
263 initiate_outgoing(ls,rs,&result->streams[j]);
266 else ms_warning("No matching stream for %i",i);
269 result->bandwidth=remote_answer->bandwidth;
270 strcpy(result->addr,remote_answer->addr);
275 * Returns a media description to run the streams with, based on the local capabilities and
276 * and the received offer.
277 * The returned media description is an answer and should be sent to the offerer.
279 int offer_answer_initiate_incoming(const SalMediaDescription *local_capabilities,
280 const SalMediaDescription *remote_offer,
281 SalMediaDescription *result, bool_t one_matching_codec){
283 const SalStreamDescription *ls,*rs;
285 for(i=0;i<remote_offer->nstreams;++i){
286 rs=&remote_offer->streams[i];
287 ms_message("Processing for stream %i",i);
289 ls=sal_media_description_find_stream((SalMediaDescription*)local_capabilities,rs->proto,rs->type);
290 ms_message("remote proto: %s => %p", (rs->proto == SalProtoRtpAvp)?"AVP":"SAVP", ls);
291 /* if matching failed, and remote proposes Avp only, ask for local Savp streams */
292 if (!ls && rs->proto == SalProtoRtpAvp) {
293 ls=sal_media_description_find_stream((SalMediaDescription*)local_capabilities,SalProtoRtpSavp,rs->type);
294 ms_message("retry with AVP => %p", ls);
297 initiate_incoming(ls,rs,&result->streams[i],one_matching_codec);
300 /* create an inactive stream for the answer, as there where no matching stream a local capability */
301 result->streams[i].dir=SalStreamInactive;
302 result->streams[i].port=0;
303 result->streams[i].type=rs->type;
304 result->streams[i].proto=rs->proto;
305 if (rs->type==SalOther){
306 strncpy(result->streams[i].typeother,rs->typeother,sizeof(rs->typeother)-1);
311 strcpy(result->username, local_capabilities->username);
312 strcpy(result->addr,local_capabilities->addr);
313 result->bandwidth=local_capabilities->bandwidth;
314 result->session_ver=local_capabilities->session_ver;
315 result->session_id=local_capabilities->session_id;