Putting initial DTMF efforts

git-svn-id: https://svn.pjsip.org/repos/pjproject/trunk@214 74dad513-b988-da41-8d7b-12977e46ad98
This commit is contained in:
Benny Prijono 2006-02-22 09:21:09 +00:00
parent f256ae7b37
commit 349a0f25f3
7 changed files with 230 additions and 11 deletions

View File

@ -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"

View File

@ -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 */
/************************************************************

View File

@ -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.
*/

View File

@ -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);
/**
* @}

View File

@ -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);

View File

@ -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;

View File

@ -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;
}