summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenny Prijono <bennylp@teluu.com>2006-02-22 12:06:39 +0000
committerBenny Prijono <bennylp@teluu.com>2006-02-22 12:06:39 +0000
commit46d2a28800d1feec83ea52f543efb28bc653c2b3 (patch)
tree01a08c4e2a15cf7bab38a4a5234c89904e132a25
parent58d9265e4c1b50b0e2e75467fac93f4df0170883 (diff)
RFC 2833 support!
git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@215 74dad513-b988-da41-8d7b-12977e46ad98
-rw-r--r--pjmedia/include/pjmedia/errno.h15
-rw-r--r--pjmedia/include/pjmedia/session.h47
-rw-r--r--pjmedia/include/pjmedia/stream.h43
-rw-r--r--pjmedia/src/pjmedia/endpoint.c6
-rw-r--r--pjmedia/src/pjmedia/errno.c13
-rw-r--r--pjmedia/src/pjmedia/session.c81
-rw-r--r--pjmedia/src/pjmedia/stream.c223
-rw-r--r--pjsip/src/pjsua/main.c33
-rw-r--r--pjsip/src/pjsua/pjsua_inv.c1
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 );
@@ -197,6 +199,51 @@ PJ_DECL(pj_status_t) pjmedia_session_get_stream_stat(pjmedia_session *session,
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.
*
* @param session The 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; i<local_m->attr_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; i<rem_m->attr_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 <pjmedia/rtp.h>
#include <pjmedia/rtcp.h>
#include <pjmedia/jbuf.h>
-#include <pj/os.h>
+#include <pj/array.h>
+#include <pj/assert.h>
#include <pj/ctype.h>
+#include <pj/compat/socket.h>
+#include <pj/errno.h>
#include <pj/log.h>
-#include <pj/string.h> /* memcpy() */
+#include <pj/os.h>
#include <pj/pool.h>
-#include <pj/assert.h>
-#include <pj/compat/socket.h>
#include <pj/sock_select.h>
-#include <pj/errno.h>
-#include <stdlib.h>
+#include <pj/string.h> /* 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",