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 res=ms_list_append(res,newp);
89 /* we should use the remote numbering even when parsing a response */
90 payload_type_set_number(newp,remote_number);
91 if (reading_response && remote_number!=local_number){
92 ms_warning("For payload type %s, proposed number was %i but the remote phone answered %i",
93 newp->mime_type, local_number, remote_number);
95 We must add this payload type with our local numbering in order to be able to receive it.
96 Indeed despite we must sent with the remote numbering, we must be able to receive with
99 newp=payload_type_clone(matched);
100 payload_type_set_number(newp,local_number);
101 res=ms_list_append(res,newp);
104 ms_message("No match for %s/%i",p2->mime_type,p2->clock_rate);
112 static SalStreamDir compute_dir_outgoing(SalStreamDir local, SalStreamDir answered){
113 SalStreamDir res=local;
114 if (local==SalStreamSendRecv){
115 if (answered==SalStreamRecvOnly){
116 res=SalStreamSendOnly;
117 }else if (answered==SalStreamSendOnly){
118 res=SalStreamRecvOnly;
121 if (answered==SalStreamInactive){
122 res=SalStreamInactive;
127 static SalStreamDir compute_dir_incoming(SalStreamDir local, SalStreamDir offered){
128 SalStreamDir res=SalStreamSendRecv;
129 if (local==SalStreamSendRecv){
130 if (offered==SalStreamSendOnly)
131 res=SalStreamRecvOnly;
132 else if (offered==SalStreamRecvOnly)
133 res=SalStreamSendOnly;
134 else if (offered==SalStreamInactive)
135 res=SalStreamInactive;
137 res=SalStreamSendRecv;
138 }else if (local==SalStreamSendOnly){
139 if (offered==SalStreamRecvOnly || offered==SalStreamSendRecv)
140 res=SalStreamSendOnly;
141 else res=SalStreamInactive;
142 }else if (local==SalStreamRecvOnly){
143 if (offered==SalStreamSendOnly || offered==SalStreamSendRecv)
144 res=SalStreamRecvOnly;
146 res=SalStreamInactive;
147 }else res=SalStreamInactive;
151 static void initiate_outgoing(const SalStreamDescription *local_offer,
152 const SalStreamDescription *remote_answer,
153 SalStreamDescription *result){
154 if (remote_answer->port!=0)
155 result->payloads=match_payloads(local_offer->payloads,remote_answer->payloads,TRUE,FALSE);
156 result->proto=local_offer->proto;
157 result->type=local_offer->type;
158 result->dir=compute_dir_outgoing(local_offer->dir,remote_answer->dir);
160 if (result->payloads && !only_telephone_event(result->payloads)){
161 strcpy(result->addr,remote_answer->addr);
162 result->port=remote_answer->port;
163 result->bandwidth=remote_answer->bandwidth;
164 result->ptime=remote_answer->ptime;
171 static void initiate_incoming(const SalStreamDescription *local_cap,
172 const SalStreamDescription *remote_offer,
173 SalStreamDescription *result, bool_t one_matching_codec){
174 result->payloads=match_payloads(local_cap->payloads,remote_offer->payloads, FALSE, one_matching_codec);
175 result->proto=local_cap->proto;
176 result->type=local_cap->type;
177 result->dir=compute_dir_incoming(local_cap->dir,remote_offer->dir);
178 if (result->payloads && !only_telephone_event(result->payloads)){
179 strcpy(result->addr,local_cap->addr);
180 memcpy(result->candidates,local_cap->candidates,sizeof(result->candidates));
181 result->port=local_cap->port;
182 result->bandwidth=local_cap->bandwidth;
183 result->ptime=local_cap->ptime;
190 * Returns a media description to run the streams with, based on a local offer
191 * and the returned response (remote).
193 int offer_answer_initiate_outgoing(const SalMediaDescription *local_offer,
194 const SalMediaDescription *remote_answer,
195 SalMediaDescription *result){
197 const SalStreamDescription *ls,*rs;
198 for(i=0,j=0;i<local_offer->nstreams;++i){
199 ms_message("Processing for stream %i",i);
200 ls=&local_offer->streams[i];
201 rs=sal_media_description_find_stream((SalMediaDescription*)remote_answer,ls->proto,ls->type);
203 initiate_outgoing(ls,rs,&result->streams[j]);
206 else ms_warning("No matching stream for %i",i);
209 result->bandwidth=remote_answer->bandwidth;
210 strcpy(result->addr,remote_answer->addr);
215 * Returns a media description to run the streams with, based on the local capabilities and
216 * and the received offer.
217 * The returned media description is an answer and should be sent to the offerer.
219 int offer_answer_initiate_incoming(const SalMediaDescription *local_capabilities,
220 const SalMediaDescription *remote_offer,
221 SalMediaDescription *result, bool_t one_matching_codec){
223 const SalStreamDescription *ls,*rs;
225 for(i=0;i<remote_offer->nstreams;++i){
226 rs=&remote_offer->streams[i];
227 ms_message("Processing for stream %i",i);
228 ls=sal_media_description_find_stream((SalMediaDescription*)local_capabilities,rs->proto,rs->type);
230 initiate_incoming(ls,rs,&result->streams[i],one_matching_codec);
232 /* create an inactive stream for the answer, as there where no matching stream a local capability */
233 result->streams[i].dir=SalStreamInactive;
234 result->streams[i].port=0;
235 result->streams[i].type=rs->type;
236 if (rs->type==SalOther){
237 strncpy(result->streams[i].typeother,rs->typeother,sizeof(rs->typeother)-1);
242 strcpy(result->username, local_capabilities->username);
243 strcpy(result->addr,local_capabilities->addr);
244 result->bandwidth=local_capabilities->bandwidth;
245 result->session_ver=local_capabilities->session_ver;
246 result->session_id=local_capabilities->session_id;