2 The oRTP library is an RTP (Realtime Transport Protocol - rfc1889) stack.
3 Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org
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.
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.
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
20 #include <ortp/telephonyevents.h>
22 #include "rtpsession_priv.h"
23 #include <ortp/ortp.h>
25 PayloadType payload_type_telephone_event={
26 PAYLOAD_AUDIO_PACKETIZED, /*type */
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 */
38 * Tells whether telephony events payload type is supported within the
39 * context of the rtp session.
40 * @param session a rtp session
42 * @return the payload type number used for telephony events if found, -1 if not found.
44 int rtp_session_telephone_events_supported(RtpSession *session)
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;
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
59 * @return the payload type number used for telephony events if found, -1 if not found.
61 int rtp_session_send_telephone_events_supported(RtpSession *session)
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;
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
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)
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;
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().
89 * @param session a rtp session.
90 * @param start boolean to indicate if the marker bit should be set.
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).
96 mblk_t *rtp_session_create_telephone_event_packet(RtpSession *session, int start)
101 return_val_if_fail(session->snd.telephone_events_pt!=-1,NULL);
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;
111 rtp->ssrc = session->snd.ssrc;
112 /* timestamp set later, when packet is sended */
113 /*seq number set later, when packet is sended */
115 /*set the payload type */
116 rtp->paytype=session->snd.telephone_events_pt;
118 /*copy the payload */
119 mp->b_wptr+=RTP_FIXED_HEADER_SIZE;
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.
132 * Adds a named telephony event to a rtp packet previously allocated using
133 * rtp_session_create_telephone_event_packet().
135 *@return 0 on success.
137 int rtp_session_add_telephone_event(RtpSession *session,
138 mblk_t *packet, uint8_t event, int end, uint8_t volume, uint16_t duration)
141 telephone_event_t *event_hdr;
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);
152 if (mp==NULL) return -1;
153 event_hdr=(telephone_event_t*)mp->b_wptr;
154 event_hdr->event=event;
157 event_hdr->volume=volume;
158 event_hdr->duration=htons(duration);
159 mp->b_wptr+=sizeof(telephone_event_t);
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.
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.
173 int rtp_session_send_dtmf(RtpSession *session, char dtmf, uint32_t userts)
175 return rtp_session_send_dtmf2(session, dtmf, userts, 480);
179 * A variation of rtp_session_send_dtmf() with duration specified.
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.
187 int rtp_session_send_dtmf2(RtpSession *session, char dtmf, uint32_t userts, int duration)
191 int durationtier = duration/3;
193 /* create the first telephony event packet */
223 tev_type=TEV_DTMF_STAR;
229 tev_type=TEV_DTMF_POUND;
259 ortp_warning("Bad dtmf: %c.",dtmf);
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);
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);
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*/
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
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);
297 * Reads telephony events from a rtp packet. *@tab points to the beginning of the event buffer.
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.
304 int rtp_session_read_telephone_event(RtpSession *session,
305 mblk_t *packet,telephone_event_t **tab)
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);
320 tev[i].duration=ntohs(tev[i].duration);
325 static void notify_tev(RtpSession *session, telephone_event_t *event){
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);
338 static void notify_events_ended(RtpSession *session, telephone_event_t *events, int num){
342 notify_tev(session, &events[i]);
347 /* for high level telephony event callback */
348 void rtp_session_check_telephone_events(RtpSession *session, mblk_t *m0)
350 telephone_event_t *events,*evbuf;
355 unsigned char *payload;
358 hdr=(rtp_header_t*)m0->b_rptr;
360 datasize=rtp_get_payload(m0,&payload);
362 num=datasize/sizeof(telephone_event_t);
363 events=(telephone_event_t*)payload;
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;
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);
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;
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)
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++)
392 /* update events that have ended */
395 /* this is a end of event, report it */
396 notify_tev(session,&events[i]);
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;
408 session->current_tev=copymsg(m0);
409 notify_events_ended(session,events,num);
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);