summaryrefslogtreecommitdiff
path: root/pjmedia/src
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 /pjmedia/src
parentdfa8dfc4a430e09c744d29237790624932953136 (diff)
Putting initial DTMF efforts
git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@214 74dad513-b988-da41-8d7b-12977e46ad98
Diffstat (limited to 'pjmedia/src')
-rw-r--r--pjmedia/src/pjmedia/conference.c7
-rw-r--r--pjmedia/src/pjmedia/endpoint.c18
-rw-r--r--pjmedia/src/pjmedia/stream.c173
3 files changed, 188 insertions, 10 deletions
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;
+}
+