From 33ccbcef51df23a167a5411ca97e7cdd9b604652 Mon Sep 17 00:00:00 2001 From: Nanang Izzuddin Date: Wed, 11 Feb 2015 05:15:29 +0000 Subject: Close #1814: Add audio frame preview callbacks. git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@4982 74dad513-b988-da41-8d7b-12977e46ad98 --- pjmedia/include/pjmedia/sound_port.h | 32 ++++++++++++++++++++++++++++++++ pjmedia/src/pjmedia/sound_port.c | 27 +++++++++++++++++++++++++++ pjsip/include/pjsua-lib/pjsua.h | 25 +++++++++++++++++++++++++ pjsip/src/pjsua-lib/pjsua_aud.c | 19 +++++++++++++++++++ 4 files changed, 103 insertions(+) diff --git a/pjmedia/include/pjmedia/sound_port.h b/pjmedia/include/pjmedia/sound_port.h index 07d15c4a..b09cb7bd 100644 --- a/pjmedia/include/pjmedia/sound_port.h +++ b/pjmedia/include/pjmedia/sound_port.h @@ -96,6 +96,38 @@ typedef struct pjmedia_snd_port_param */ unsigned ec_options; + /** + * Arbitrary user data for playback and record preview callbacks below. + */ + void *user_data; + + /** + * Optional callback for audio frame preview right before queued to + * the speaker. + * Notes: + * - application MUST NOT block or perform long operation in the callback + * as the callback may be executed in sound device thread + * - when using software echo cancellation, application MUST NOT modify + * the audio data from within the callback, otherwise the echo canceller + * will not work properly. + * - the return value of the callback will be ignored + */ + pjmedia_aud_play_cb on_play_frame; + + /** + * Optional callback for audio frame preview recorded from the microphone + * before being processed by any media component such as software echo + * canceller. + * Notes: + * - application MUST NOT block or perform long operation in the callback + * as the callback may be executed in sound device thread + * - when using software echo cancellation, application MUST NOT modify + * the audio data from within the callback, otherwise the echo canceller + * will not work properly. + * - the return value of the callback will be ignored + */ + pjmedia_aud_rec_cb on_rec_frame; + } pjmedia_snd_port_param; /** diff --git a/pjmedia/src/pjmedia/sound_port.c b/pjmedia/src/pjmedia/sound_port.c index 73305d83..79525d8d 100644 --- a/pjmedia/src/pjmedia/sound_port.c +++ b/pjmedia/src/pjmedia/sound_port.c @@ -61,6 +61,11 @@ struct pjmedia_snd_port pj_bool_t ec_suspended; unsigned ec_suspend_count; unsigned ec_suspend_limit; + + /* audio frame preview callbacks */ + void *user_data; + pjmedia_aud_play_cb on_play_frame; + pjmedia_aud_rec_cb on_rec_frame; }; /* @@ -100,6 +105,9 @@ static pj_status_t play_cb(void *user_data, pjmedia_frame *frame) pjmedia_echo_playback(snd_port->ec_state, (pj_int16_t*)frame->buf); } + /* Invoke preview callback */ + if (snd_port->on_play_frame) + (*snd_port->on_play_frame)(snd_port->user_data, frame); return PJ_SUCCESS; @@ -120,6 +128,10 @@ no_frame: } } + /* Invoke preview callback */ + if (snd_port->on_play_frame) + (*snd_port->on_play_frame)(snd_port->user_data, frame); + return PJ_SUCCESS; } @@ -135,6 +147,10 @@ static pj_status_t rec_cb(void *user_data, pjmedia_frame *frame) pjmedia_clock_src_update(&snd_port->cap_clocksrc, &frame->timestamp); + /* Invoke preview callback */ + if (snd_port->on_rec_frame) + (*snd_port->on_rec_frame)(snd_port->user_data, frame); + port = snd_port->port; if (port == NULL) return PJ_SUCCESS; @@ -166,6 +182,10 @@ static pj_status_t play_cb_ext(void *user_data, pjmedia_frame *frame) pjmedia_port_get_frame(port, frame); + /* Invoke preview callback */ + if (snd_port->on_play_frame) + (*snd_port->on_play_frame)(snd_port->user_data, frame); + return PJ_SUCCESS; } @@ -179,6 +199,10 @@ static pj_status_t rec_cb_ext(void *user_data, pjmedia_frame *frame) pjmedia_snd_port *snd_port = (pjmedia_snd_port*) user_data; pjmedia_port *port; + /* Invoke preview callback */ + if (snd_port->on_rec_frame) + (*snd_port->on_rec_frame)(snd_port->user_data, frame); + port = snd_port->port; if (port == NULL) return PJ_SUCCESS; @@ -464,6 +488,9 @@ PJ_DEF(pj_status_t) pjmedia_snd_port_create2(pj_pool_t *pool, pj_memcpy(&snd_port->aud_param, &prm->base, sizeof(snd_port->aud_param)); snd_port->options = prm->options; snd_port->prm_ec_options = prm->ec_options; + snd_port->user_data = prm->user_data; + snd_port->on_play_frame = prm->on_play_frame; + snd_port->on_rec_frame = prm->on_rec_frame; ptime_usec = prm->base.samples_per_frame * 1000 / prm->base.channel_count / prm->base.clock_rate * 1000; diff --git a/pjsip/include/pjsua-lib/pjsua.h b/pjsip/include/pjsua-lib/pjsua.h index 31c5378b..6325b70a 100644 --- a/pjsip/include/pjsua-lib/pjsua.h +++ b/pjsip/include/pjsua-lib/pjsua.h @@ -5811,6 +5811,31 @@ struct pjsua_media_config * Default: PJ_FALSE */ pj_bool_t no_rtcp_sdes_bye; + + /** + * Optional callback for audio frame preview right before queued to + * the speaker. + * Notes: + * - application MUST NOT block or perform long operation in the callback + * as the callback may be executed in sound device thread + * - when using software echo cancellation, application MUST NOT modify + * the audio data from within the callback, otherwise the echo canceller + * will not work properly. + */ + void (*on_aud_prev_play_frame)(pjmedia_frame *frame); + + /** + * Optional callback for audio frame preview recorded from the microphone + * before being processed by any media component such as software echo + * canceller. + * Notes: + * - application MUST NOT block or perform long operation in the callback + * as the callback may be executed in sound device thread + * - when using software echo cancellation, application MUST NOT modify + * the audio data from within the callback, otherwise the echo canceller + * will not work properly. + */ + void (*on_aud_prev_rec_frame)(pjmedia_frame *frame); }; diff --git a/pjsip/src/pjsua-lib/pjsua_aud.c b/pjsip/src/pjsua-lib/pjsua_aud.c index 65c69979..921a5f61 100644 --- a/pjsip/src/pjsua-lib/pjsua_aud.c +++ b/pjsip/src/pjsua-lib/pjsua_aud.c @@ -1677,6 +1677,20 @@ static const char *get_fmt_name(pj_uint32_t id) return name; } +static pj_status_t on_aud_prev_play_frame(void *user_data, pjmedia_frame *frame) +{ + PJ_UNUSED_ARG(user_data); + (*pjsua_var.media_cfg.on_aud_prev_play_frame)(frame); + return PJ_SUCCESS; +} + +static pj_status_t on_aud_prev_rec_frame(void *user_data, pjmedia_frame *frame) +{ + PJ_UNUSED_ARG(user_data); + (*pjsua_var.media_cfg.on_aud_prev_rec_frame)(frame); + return PJ_SUCCESS; +} + /* Open sound device with the setting. */ static pj_status_t open_snd_dev(pjmedia_snd_port_param *param) { @@ -1704,6 +1718,11 @@ static pj_status_t open_snd_dev(pjmedia_snd_port_param *param) pjsua_var.snd_pool = pjsua_pool_create("pjsua_snd", 4000, 4000); PJ_ASSERT_RETURN(pjsua_var.snd_pool, PJ_ENOMEM); + /* Setup preview callbacks, if configured */ + if (pjsua_var.media_cfg.on_aud_prev_play_frame) + param->on_play_frame = &on_aud_prev_play_frame; + if (pjsua_var.media_cfg.on_aud_prev_rec_frame) + param->on_rec_frame = &on_aud_prev_rec_frame; PJ_LOG(4,(THIS_FILE, "Opening sound device %s@%d/%d/%dms", get_fmt_name(param->base.ext_fmt.id), -- cgit v1.2.3