]> sjero.net Git - linphone/blob - linphone/oRTP/src/telephonyevents.c
538bdba4e8447567b4e3a62cf14f2c757abf6fc0
[linphone] / linphone / oRTP / src / telephonyevents.c
1 /*
2   The oRTP library is an RTP (Realtime Transport Protocol - rfc1889) stack.
3   Copyright (C) 2001  Simon MORLAT simon.morlat@linphone.org
4
5   This library is free software; you can redistribute it and/or
6   modify it under the terms of the GNU Lesser General Public
7   License as published by the Free Software Foundation; either
8   version 2.1 of the License, or (at your option) any later version.
9
10   This library 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 GNU
13   Lesser General Public License for more details.
14
15   You should have received a copy of the GNU Lesser General Public
16   License along with this library; if not, write to the Free Software
17   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18 */
19
20 #include <ortp/telephonyevents.h>
21 #include "utils.h"
22 #include "rtpsession_priv.h"
23 #include <ortp/ortp.h>
24
25 PayloadType     payload_type_telephone_event={
26         PAYLOAD_AUDIO_PACKETIZED, /*type */
27         8000,   /*clock rate */
28         0,              /* bytes per sample N/A */
29         NULL,   /* zero pattern N/A*/
30         0,              /*pattern_length N/A */
31         0,              /*      normal_bitrate */
32         "telephone-event",      /* MIME subtype */
33         0,              /* Audio Channels N/A */
34         0               /*flags */
35 };
36
37 /**
38  * Tells whether telephony events payload type is supported within the 
39  * context of the rtp session.
40  * @param session a rtp session 
41  *
42  * @return the payload type number used for telephony events if found, -1 if not found.
43 **/
44 int rtp_session_telephone_events_supported(RtpSession *session)
45 {
46         /* search for a telephony event payload in the current profile */
47         session->snd.telephone_events_pt=rtp_profile_get_payload_number_from_mime(session->snd.profile,"telephone-event");
48         session->rcv.telephone_events_pt=rtp_profile_get_payload_number_from_mime(session->rcv.profile,"telephone-event");
49         /*printf("Telephone event pt is %i\n",session->telephone_events_pt);*/
50         return session->snd.telephone_events_pt;
51 }
52
53
54 /**
55  * Tells whether telephone event payload type is supported for send within the 
56  * context of the rtp session.
57  * @param session a rtp session 
58  *
59  * @return the payload type number used for telephony events if found, -1 if not found.
60 **/
61 int rtp_session_send_telephone_events_supported(RtpSession *session)
62 {
63         /* search for a telephony event payload in the current profile */
64         session->snd.telephone_events_pt=rtp_profile_get_payload_number_from_mime(session->snd.profile,"telephone-event");
65         /*printf("Telephone event pt is %i\n",session->telephone_events_pt);*/
66         return session->snd.telephone_events_pt;
67 }
68
69 /**
70  * Tells whether telephone event payload type is supported for receiving within the 
71  * context of the rtp session.
72  * @param session a rtp session 
73  *
74  * @return the payload type number used for telephony events if found, -1 if not found.
75 **/int rtp_session_recv_telephone_events_supported(RtpSession *session)
76 {
77         /* search for a telephony event payload in the current profile */
78         session->rcv.telephone_events_pt=rtp_profile_get_payload_number_from_mime(session->rcv.profile,"telephone-event");
79         /*printf("Telephone event pt is %i\n",session->telephone_events_pt);*/
80         return session->snd.telephone_events_pt;
81 }
82
83
84 /**
85  *      Allocates a new rtp packet to be used to add named telephony events. The application can use
86  *      then rtp_session_add_telephone_event() to add named events to the packet.
87  *      Finally the packet has to be sent with rtp_session_sendm_with_ts().
88  *
89  * @param session a rtp session.
90  * @param start boolean to indicate if the marker bit should be set.
91  *
92  * @return a message block containing the rtp packet if successfull, NULL if the rtp session
93  *cannot support telephony event (because the rtp profile it is bound to does not include
94  *a telephony event payload type).
95 **/
96 mblk_t  *rtp_session_create_telephone_event_packet(RtpSession *session, int start)
97 {
98         mblk_t *mp;
99         rtp_header_t *rtp;
100         
101         return_val_if_fail(session->snd.telephone_events_pt!=-1,NULL);
102         
103         mp=allocb(RTP_FIXED_HEADER_SIZE+TELEPHONY_EVENTS_ALLOCATED_SIZE,BPRI_MED);
104         if (mp==NULL) return NULL;
105         rtp=(rtp_header_t*)mp->b_rptr;
106         rtp->version = 2;
107         rtp->markbit=start;
108         rtp->padbit = 0;
109         rtp->extbit = 0;
110         rtp->cc = 0;
111         rtp->ssrc = session->snd.ssrc;
112         /* timestamp set later, when packet is sended */
113         /*seq number set later, when packet is sended */
114         
115         /*set the payload type */
116         rtp->paytype=session->snd.telephone_events_pt;
117         
118         /*copy the payload */
119         mp->b_wptr+=RTP_FIXED_HEADER_SIZE;
120         return mp;
121 }
122
123
124 /**
125  *@param session a rtp session.
126  *@param packet a rtp packet as a mblk_t
127  *@param event the event type as described in rfc2833, ie one of the TEV_* macros.
128  *@param end a boolean to indicate if the end bit should be set. (end of tone)
129  *@param volume the volume of the telephony tone, as described in rfc2833
130  *@param duration the duration of the telephony tone, in timestamp unit.
131  *
132  * Adds a named telephony event to a rtp packet previously allocated using
133  * rtp_session_create_telephone_event_packet().
134  *
135  *@return 0 on success.
136 **/
137 int rtp_session_add_telephone_event(RtpSession *session,
138                         mblk_t *packet, uint8_t event, int end, uint8_t volume, uint16_t duration)
139 {
140         mblk_t *mp=packet;
141         telephone_event_t *event_hdr;
142
143
144         /* find the place where to add the new telephony event to the packet */
145         while(mp->b_cont!=NULL) mp=mp->b_cont;
146         /* see if we need to allocate a new mblk_t */
147         if ( ( mp->b_wptr) >= (mp->b_datap->db_lim)){
148                 mblk_t *newm=allocb(TELEPHONY_EVENTS_ALLOCATED_SIZE,BPRI_MED);
149                 mp->b_cont=newm;
150                 mp=mp->b_cont;
151         }
152         if (mp==NULL) return -1;
153         event_hdr=(telephone_event_t*)mp->b_wptr;
154         event_hdr->event=event;
155         event_hdr->R=0;
156         event_hdr->E=end;
157         event_hdr->volume=volume;
158         event_hdr->duration=htons(duration);
159         mp->b_wptr+=sizeof(telephone_event_t);
160         return 0;
161 }
162 /**
163  *      This functions creates telephony events packets for dtmf and sends them.
164  *      It uses rtp_session_create_telephone_event_packet() and
165  *      rtp_session_add_telephone_event() to create them and finally
166  *      rtp_session_sendm_with_ts() to send them.
167  *
168  * @param session a rtp session
169  * @param dtmf a character meaning the dtmf (ex: '1', '#' , '9' ...)
170  * @param userts the timestamp
171  * @return 0 if successfull, -1 if the session cannot support telephony events or if the dtmf given as argument is not valid.
172 **/
173 int rtp_session_send_dtmf(RtpSession *session, char dtmf, uint32_t userts)
174 {
175   return rtp_session_send_dtmf2(session, dtmf, userts, 480);
176 }
177
178 /**
179  * A variation of rtp_session_send_dtmf() with duration specified.
180  *
181  * @param session a rtp session
182  * @param dtmf a character meaning the dtmf (ex: '1', '#' , '9' ...)
183  * @param userts the timestamp
184  * @param duration duration of the dtmf in timestamp units
185  * @return 0 if successfull, -1 if the session cannot support telephony events or if the dtmf given as argument is not valid.
186 **/
187 int rtp_session_send_dtmf2(RtpSession *session, char dtmf, uint32_t userts, int duration)
188 {
189         mblk_t *m1,*m2,*m3;
190         int tev_type;
191         int durationtier = duration/3;
192
193         /* create the first telephony event packet */
194         switch (dtmf){
195                 case '1':
196                         tev_type=TEV_DTMF_1;
197                 break;
198                 case '2':
199                         tev_type=TEV_DTMF_2;
200                 break;
201                 case '3':
202                         tev_type=TEV_DTMF_3;
203                 break;
204                 case '4':
205                         tev_type=TEV_DTMF_4;
206                 break;
207                 case '5':
208                         tev_type=TEV_DTMF_5;
209                 break;
210                 case '6':
211                         tev_type=TEV_DTMF_6;
212                 break;
213                 case '7':
214                         tev_type=TEV_DTMF_7;
215                 break;
216                 case '8':
217                         tev_type=TEV_DTMF_8;
218                 break;
219                 case '9':
220                         tev_type=TEV_DTMF_9;
221                 break;
222                 case '*':
223                         tev_type=TEV_DTMF_STAR;
224                 break;
225                 case '0':
226                         tev_type=TEV_DTMF_0;
227                 break;
228                 case '#':
229                         tev_type=TEV_DTMF_POUND;
230                 break;
231
232                 case 'A':
233                 case 'a':
234                   tev_type=TEV_DTMF_A;
235                   break;
236
237
238                 case 'B':
239                 case 'b':
240                   tev_type=TEV_DTMF_B;
241                   break;
242
243                 case 'C':
244                 case 'c':
245                   tev_type=TEV_DTMF_C;
246                   break;
247
248                 case 'D':
249                 case 'd':
250                   tev_type=TEV_DTMF_D;
251                   break;
252
253                 case '!':
254                   tev_type=TEV_FLASH;
255                   break;
256
257
258                 default:
259                 ortp_warning("Bad dtmf: %c.",dtmf);
260                 return -1;
261         }
262
263         m1=rtp_session_create_telephone_event_packet(session,1);
264         if (m1==NULL) return -1;
265         rtp_session_add_telephone_event(session,m1,tev_type,0,10,durationtier);
266         /* create a second packet */
267         m2=rtp_session_create_telephone_event_packet(session,0);
268         if (m2==NULL) return -1;
269         rtp_session_add_telephone_event(session,m2,tev_type,0,10, durationtier+durationtier);
270                 
271         /* create a third and final packet */
272         m3=rtp_session_create_telephone_event_packet(session,0);
273         if (m3==NULL) return -1;
274         rtp_session_add_telephone_event(session,m3,tev_type,1,10,duration);
275         
276         /* and now sends them */
277         rtp_session_sendm_with_ts(session,m1,userts);
278         rtp_session_sendm_with_ts(session,m2,userts);
279         /* the last packet is sent three times in order to improve reliability*/
280         m1=copymsg(m3);
281         m2=copymsg(m3);
282         /*                      NOTE:                   */
283         /* we need to copymsg() instead of dupmsg() because the buffers are modified when
284         the packet is sended because of the host-to-network conversion of timestamp,ssrc, csrc, and
285         seq number.
286         */
287         rtp_session_sendm_with_ts(session,m3,userts);
288         session->rtp.snd_seq--;
289         rtp_session_sendm_with_ts(session,m1,userts);
290         session->rtp.snd_seq--;
291         rtp_session_sendm_with_ts(session,m2,userts);
292         return 0;
293 }
294
295
296 /**
297  *      Reads telephony events from a rtp packet. *@tab points to the beginning of the event buffer.
298  *
299  * @param session a rtp session from which telephony events are received.
300  * @param packet a rtp packet as a mblk_t.
301  * @param tab the address of a pointer.
302  * @return the number of events in the packet if successfull, 0 if the packet did not contain telephony events.
303 **/
304 int rtp_session_read_telephone_event(RtpSession *session,
305                 mblk_t *packet,telephone_event_t **tab)
306 {
307         int datasize;
308         int num;
309         int i;
310         telephone_event_t *tev;
311         rtp_header_t *hdr=(rtp_header_t*)packet->b_rptr;
312         unsigned char *payload;
313         if (hdr->paytype!=session->rcv.telephone_events_pt) return 0;  /* this is not tel ev.*/
314         datasize=rtp_get_payload(packet,&payload);
315         tev=*tab=(telephone_event_t*)payload;
316         /* convert from network to host order what should be */
317         num=datasize/sizeof(telephone_event_t);
318         for (i=0;i<num;i++)
319         {
320                 tev[i].duration=ntohs(tev[i].duration);
321         }
322         return num;
323 }
324
325 static void notify_tev(RtpSession *session, telephone_event_t *event){
326         OrtpEvent *ev;
327         OrtpEventData *evd;
328         rtp_signal_table_emit2(&session->on_telephone_event,(long)(long)event[0].event);
329         if (session->eventqs!=NULL){
330                 ev=ortp_event_new(ORTP_EVENT_TELEPHONE_EVENT);
331                 evd=ortp_event_get_data(ev);
332                 evd->packet=dupmsg(session->current_tev);
333                 evd->info.telephone_event=event[0].event;
334                 rtp_session_dispatch_event(session,ev);
335         }
336 }
337
338 static void notify_events_ended(RtpSession *session, telephone_event_t *events, int num){
339         int i;
340         for (i=0;i<num;i++){
341                 if (events[i].E==1){
342                         notify_tev(session, &events[i]);
343                 }
344         }
345 }
346
347 /* for high level telephony event callback */
348 void rtp_session_check_telephone_events(RtpSession *session, mblk_t *m0)
349 {
350         telephone_event_t *events,*evbuf;
351         int num,num2;
352         int i;
353         rtp_header_t *hdr;
354         mblk_t *cur_tev;
355         unsigned char *payload;
356         int datasize;
357         
358         hdr=(rtp_header_t*)m0->b_rptr;
359         
360         datasize=rtp_get_payload(m0,&payload);
361
362         num=datasize/sizeof(telephone_event_t);
363         events=(telephone_event_t*)payload;
364         
365         
366         if (hdr->markbit==1)
367         {
368                 /* this is a start of new events. Store the event buffer for later use*/
369                 if (session->current_tev!=NULL) {
370                         freemsg(session->current_tev);
371                         session->current_tev=NULL;
372                 }
373                 session->current_tev=copymsg(m0);
374                 /* handle the case where the events are short enough to end within the packet that has the marker bit*/
375                 notify_events_ended(session,events,num);
376         }
377         /* whatever there is a markbit set or not, we parse the packet and compare it to previously received one */
378         cur_tev=session->current_tev;
379         if (cur_tev!=NULL)
380         {
381                 /* first compare timestamp, they must be identical */
382                 if (((rtp_header_t*)cur_tev->b_rptr)->timestamp==
383                         ((rtp_header_t*)m0->b_rptr)->timestamp)
384                 {
385                         datasize=rtp_get_payload(cur_tev,&payload);
386                         num2=datasize/sizeof(telephone_event_t);
387                         evbuf=(telephone_event_t*)payload;
388                         for (i=0;i<MIN(num,num2);i++)
389                         {
390                                 if (events[i].E==1)
391                                 {
392                                         /* update events that have ended */
393                                         if (evbuf[i].E==0){
394                                                 evbuf[i].E=1;
395                                                 /* this is a end of event, report it */
396                                                 notify_tev(session,&events[i]);
397                                         }
398                                 }
399                         }
400                 }
401                 else
402                 {
403                         /* timestamp are not identical: this is not the same events*/
404                         if (session->current_tev!=NULL) {
405                                 freemsg(session->current_tev);
406                                 session->current_tev=NULL;
407                         }
408                         session->current_tev=copymsg(m0);
409                         notify_events_ended(session,events,num);
410                 }
411         }
412         else
413         {
414                 /* there is no pending events, but we did not received marked bit packet
415                 either the sending implementation is not compliant, either it has been lost, 
416                 we must deal with it anyway.*/
417                 session->current_tev=copymsg(m0);
418                 /* inform the application if there are tone ends */
419                 notify_events_ended(session,events,num);
420         }
421 }