From 58d9265e4c1b50b0e2e75467fac93f4df0170883 Mon Sep 17 00:00:00 2001 From: Benny Prijono Date: Wed, 22 Feb 2006 09:21:09 +0000 Subject: Putting initial DTMF efforts git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@214 74dad513-b988-da41-8d7b-12977e46ad98 --- pjmedia/build/pjmedia.dsp | 2 +- pjmedia/include/pjmedia/errno.h | 5 ++ pjmedia/include/pjmedia/rtp.h | 20 +++++ pjmedia/include/pjmedia/stream.h | 16 ++++ pjmedia/src/pjmedia/conference.c | 7 ++ pjmedia/src/pjmedia/endpoint.c | 18 +++- pjmedia/src/pjmedia/stream.c | 173 +++++++++++++++++++++++++++++++++++++-- 7 files changed, 230 insertions(+), 11 deletions(-) (limited to 'pjmedia') 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 #include #include +#include #include #include /* memcpy() */ #include @@ -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; ipt == 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; islen; ++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; +} + -- cgit v1.2.3