From 46d2a28800d1feec83ea52f543efb28bc653c2b3 Mon Sep 17 00:00:00 2001 From: Benny Prijono Date: Wed, 22 Feb 2006 12:06:39 +0000 Subject: RFC 2833 support! git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@215 74dad513-b988-da41-8d7b-12977e46ad98 --- pjmedia/include/pjmedia/errno.h | 15 ++- pjmedia/include/pjmedia/session.h | 47 ++++++++ pjmedia/include/pjmedia/stream.h | 43 +++++++- pjmedia/src/pjmedia/endpoint.c | 6 +- pjmedia/src/pjmedia/errno.c | 13 ++- pjmedia/src/pjmedia/session.c | 81 +++++++++++++- pjmedia/src/pjmedia/stream.c | 223 +++++++++++++++++++++++++++++++------- pjsip/src/pjsua/main.c | 33 +++++- pjsip/src/pjsua/pjsua_inv.c | 1 + 9 files changed, 409 insertions(+), 53 deletions(-) diff --git a/pjmedia/include/pjmedia/errno.h b/pjmedia/include/pjmedia/errno.h index 4fa26afd..accf3fd1 100644 --- a/pjmedia/include/pjmedia/errno.h +++ b/pjmedia/include/pjmedia/errno.h @@ -293,6 +293,16 @@ PJ_BEGIN_DECL * Invalid media type. */ #define PJMEDIA_EINVALIMEDIATYPE (PJMEDIA_ERRNO_START+104) /* 220104 */ +/** + * @hideinitializer + * Remote does not support DTMF. + */ +#define PJMEDIA_EREMOTENODTMF (PJMEDIA_ERRNO_START+105) /* 220105 */ +/** + * @hideinitializer + * Invalid DTMF digit. + */ +#define PJMEDIA_RTP_EINDTMF (PJMEDIA_ERRNO_START+106) /* 220106 */ @@ -354,11 +364,6 @@ PJ_BEGIN_DECL * RTP is not configured. */ #define PJMEDIA_RTP_ENOCONFIG (PJMEDIA_ERRNO_START+134) /* 220134 */ -/** - * @hideinitializer - * Invalid DTMF digit. - */ -#define PJMEDIA_RTP_EINDTMF (PJMEDIA_ERRNO_START+135) /* 220135 */ /************************************************************ diff --git a/pjmedia/include/pjmedia/session.h b/pjmedia/include/pjmedia/session.h index 78d699ff..1335bd5f 100644 --- a/pjmedia/include/pjmedia/session.h +++ b/pjmedia/include/pjmedia/session.h @@ -80,6 +80,7 @@ struct pjmedia_session_info * created in the session. * @param local_sdp The SDP describing local capability. * @param rem_sdp The SDP describing remote capability. + * @param user_data Arbitrary user data to be kept in the session. * @param p_session Pointer to receive the media session. * * @return PJ_SUCCESS if media session can be created @@ -91,6 +92,7 @@ pjmedia_session_create( pjmedia_endpt *endpt, const pjmedia_sock_info skinfo[], const pjmedia_sdp_session *local_sdp, const pjmedia_sdp_session *rem_sdp, + void *user_data, pjmedia_session **p_session ); @@ -196,6 +198,51 @@ PJ_DECL(pj_status_t) pjmedia_session_get_stream_stat(pjmedia_session *session, unsigned index, pjmedia_stream_stat *sta); +/** + * Dial DTMF digit to the stream, using RFC 2833 mechanism. + * + * @param session The media session. + * @param index The stream index. + * @param ascii_digits String of ASCII digits (i.e. 0-9*#A-B). + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) pjmedia_session_dial_dtmf( pjmedia_session *session, + unsigned index, + const pj_str_t *ascii_digits ); + + +/** + * Check if the specified stream has received DTMF digits. + * + * @param session The media session. + * @param index The stream index. + * + * @return Non-zero (PJ_TRUE) if the stream has DTMF digits. + */ +PJ_DECL(pj_status_t) pjmedia_session_check_dtmf( pjmedia_session *session, + unsigned index); + + +/** + * Retrieve DTMF digits from the specified stream. + * + * @param session The media session. + * @param index The stream index. + * @param ascii_digits Buffer to receive the digits. The length of this + * buffer is indicated in the "size" argument. + * @param size On input, contains the maximum digits to be copied + * to the buffer. + * On output, it contains the actual digits that has + * been copied to the buffer. + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) pjmedia_session_get_dtmf( pjmedia_session *session, + unsigned index, + char *ascii_digits, + unsigned *size ); + /** * Destroy media session. * diff --git a/pjmedia/include/pjmedia/stream.h b/pjmedia/include/pjmedia/stream.h index 6a685e06..67975566 100644 --- a/pjmedia/include/pjmedia/stream.h +++ b/pjmedia/include/pjmedia/stream.h @@ -71,8 +71,8 @@ struct pjmedia_stream_info pjmedia_sock_info sock_info; /**< Media transport (RTP/RTCP sockets) */ pj_sockaddr_in rem_addr; /**< Remote RTP address */ pjmedia_codec_info fmt; /**< Codec format info. */ - unsigned tx_event_pt;/**< Outgoing pt for telephone-events. */ - unsigned rx_event_pt;/**< Incoming pt for telephone-events. */ + int tx_event_pt;/**< Outgoing pt for telephone-events. */ + int rx_event_pt;/**< Incoming pt for telephone-events. */ pj_uint32_t ssrc; /**< RTP SSRC. */ int jb_min; /**< Jitter buffer min delay. */ int jb_max; /**< Jitter buffer max delay. */ @@ -110,6 +110,7 @@ struct pjmedia_stream_stat * number of memory may be needed because jitter * buffer needs to preallocate some storage. * @param info Stream information. + * @param user_data Arbitrary user data (for future callback feature). * @param p_stream Pointer to receive the media stream. * * @return PJ_SUCCESS on success. @@ -117,6 +118,7 @@ struct pjmedia_stream_stat PJ_DECL(pj_status_t) pjmedia_stream_create(pjmedia_endpt *endpt, pj_pool_t *pool, const pjmedia_stream_info *info, + void *user_data, pjmedia_stream **p_stream); /** @@ -191,14 +193,47 @@ PJ_DECL(pj_status_t) pjmedia_stream_resume(pjmedia_stream *stream, * only valid for audio stream. * * @param stream The media stream. - * @param digit A single digit ('0123456789*#ABCD'). + * @param ascii_digit String containing digits to be sent to remote. + * Currently the maximum number of digits are 32. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_stream_dial_dtmf(pjmedia_stream *stream, - const pj_str_t *digit_char); + const pj_str_t *ascii_digit); +/** + * Check if the stream has incoming DTMF digits in the incoming DTMF + * queue. Incoming DTMF digits received via RFC 2833 mechanism are + * saved in the incoming digits queue. + * + * @param stream The media stream. + * + * @return Non-zero (PJ_TRUE) if the stream has received DTMF + * digits in the . + */ +PJ_DECL(pj_bool_t) pjmedia_stream_check_dtmf(pjmedia_stream *stream); + + +/** + * Retrieve the incoming DTMF digits from the stream. Note that the digits + * buffer will not be NULL terminated. + * + * @param stream The media stream. + * @param ascii_digits Buffer to receive the digits. The length of this + * buffer is indicated in the "size" argument. + * @param size On input, contains the maximum digits to be copied + * to the buffer. + * On output, it contains the actual digits that has + * been copied to the buffer. + * + * @return Non-zero (PJ_TRUE) if the stream has received DTMF + * digits in the . + */ +PJ_DECL(pj_status_t) pjmedia_stream_get_dtmf( pjmedia_stream *stream, + char *ascii_digits, + unsigned *size); + /** * @} diff --git a/pjmedia/src/pjmedia/endpoint.c b/pjmedia/src/pjmedia/endpoint.c index 24f409b1..99a32729 100644 --- a/pjmedia/src/pjmedia/endpoint.c +++ b/pjmedia/src/pjmedia/endpoint.c @@ -263,9 +263,9 @@ PJ_DEF(pj_status_t) pjmedia_endpt_create_sdp( pjmedia_endpt *endpt, m->attr[m->attr_count++] = attr; #if 1 - // - // Test: add telephony events - // + /* + * Add support telephony event + */ m->desc.fmt[m->desc.fmt_count++] = pj_str("101"); /* Add rtpmap. */ attr = pj_pool_zalloc(pool, sizeof(pjmedia_sdp_attr)); diff --git a/pjmedia/src/pjmedia/errno.c b/pjmedia/src/pjmedia/errno.c index bce55a95..3a91deff 100644 --- a/pjmedia/src/pjmedia/errno.c +++ b/pjmedia/src/pjmedia/errno.c @@ -89,6 +89,8 @@ static const struct { PJMEDIA_EINVALIDPT, "Invalid media payload type" }, { PJMEDIA_EMISSINGRTPMAP, "Missing rtpmap in media description" }, { PJMEDIA_EINVALIMEDIATYPE, "Invalid media type" }, + { PJMEDIA_EREMOTENODTMF, "Remote does not support DTMF" }, + { PJMEDIA_RTP_EINDTMF, "Invalid DTMF digit" }, /* RTP session errors. */ { PJMEDIA_RTP_EINPKT, "Invalid RTP packet" }, @@ -100,7 +102,16 @@ static const struct { PJMEDIA_RTP_ESESSRESTART, "RTP session restarted" }, { PJMEDIA_RTP_ESESSPROBATION, "RTP session in probation" }, { PJMEDIA_RTP_EBADSEQ, "Bad sequence number in RTP packet" }, - + { PJMEDIA_RTP_EBADDEST, "RTP media port destination is not configured" }, + { PJMEDIA_RTP_ENOCONFIG, "RTP is not configured" }, + + /* Media port errors: */ + { PJMEDIA_ENOTCOMPATIBLE, "Media ports are not compatible" }, + { PJMEDIA_ENCCLOCKRATE, "Media ports have incompatible clock rate" }, + { PJMEDIA_ENCSAMPLESPFRAME, "Media ports have incompatible samples per frame" }, + { PJMEDIA_ENCTYPE, "Media ports have incompatible media type" }, + { PJMEDIA_ENCBITS, "Media ports have incompatible bits per sample" }, + { PJMEDIA_ENCBYTES, "Media ports have incompatible bytes per frame" }, }; diff --git a/pjmedia/src/pjmedia/session.c b/pjmedia/src/pjmedia/session.c index 884d8a9f..59960941 100644 --- a/pjmedia/src/pjmedia/session.c +++ b/pjmedia/src/pjmedia/session.c @@ -33,6 +33,7 @@ struct pjmedia_session unsigned stream_cnt; pjmedia_stream_info stream_info[PJMEDIA_MAX_SDP_MEDIA]; pjmedia_stream *stream[PJMEDIA_MAX_SDP_MEDIA]; + void *user_data; }; #define THIS_FILE "session.c" @@ -47,6 +48,7 @@ static const pj_str_t ID_IP4 = { "IP4", 3}; static const pj_str_t ID_RTP_AVP = { "RTP/AVP", 7 }; static const pj_str_t ID_SDP_NAME = { "pjmedia", 7 }; static const pj_str_t ID_RTPMAP = { "rtpmap", 6 }; +static const pj_str_t ID_TELEPHONE_EVENT = { "telephone-event", 15 }; static const pj_str_t STR_INACTIVE = { "inactive", 8 }; static const pj_str_t STR_SENDRECV = { "sendrecv", 8 }; @@ -66,6 +68,7 @@ static pj_status_t create_stream_info_from_sdp(pj_pool_t *pool, { const pjmedia_sdp_attr *attr; pjmedia_sdp_rtpmap *rtpmap; + unsigned i; pj_status_t status; @@ -151,7 +154,8 @@ static pj_status_t create_stream_info_from_sdp(pj_pool_t *pool, * For this version of PJMEDIA, we do not support static payload * type without rtpmap. */ - attr = pjmedia_sdp_media_find_attr(local_m, &ID_RTPMAP, NULL); + attr = pjmedia_sdp_media_find_attr(local_m, &ID_RTPMAP, + &local_m->desc.fmt[0]); if (attr == NULL) return PJMEDIA_EMISSINGRTPMAP; @@ -166,6 +170,39 @@ static pj_status_t create_stream_info_from_sdp(pj_pool_t *pool, pj_strdup(pool, &si->fmt.encoding_name, &rtpmap->enc_name); si->fmt.sample_rate = rtpmap->clock_rate; + /* Get local DTMF payload type */ + si->tx_event_pt = -1; + for (i=0; iattr_count; ++i) { + pjmedia_sdp_rtpmap r; + + attr = local_m->attr[i]; + if (pj_strcmp(&attr->name, &ID_RTPMAP) != 0) + continue; + if (pjmedia_sdp_attr_get_rtpmap(attr, &r) != PJ_SUCCESS) + continue; + if (pj_strcmp(&r.enc_name, &ID_TELEPHONE_EVENT) == 0) { + si->tx_event_pt = pj_strtoul(&r.pt); + break; + } + } + + /* Get remote DTMF payload type */ + si->rx_event_pt = -1; + for (i=0; iattr_count; ++i) { + pjmedia_sdp_rtpmap r; + + attr = rem_m->attr[i]; + if (pj_strcmp(&attr->name, &ID_RTPMAP) != 0) + continue; + if (pjmedia_sdp_attr_get_rtpmap(attr, &r) != PJ_SUCCESS) + continue; + if (pj_strcmp(&r.enc_name, &ID_TELEPHONE_EVENT) == 0) { + si->rx_event_pt = pj_strtoul(&r.pt); + break; + } + } + + /* Leave SSRC to zero. */ /* Leave jitter buffer parameter. */ @@ -182,6 +219,7 @@ PJ_DEF(pj_status_t) pjmedia_session_create( pjmedia_endpt *endpt, const pjmedia_sock_info skinfo[], const pjmedia_sdp_session *local_sdp, const pjmedia_sdp_session *rem_sdp, + void *user_data, pjmedia_session **p_session ) { pj_pool_t *pool; @@ -203,6 +241,7 @@ PJ_DEF(pj_status_t) pjmedia_session_create( pjmedia_endpt *endpt, session->pool = pool; session->endpt = endpt; session->stream_cnt = stream_cnt; + session->user_data = user_data; /* Stream count is the lower number of stream_cnt or SDP m= lines count */ if (stream_cnt < local_sdp->media_count) @@ -239,6 +278,7 @@ PJ_DEF(pj_status_t) pjmedia_session_create( pjmedia_endpt *endpt, status = pjmedia_stream_create(endpt, session->pool, &session->stream_info[i], + session, &session->stream[i]); if (status == PJ_SUCCESS) status = pjmedia_stream_start(session->stream[i]); @@ -385,6 +425,9 @@ PJ_DEF(pj_status_t) pjmedia_session_enum_streams(const pjmedia_session *session, } +/* + * Get the port interface. + */ PJ_DEF(pj_status_t) pjmedia_session_get_port( pjmedia_session *session, unsigned index, pjmedia_port **p_port) @@ -392,7 +435,7 @@ PJ_DEF(pj_status_t) pjmedia_session_get_port( pjmedia_session *session, return pjmedia_stream_get_port( session->stream[index], p_port); } -/** +/* * Get statistics */ PJ_DEF(pj_status_t) pjmedia_session_get_stream_stat( pjmedia_session *session, @@ -406,3 +449,37 @@ PJ_DEF(pj_status_t) pjmedia_session_get_stream_stat( pjmedia_session *session, } +/* + * Dial DTMF digit to the stream, using RFC 2833 mechanism. + */ +PJ_DEF(pj_status_t) pjmedia_session_dial_dtmf( pjmedia_session *session, + unsigned index, + const pj_str_t *ascii_digits ) +{ + PJ_ASSERT_RETURN(session && ascii_digits, PJ_EINVAL); + return pjmedia_stream_dial_dtmf(session->stream[index], ascii_digits); +} + +/* + * Check if the specified stream has received DTMF digits. + */ +PJ_DEF(pj_status_t) pjmedia_session_check_dtmf( pjmedia_session *session, + unsigned index ) +{ + PJ_ASSERT_RETURN(session, PJ_EINVAL); + return pjmedia_stream_check_dtmf(session->stream[index]); +} + + +/* + * Retrieve DTMF digits from the specified stream. + */ +PJ_DEF(pj_status_t) pjmedia_session_get_dtmf( pjmedia_session *session, + unsigned index, + char *ascii_digits, + unsigned *size ) +{ + PJ_ASSERT_RETURN(session && ascii_digits && size, PJ_EINVAL); + return pjmedia_stream_get_dtmf(session->stream[index], ascii_digits, + size); +} diff --git a/pjmedia/src/pjmedia/stream.c b/pjmedia/src/pjmedia/stream.c index b1dc90f1..a5737009 100644 --- a/pjmedia/src/pjmedia/stream.c +++ b/pjmedia/src/pjmedia/stream.c @@ -21,16 +21,16 @@ #include #include #include -#include +#include +#include #include +#include +#include #include -#include /* memcpy() */ +#include #include -#include -#include #include -#include -#include +#include /* memcpy() */ #define THIS_FILE "stream.c" @@ -68,7 +68,6 @@ struct dtmf { int event; pj_uint32_t start_ts; - pj_uint32_t end_ts; }; /** @@ -86,6 +85,7 @@ struct pjmedia_stream pjmedia_dir dir; /**< Stream direction. */ pjmedia_stream_stat stat; /**< Stream statistics. */ + void *user_data; /**< User data. */ pjmedia_codec_mgr *codec_mgr; /**< Codec manager instance. */ pjmedia_codec *codec; /**< Codec instance being used. */ @@ -103,8 +103,16 @@ struct pjmedia_stream pj_thread_t *thread; /**< Jitter buffer's thread. */ /* RFC 2833 DTMF transmission queue: */ - int dtmf_count; /**< # of digits in queue. */ - struct dtmf dtmf_queue[32];/**< Outgoing dtmf queue. */ + int tx_event_pt; /**< Outgoing pt for dtmf. */ + int tx_dtmf_count; /**< # of digits in tx dtmf buf.*/ + struct dtmf tx_dtmf_buf[32];/**< Outgoing dtmf queue. */ + + /* Incoming DTMF: */ + int rx_event_pt; /**< Incoming pt for dtmf. */ + int last_dtmf; /**< Current digit, or -1. */ + pj_uint32_t last_dtmf_dur; /**< Start ts for cur digit. */ + unsigned rx_dtmf_count; /**< # of digits in dtmf rx buf.*/ + char rx_dtmf_buf[32];/**< Incoming DTMF buffer. */ }; @@ -193,19 +201,38 @@ static pj_status_t get_frame( pjmedia_port *port, pjmedia_frame *frame) /* * Transmit DTMF */ -static void transmit_dtmf(pjmedia_stream *stream, +static void create_dtmf_payload(pjmedia_stream *stream, struct pjmedia_frame *frame_out) { pjmedia_rtp_dtmf_event *event; - struct dtmf *digit = &stream->dtmf_queue[0]; + struct dtmf *digit = &stream->tx_dtmf_buf[0]; + unsigned duration; pj_uint32_t cur_ts; + pj_assert(sizeof(pjmedia_rtp_dtmf_event) == 4); + event = frame_out->buf; cur_ts = pj_ntohl(stream->enc->rtp.out_hdr.ts); + duration = cur_ts - digit->start_ts; event->event = (pj_uint8_t)digit->event; event->e_vol = 10; - event->duration = pj_htonl(cur_ts - digit->start_ts); + event->duration = pj_htons((pj_uint16_t)duration); + + if (duration >= PJMEDIA_DTMF_DURATION) { + event->e_vol |= 0x80; + + /* Prepare next digit. */ + pj_mutex_lock(stream->jb_mutex); + pj_array_erase(stream->tx_dtmf_buf, sizeof(stream->tx_dtmf_buf[0]), + stream->tx_dtmf_count, 0); + --stream->tx_dtmf_count; + + stream->tx_dtmf_buf[0].start_ts = cur_ts; + pj_mutex_unlock(stream->jb_mutex); + } + + frame_out->size = 4; } /** @@ -227,7 +254,6 @@ static pj_status_t put_frame( pjmedia_port *port, int rtphdrlen; pj_ssize_t sent; - /* Check if stream is quitting. */ if (stream->quit_flag) return -1; @@ -241,8 +267,17 @@ static pj_status_t put_frame( pjmedia_port *port, /* If we have DTMF digits in the queue, transmit the digits. * Otherwise encode the PCM buffer. */ - if (stream->dtmf_count) { - transmit_dtmf(stream, &frame_out); + if (stream->tx_dtmf_count) { + + create_dtmf_payload(stream, &frame_out); + + /* Encapsulate. */ + status = pjmedia_rtp_encode_rtp( &channel->rtp, + stream->tx_event_pt, 0, + frame_out.size, ts_len, + (const void**)&rtphdr, + &rtphdrlen); + } else { unsigned max_size; @@ -254,13 +289,15 @@ static pj_status_t put_frame( pjmedia_port *port, TRACE_((THIS_FILE, "Codec encode() error", status)); return status; } + + /* Encapsulate. */ + status = pjmedia_rtp_encode_rtp( &channel->rtp, + channel->pt, 0, + frame_out.size, ts_len, + (const void**)&rtphdr, + &rtphdrlen); } - /* Encapsulate. */ - status = pjmedia_rtp_encode_rtp( &channel->rtp, - channel->pt, 0, - frame_out.size, ts_len, - (const void**)&rtphdr, &rtphdrlen); if (status != 0) { TRACE_((THIS_FILE, "RTP encode_rtp() error", status)); return status; @@ -289,7 +326,7 @@ static pj_status_t put_frame( pjmedia_port *port, return PJ_SUCCESS; } - +#if 0 static void dump_bin(const char *buf, unsigned len) { unsigned i; @@ -312,6 +349,68 @@ static void dump_bin(const char *buf, unsigned len) } PJ_LOG(3,(THIS_FILE, "end dump")); } +#endif + +/* + * Handle incoming DTMF digits. + */ +static void handle_incoming_dtmf( pjmedia_stream *stream, + const void *payload, unsigned payloadlen) +{ + static const char digitmap[16] = { '0', '1', '2', '3', + '4', '5', '6', '7', + '8', '9', '*', '#', + 'A', 'B', 'C', 'D'}; + const pjmedia_rtp_dtmf_event *event = payload; + + /* Check compiler packing. */ + pj_assert(sizeof(pjmedia_rtp_dtmf_event)==4); + + /* Must have sufficient length before we proceed. */ + if (payloadlen < sizeof(pjmedia_rtp_dtmf_event)) + return; + + //dump_bin(payload, payloadlen); + + /* Check if this is the same/current digit of the last packet. */ + if (stream->last_dtmf != -1 && + event->event == stream->last_dtmf && + pj_ntohs(event->duration) >= stream->last_dtmf_dur) + { + /* Yes, this is the same event. */ + stream->last_dtmf_dur = pj_ntohs(event->duration); + return; + } + + /* Ignore unknown event. */ + if (event->event > 15) { + PJ_LOG(5,(THIS_FILE, "Ignored RTP pkt with bad DTMF event %d", + event->event)); + return; + } + + /* New event! */ + PJ_LOG(5,(THIS_FILE, "Received DTMF digit %c, vol=%d", + digitmap[event->event], + (event->e_vol & 0x3F))); + + stream->last_dtmf = event->event; + stream->last_dtmf_dur = pj_ntohs(event->duration); + + /* By convention, we use jitter buffer's mutex to access shared + * DTMF variables. + */ + pj_mutex_lock(stream->jb_mutex); + if (stream->rx_dtmf_count >= PJ_ARRAY_SIZE(stream->rx_dtmf_buf)) { + /* DTMF digits overflow. Discard the oldest digit. */ + pj_array_erase(stream->rx_dtmf_buf, sizeof(stream->rx_dtmf_buf[0]), + stream->rx_dtmf_count, 0); + --stream->rx_dtmf_count; + } + stream->rx_dtmf_buf[stream->rx_dtmf_count++] = digitmap[event->event]; + pj_mutex_unlock(stream->jb_mutex); +} + /* * This thread will poll the socket for incoming packets, and put @@ -376,12 +475,11 @@ static int PJ_THREAD_FUNC jitter_buffer_thread (void*arg) continue; } -#if 1 - if (hdr->pt == 101) { - dump_bin((char*)payload, payloadlen); + /* Handle incoming DTMF. */ + if (hdr->pt == stream->rx_event_pt) { + handle_incoming_dtmf(stream, payload, payloadlen); continue; } -#endif status = pjmedia_rtp_session_update(&channel->rtp, hdr); if (status != 0 && @@ -520,6 +618,7 @@ static pj_status_t create_channel( pj_pool_t *pool, PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt, pj_pool_t *pool, const pjmedia_stream_info *info, + void *user_data, pjmedia_stream **p_stream) { @@ -552,9 +651,14 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt, /* Init stream: */ stream->dir = info->dir; + stream->user_data = user_data; stream->codec_mgr = pjmedia_endpt_get_codec_mgr(endpt); stream->skinfo = info->sock_info; stream->rem_rtp_addr = info->rem_addr; + stream->tx_event_pt = info->tx_event_pt; + stream->rx_event_pt = info->rx_event_pt; + stream->last_dtmf = -1; + PJ_TODO(INITIALIZE_RTCP_REMOTE_ADDRESS); @@ -824,10 +928,15 @@ PJ_DEF(pj_status_t) pjmedia_stream_dial_dtmf( pjmedia_stream *stream, */ PJ_ASSERT_RETURN(stream && digit_char, PJ_EINVAL); + /* Check that remote can receive DTMF events. */ + if (stream->tx_event_pt < 0) { + return PJMEDIA_RTP_EINDTMF; + } + pj_mutex_lock(stream->jb_mutex); - if (stream->dtmf_count+digit_char->slen >= - PJ_ARRAY_SIZE(stream->dtmf_queue)) + if (stream->tx_dtmf_count+digit_char->slen >= + PJ_ARRAY_SIZE(stream->tx_dtmf_buf)) { status = PJ_ETOOMANY; } else { @@ -863,27 +972,24 @@ PJ_DEF(pj_status_t) pjmedia_stream_dial_dtmf( pjmedia_stream *stream, break; } - stream->dtmf_queue[stream->dtmf_count+1].event = pt; - stream->dtmf_queue[stream->dtmf_count+1].start_ts = start_ts; - stream->dtmf_queue[stream->dtmf_count+1].end_ts = - start_ts + PJMEDIA_DTMF_DURATION; - - start_ts += PJMEDIA_DTMF_DURATION + 320; + stream->tx_dtmf_buf[stream->tx_dtmf_count+i].event = pt; } if (status != PJ_SUCCESS) goto on_return; - if (stream->dtmf_count ==0) { + /* Init start_ts and end_ts only for the first digit. + * Subsequent digits are initialized on the fly. + */ + if (stream->tx_dtmf_count ==0) { pj_uint32_t start_ts; start_ts = pj_ntohl(stream->enc->rtp.out_hdr.ts); - stream->dtmf_queue[0].start_ts = start_ts; - stream->dtmf_queue[0].end_ts = start_ts + PJMEDIA_DTMF_DURATION; + stream->tx_dtmf_buf[0].start_ts = start_ts; } /* Increment digit count only if all digits are valid. */ - stream->dtmf_count += digit_char->slen; + stream->tx_dtmf_count += digit_char->slen; } @@ -893,3 +999,46 @@ on_return: return status; } + +/* + * See if we have DTMF digits in the rx buffer. + */ +PJ_DEF(pj_bool_t) pjmedia_stream_check_dtmf(pjmedia_stream *stream) +{ + return stream->rx_dtmf_count != 0; +} + + +/* + * Retrieve incoming DTMF digits from the stream's DTMF buffer. + */ +PJ_DEF(pj_status_t) pjmedia_stream_get_dtmf( pjmedia_stream *stream, + char *digits, + unsigned *size) +{ + PJ_ASSERT_RETURN(stream && digits && size, PJ_EINVAL); + + pj_assert(sizeof(stream->rx_dtmf_buf[0]) == 0); + + /* By convention, we use jitter buffer's mutex to access DTMF + * digits resources. + */ + pj_mutex_lock(stream->jb_mutex); + + if (stream->rx_dtmf_count < *size) + *size = stream->rx_dtmf_count; + + if (*size) { + pj_memcpy(digits, stream->rx_dtmf_buf, *size); + stream->rx_dtmf_count -= *size; + if (stream->rx_dtmf_count) { + pj_memmove(stream->rx_dtmf_buf, + &stream->rx_dtmf_buf[*size], + stream->rx_dtmf_count); + } + } + + pj_mutex_unlock(stream->jb_mutex); + + return PJ_SUCCESS; +} diff --git a/pjsip/src/pjsua/main.c b/pjsip/src/pjsua/main.c index 4068e77f..5508e069 100644 --- a/pjsip/src/pjsua/main.c +++ b/pjsip/src/pjsua/main.c @@ -139,7 +139,7 @@ static void keystroke_help(void) puts("| H Hold call | Conference Command | |"); puts("| v re-inVite (release hold) | cl List ports | |"); puts("| x Xfer call | cc Connect port | |"); - puts("| | cd Disconnect port | |"); + puts("| # Send DTMF string | cd Disconnect port | |"); puts("+------------------------------+--------------------------+-------------------+"); puts("| q QUIT |"); puts("+=============================================================================+"); @@ -440,6 +440,37 @@ static void ui_console_main(void) } break; + case '#': + /* + * Send DTMF strings. + */ + if (inv_session == &pjsua.inv_list) { + + PJ_LOG(3,(THIS_FILE, "No current call")); + + } else if (inv_session->session == NULL) { + + PJ_LOG(3,(THIS_FILE, "Media is not established yet!")); + + } else { + pj_str_t digits; + pj_status_t status; + + if (!simple_input("DTMF strings to send (0-9*#A-B)", buf, + sizeof(buf))) + break; + + digits = pj_str(buf); + status = pjmedia_session_dial_dtmf(inv_session->session, 0, + &digits); + if (status != PJ_SUCCESS) { + pjsua_perror(THIS_FILE, "Unable to send DTMF", status); + } else { + puts("DTMF digits enqueued for transmission"); + } + } + break; + case 's': case 'u': /* diff --git a/pjsip/src/pjsua/pjsua_inv.c b/pjsip/src/pjsua/pjsua_inv.c index 34bf651f..824943cd 100644 --- a/pjsip/src/pjsua/pjsua_inv.c +++ b/pjsip/src/pjsua/pjsua_inv.c @@ -716,6 +716,7 @@ void pjsua_inv_on_media_update(pjsip_inv_session *inv, pj_status_t status) status = pjmedia_session_create( pjsua.med_endpt, 1, &pjsua.med_sock_info[inv_data->call_slot], local_sdp, remote_sdp, + inv_data, &inv_data->session ); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Unable to create media session", -- cgit v1.2.3