summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenny Prijono <bennylp@teluu.com>2006-02-22 09:21:09 +0000
committerBenny Prijono <bennylp@teluu.com>2006-02-22 09:21:09 +0000
commit58d9265e4c1b50b0e2e75467fac93f4df0170883 (patch)
tree00bf8f86799fde5401bc17b4848903424117c39b
parentdfa8dfc4a430e09c744d29237790624932953136 (diff)
Putting initial DTMF efforts
git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@214 74dad513-b988-da41-8d7b-12977e46ad98
-rw-r--r--pjmedia/build/pjmedia.dsp2
-rw-r--r--pjmedia/include/pjmedia/errno.h5
-rw-r--r--pjmedia/include/pjmedia/rtp.h20
-rw-r--r--pjmedia/include/pjmedia/stream.h16
-rw-r--r--pjmedia/src/pjmedia/conference.c7
-rw-r--r--pjmedia/src/pjmedia/endpoint.c18
-rw-r--r--pjmedia/src/pjmedia/stream.c173
7 files changed, 230 insertions, 11 deletions
diff --git a/pjmedia/build/pjmedia.dsp b/pjmedia/build/pjmedia.dsp
index fc1e6d35..97f4b5ca 100644
--- a/pjmedia/build/pjmedia.dsp
+++ b/pjmedia/build/pjmedia.dsp
@@ -65,7 +65,7 @@ LIB32=link.exe -lib
# PROP Intermediate_Dir ".\output\pjmedia-i386-win32-vc6-debug"
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c
-# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "../include" /I "../../pjlib/include" /I "../../pjlib-util/include" /I "../src/pjmedia/portaudio" /D "_DEBUG" /D "PA_NO_ASIO" /D "PA_NO_WIN_DS" /D "WIN32" /D "_MBCS" /D "_LIB" /D PJ_WIN32=1 /D PJ_M_I386=1 /FR /FD /GZ /c
+# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "../include" /I "../../pjlib/include" /I "../../pjlib-util/include" /I "../src/pjmedia/portaudio" /D "_DEBUG" /D "PA_NO_ASIO" /D "WIN32" /D "_MBCS" /D "_LIB" /D PJ_WIN32=1 /D PJ_M_I386=1 /FR /FD /GZ /c
# SUBTRACT CPP /YX
# ADD BASE RSC /l 0x409 /d "_DEBUG"
# ADD RSC /l 0x409 /d "_DEBUG"
diff --git a/pjmedia/include/pjmedia/errno.h b/pjmedia/include/pjmedia/errno.h
index 708f5e04..4fa26afd 100644
--- a/pjmedia/include/pjmedia/errno.h
+++ b/pjmedia/include/pjmedia/errno.h
@@ -354,6 +354,11 @@ 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/rtp.h b/pjmedia/include/pjmedia/rtp.h
index 3afc1307..34c576bd 100644
--- a/pjmedia/include/pjmedia/rtp.h
+++ b/pjmedia/include/pjmedia/rtp.h
@@ -122,6 +122,26 @@ struct pjmedia_rtp_ext_hdr
typedef struct pjmedia_rtp_ext_hdr pjmedia_rtp_ext_hdr;
+#pragma pack(1)
+
+/**
+ * Declaration for DTMF telephony-events (RFC2833).
+ */
+struct pjmedia_rtp_dtmf_event
+{
+ pj_uint8_t event;
+ pj_uint8_t e_vol;
+ pj_uint16_t duration;
+};
+
+/**
+ * @see pjmedia_rtp_dtmf_event
+ */
+typedef struct pjmedia_rtp_dtmf_event pjmedia_rtp_dtmf_event;
+
+#pragma pack()
+
+
/**
* A generic sequence number management, used by both RTP and RTCP.
*/
diff --git a/pjmedia/include/pjmedia/stream.h b/pjmedia/include/pjmedia/stream.h
index 52b8008f..6a685e06 100644
--- a/pjmedia/include/pjmedia/stream.h
+++ b/pjmedia/include/pjmedia/stream.h
@@ -71,6 +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. */
pj_uint32_t ssrc; /**< RTP SSRC. */
int jb_min; /**< Jitter buffer min delay. */
int jb_max; /**< Jitter buffer max delay. */
@@ -183,6 +185,20 @@ PJ_DECL(pj_status_t) pjmedia_stream_pause( pjmedia_stream *stream,
PJ_DECL(pj_status_t) pjmedia_stream_resume(pjmedia_stream *stream,
pjmedia_dir dir);
+/**
+ * Transmit DTMF to this stream. The DTMF will be transmitted uisng
+ * RTP telephone-events as described in RFC 2833. This operation is
+ * only valid for audio stream.
+ *
+ * @param stream The media stream.
+ * @param digit A single digit ('0123456789*#ABCD').
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_stream_dial_dtmf(pjmedia_stream *stream,
+ const pj_str_t *digit_char);
+
+
/**
* @}
diff --git a/pjmedia/src/pjmedia/conference.c b/pjmedia/src/pjmedia/conference.c
index 530815f1..538dfa5e 100644
--- a/pjmedia/src/pjmedia/conference.c
+++ b/pjmedia/src/pjmedia/conference.c
@@ -746,6 +746,13 @@ static pj_status_t play_cb( /* in */ void *user_data,
if (!conf_port)
continue;
+ //
+ // TODO:
+ // When there's no source, not transmit the frame, but instead
+ // transmit a 'silence' frame. This is to allow the 'port' to
+ // do some processing, such as updating timestamp for RTP session
+ // or transmit signal when it's in the middle of transmitting DTMF.
+ //
target_buf = (conf_port->cur_tx_buf==conf_port->tx_buf1?
conf_port->tx_buf2 : conf_port->tx_buf1);
diff --git a/pjmedia/src/pjmedia/endpoint.c b/pjmedia/src/pjmedia/endpoint.c
index 741de45a..24f409b1 100644
--- a/pjmedia/src/pjmedia/endpoint.c
+++ b/pjmedia/src/pjmedia/endpoint.c
@@ -257,11 +257,27 @@ PJ_DEF(pj_status_t) pjmedia_endpt_create_sdp( pjmedia_endpt *endpt,
}
- /* Add sendrect attribute. */
+ /* Add sendrecv attribute. */
attr = pj_pool_zalloc(pool, sizeof(pjmedia_sdp_attr));
attr->name = STR_SENDRECV;
m->attr[m->attr_count++] = attr;
+#if 1
+ //
+ // Test: add telephony events
+ //
+ m->desc.fmt[m->desc.fmt_count++] = pj_str("101");
+ /* Add rtpmap. */
+ attr = pj_pool_zalloc(pool, sizeof(pjmedia_sdp_attr));
+ attr->name = pj_str("rtpmap");
+ attr->value = pj_str(":101 telephone-event/8000");
+ m->attr[m->attr_count++] = attr;
+ /* Add fmtp */
+ attr = pj_pool_zalloc(pool, sizeof(pjmedia_sdp_attr));
+ attr->name = pj_str("fmtp");
+ attr->value = pj_str(":101 0-15");
+ m->attr[m->attr_count++] = attr;
+#endif
/* Done */
*p_sdp = sdp;
diff --git a/pjmedia/src/pjmedia/stream.c b/pjmedia/src/pjmedia/stream.c
index a3b15303..b1dc90f1 100644
--- a/pjmedia/src/pjmedia/stream.c
+++ b/pjmedia/src/pjmedia/stream.c
@@ -22,6 +22,7 @@
#include <pjmedia/rtcp.h>
#include <pjmedia/jbuf.h>
#include <pj/os.h>
+#include <pj/ctype.h>
#include <pj/log.h>
#include <pj/string.h> /* memcpy() */
#include <pj/pool.h>
@@ -39,7 +40,7 @@
#define PJMEDIA_MAX_FRAME_DURATION_MS 200
#define PJMEDIA_MAX_BUFFER_SIZE_MS 2000
#define PJMEDIA_MAX_MTU 1500
-
+#define PJMEDIA_DTMF_DURATION 1600 /* in timestamp */
/**
@@ -63,6 +64,13 @@ struct pjmedia_channel
};
+struct dtmf
+{
+ int event;
+ pj_uint32_t start_ts;
+ pj_uint32_t end_ts;
+};
+
/**
* This structure describes media stream.
* A media stream is bidirectional media transmission between two endpoints.
@@ -93,6 +101,10 @@ struct pjmedia_stream
pj_bool_t quit_flag; /**< To signal thread exit. */
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. */
};
@@ -178,6 +190,24 @@ static pj_status_t get_frame( pjmedia_port *port, pjmedia_frame *frame)
}
+/*
+ * Transmit DTMF
+ */
+static void transmit_dtmf(pjmedia_stream *stream,
+ struct pjmedia_frame *frame_out)
+{
+ pjmedia_rtp_dtmf_event *event;
+ struct dtmf *digit = &stream->dtmf_queue[0];
+ pj_uint32_t cur_ts;
+
+ event = frame_out->buf;
+ cur_ts = pj_ntohl(stream->enc->rtp.out_hdr.ts);
+
+ event->event = (pj_uint8_t)digit->event;
+ event->e_vol = 10;
+ event->duration = pj_htonl(cur_ts - digit->start_ts);
+}
+
/**
* rec_callback()
*
@@ -202,18 +232,31 @@ static pj_status_t put_frame( pjmedia_port *port,
if (stream->quit_flag)
return -1;
- /* Encode. */
+ /* Number of samples in the frame */
+ ts_len = frame->size / (channel->snd_info.bits_per_sample / 8);
+
+ /* Init frame_out buffer. */
frame_out.buf = ((char*)channel->out_pkt) + sizeof(pjmedia_rtp_hdr);
- status = stream->codec->op->encode( stream->codec, frame,
- channel->out_pkt_size - sizeof(pjmedia_rtp_hdr),
- &frame_out);
- if (status != 0) {
- TRACE_((THIS_FILE, "Codec encode() error", status));
- return status;
+
+ /* 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);
+ } else {
+ unsigned max_size;
+
+ max_size = channel->out_pkt_size - sizeof(pjmedia_rtp_hdr);
+ status = stream->codec->op->encode( stream->codec, frame,
+ max_size,
+ &frame_out);
+ if (status != 0) {
+ TRACE_((THIS_FILE, "Codec encode() error", status));
+ return status;
+ }
}
/* Encapsulate. */
- ts_len = frame->size / (channel->snd_info.bits_per_sample / 8);
status = pjmedia_rtp_encode_rtp( &channel->rtp,
channel->pt, 0,
frame_out.size, ts_len,
@@ -247,6 +290,29 @@ static pj_status_t put_frame( pjmedia_port *port,
}
+static void dump_bin(const char *buf, unsigned len)
+{
+ unsigned i;
+
+ PJ_LOG(3,(THIS_FILE, "begin dump"));
+ for (i=0; i<len; ++i) {
+ int j;
+ char bits[9];
+ unsigned val = buf[i] & 0xFF;
+
+ bits[8] = '\0';
+ for (j=0; j<8; ++j) {
+ if (val & (1 << (7-j)))
+ bits[j] = '1';
+ else
+ bits[j] = '0';
+ }
+
+ PJ_LOG(3,(THIS_FILE, "%2d %s [%d]", i, bits, val));
+ }
+ PJ_LOG(3,(THIS_FILE, "end dump"));
+}
+
/*
* This thread will poll the socket for incoming packets, and put
* the packets to jitter buffer.
@@ -310,6 +376,13 @@ static int PJ_THREAD_FUNC jitter_buffer_thread (void*arg)
continue;
}
+#if 1
+ if (hdr->pt == 101) {
+ dump_bin((char*)payload, payloadlen);
+ continue;
+ }
+#endif
+
status = pjmedia_rtp_session_update(&channel->rtp, hdr);
if (status != 0 &&
status != PJMEDIA_RTP_ESESSPROBATION &&
@@ -738,3 +811,85 @@ PJ_DEF(pj_status_t) pjmedia_stream_resume( pjmedia_stream *stream,
return PJ_SUCCESS;
}
+/*
+ * Dial DTMF
+ */
+PJ_DEF(pj_status_t) pjmedia_stream_dial_dtmf( pjmedia_stream *stream,
+ const pj_str_t *digit_char)
+{
+ pj_status_t status = PJ_SUCCESS;
+
+ /* By convention we use jitter buffer mutex to access DTMF
+ * queue.
+ */
+ PJ_ASSERT_RETURN(stream && digit_char, PJ_EINVAL);
+
+ pj_mutex_lock(stream->jb_mutex);
+
+ if (stream->dtmf_count+digit_char->slen >=
+ PJ_ARRAY_SIZE(stream->dtmf_queue))
+ {
+ status = PJ_ETOOMANY;
+ } else {
+ int i;
+
+ /* convert ASCII digits into payload type first, to make sure
+ * that all digits are valid.
+ */
+ for (i=0; i<digit_char->slen; ++i) {
+ unsigned pt;
+
+ if (digit_char->ptr[i] >= '0' &&
+ digit_char->ptr[i] <= '9')
+ {
+ pt = digit_char->ptr[i] - '0';
+ }
+ else if (pj_tolower(digit_char->ptr[i]) >= 'a' &&
+ pj_tolower(digit_char->ptr[i]) <= 'd')
+ {
+ pt = pj_tolower(digit_char->ptr[i]) - 'a' + 12;
+ }
+ else if (digit_char->ptr[i] == '*')
+ {
+ pt = 10;
+ }
+ else if (digit_char->ptr[i] == '#')
+ {
+ pt = 11;
+ }
+ else
+ {
+ status = PJMEDIA_RTP_EINDTMF;
+ 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;
+ }
+
+ if (status != PJ_SUCCESS)
+ goto on_return;
+
+ if (stream->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;
+ }
+
+ /* Increment digit count only if all digits are valid. */
+ stream->dtmf_count += digit_char->slen;
+
+ }
+
+on_return:
+ pj_mutex_unlock(stream->jb_mutex);
+
+ return status;
+}
+