diff options
Diffstat (limited to 'pjmedia')
-rw-r--r-- | pjmedia/include/pjmedia/sound.h | 31 | ||||
-rw-r--r-- | pjmedia/src/pjmedia/dsound.c | 32 | ||||
-rw-r--r-- | pjmedia/src/pjmedia/pasound.c | 94 | ||||
-rw-r--r-- | pjmedia/src/pjmedia/sound_port.c | 20 |
4 files changed, 158 insertions, 19 deletions
diff --git a/pjmedia/include/pjmedia/sound.h b/pjmedia/include/pjmedia/sound.h index ecff739a..39c71b92 100644 --- a/pjmedia/include/pjmedia/sound.h +++ b/pjmedia/include/pjmedia/sound.h @@ -75,6 +75,24 @@ typedef struct pjmedia_snd_dev_info } pjmedia_snd_dev_info; /** + * Stream information, can be retrieved from a live stream by calling + * #pjmedia_snd_stream_get_info(). + */ +typedef struct pjmedia_snd_stream_info +{ + pjmedia_dir dir; /**< Stream direction. */ + int play_id; /**< Playback dev id, or -1 for rec only*/ + int rec_id; /**< Capture dev id, or -1 for play only*/ + unsigned clock_rate; /**< Actual clock rate. */ + unsigned channel_count; /**< Number of channels. */ + unsigned samples_per_frame; /**< Samples per frame. */ + unsigned bits_per_sample; /**< Bits per sample. */ + unsigned rec_latency; /**< Record latency, in samples. */ + unsigned play_latency; /**< Playback latency, in samples. */ +} pjmedia_snd_stream_info; + + +/** * This callback is called by player stream when it needs additional data * to be played by the device. Application must fill in the whole of output * buffer with sound samples. @@ -232,6 +250,19 @@ PJ_DECL(pj_status_t) pjmedia_snd_open_player( int index, /** + * Get information about live stream. + * + * @param strm The stream to be queried. + * @param i Pointer to stream information to be filled up with + * information about the stream. + * + * @return PJ_SUCCESS on success or the appropriate error code. + */ +PJ_DECL(pj_status_t) pjmedia_snd_stream_get_info(pjmedia_snd_stream *strm, + pjmedia_snd_stream_info *pi); + + +/** * Start the stream. * * @param stream The recorder or player stream. diff --git a/pjmedia/src/pjmedia/dsound.c b/pjmedia/src/pjmedia/dsound.c index cf8e1600..04cbc215 100644 --- a/pjmedia/src/pjmedia/dsound.c +++ b/pjmedia/src/pjmedia/dsound.c @@ -89,6 +89,8 @@ struct dsound_stream struct pjmedia_snd_stream { pjmedia_dir dir; /**< Sound direction. */ + int play_id; /**< Playback dev id. */ + int rec_id; /**< Recording dev id. */ pj_pool_t *pool; /**< Memory pool. */ pjmedia_snd_rec_cb rec_cb; /**< Capture callback. */ @@ -101,6 +103,8 @@ struct pjmedia_snd_stream void *buffer; /**< Temp. frame buffer. */ unsigned clock_rate; /**< Clock rate. */ unsigned samples_per_frame; /**< Samples per frame. */ + unsigned bits_per_sample; /**< Bits per sample. */ + unsigned channel_count; /**< Channel count. */ pj_thread_t *thread; /**< Thread handle. */ pj_bool_t thread_quit_flag; /**< Quit signal to thread */ @@ -719,12 +723,16 @@ static pj_status_t open_stream( pjmedia_dir dir, strm = pj_pool_zalloc(pool, sizeof(pjmedia_snd_stream)); strm->dir = dir; + strm->play_id = play_id; + strm->rec_id = rec_id; strm->pool = pool; strm->rec_cb = rec_cb; strm->play_cb = play_cb; strm->user_data = user_data; strm->clock_rate = clock_rate; strm->samples_per_frame = samples_per_frame; + strm->bits_per_sample = bits_per_sample; + strm->channel_count = channel_count; strm->buffer = pj_pool_alloc(pool, samples_per_frame * BYTES_PER_SAMPLE); if (!strm->buffer) { pj_pool_release(pool); @@ -827,6 +835,30 @@ PJ_DEF(pj_status_t) pjmedia_snd_open( int rec_id, } /* + * Get stream info. + */ +PJ_DEF(pj_status_t) pjmedia_snd_stream_get_info(pjmedia_snd_stream *strm, + pjmedia_snd_stream_info *pi) +{ + + PJ_ASSERT_RETURN(strm && pi, PJ_EINVAL); + + pj_bzero(pi, sizeof(*pi)); + pi->dir = strm->dir; + pi->play_id = strm->play_id; + pi->rec_id = strm->rec_id; + pi->clock_rate = strm->clock_rate; + pi->channel_count = strm->channel_count; + pi->samples_per_frame = strm->samples_per_frame; + pi->bits_per_sample = strm->bits_per_sample; + pi->rec_latency = 0; + pi->play_latency = 0; + + return PJ_SUCCESS; +} + + +/* * Start stream. */ PJ_DEF(pj_status_t) pjmedia_snd_stream_start(pjmedia_snd_stream *stream) diff --git a/pjmedia/src/pjmedia/pasound.c b/pjmedia/src/pjmedia/pasound.c index 527c0a02..9e8a92b1 100644 --- a/pjmedia/src/pjmedia/pasound.c +++ b/pjmedia/src/pjmedia/pasound.c @@ -18,6 +18,7 @@ */ #include <pjmedia/sound.h> #include <pjmedia/errno.h> +#include <pj/assert.h> #include <pj/log.h> #include <pj/os.h> #include <pj/string.h> @@ -42,8 +43,11 @@ struct pjmedia_snd_stream pj_pool_t *pool; pj_str_t name; pjmedia_dir dir; + int play_id; + int rec_id; int bytes_per_sample; pj_uint32_t samples_per_sec; + unsigned samples_per_frame; int channel_count; PaStream *stream; @@ -241,7 +245,8 @@ PJ_DEF(pj_status_t) pjmedia_snd_open_rec( int index, int sampleFormat; const PaDeviceInfo *paDevInfo = NULL; const PaHostApiInfo *paHostApiInfo = NULL; - unsigned paFrames; + unsigned paFrames, paRate, paLatency; + const PaStreamInfo *paSI; PaError err; if (index <= 0) { @@ -280,8 +285,11 @@ PJ_DEF(pj_status_t) pjmedia_snd_open_rec( int index, stream->pool = pool; pj_strdup2_with_null(pool, &stream->name, paDevInfo->name); stream->dir = PJMEDIA_DIR_CAPTURE; + stream->rec_id = index; + stream->play_id = -1; stream->user_data = user_data; stream->samples_per_sec = clock_rate; + stream->samples_per_frame = samples_per_frame; stream->bytes_per_sample = bits_per_sample / 8; stream->channel_count = channel_count; stream->rec_cb = rec_cb; @@ -306,13 +314,17 @@ PJ_DEF(pj_status_t) pjmedia_snd_open_rec( int index, return PJMEDIA_ERRNO_FROM_PORTAUDIO(err); } - PJ_LOG(5,(THIS_FILE, "%s opening device %s (%s) for recording, sample " + paSI = Pa_GetStreamInfo(stream->stream); + paRate = (unsigned)paSI->sampleRate; + paLatency = (unsigned)(paSI->inputLatency * 1000); + + PJ_LOG(5,(THIS_FILE, "Opened device %s (%s) for recording, sample " "rate=%d, ch=%d, " - "bits=%d, %d samples per frame", - (err==0 ? "Success" : "Error"), + "bits=%d, %d samples per frame, latency=%d ms", paDevInfo->name, paHostApiInfo->name, - clock_rate, channel_count, - bits_per_sample, samples_per_frame)); + paSI->sampleRate, channel_count, + bits_per_sample, samples_per_frame, + paLatency)); *p_snd_strm = stream; return PJ_SUCCESS; @@ -334,7 +346,8 @@ PJ_DEF(pj_status_t) pjmedia_snd_open_player( int index, int sampleFormat; const PaDeviceInfo *paDevInfo = NULL; const PaHostApiInfo *paHostApiInfo = NULL; - unsigned paFrames; + const PaStreamInfo *paSI; + unsigned paFrames, paRate, paLatency; PaError err; if (index <= 0) { @@ -373,8 +386,11 @@ PJ_DEF(pj_status_t) pjmedia_snd_open_player( int index, stream->pool = pool; pj_strdup2_with_null(pool, &stream->name, paDevInfo->name); stream->dir = stream->dir = PJMEDIA_DIR_PLAYBACK; + stream->play_id = index; + stream->rec_id = -1; stream->user_data = user_data; stream->samples_per_sec = clock_rate; + stream->samples_per_frame = samples_per_frame; stream->bytes_per_sample = bits_per_sample / 8; stream->channel_count = channel_count; stream->play_cb = play_cb; @@ -399,13 +415,16 @@ PJ_DEF(pj_status_t) pjmedia_snd_open_player( int index, return PJMEDIA_ERRNO_FROM_PORTAUDIO(err); } - PJ_LOG(5,(THIS_FILE, "%s opening device %d: %s(%s) for playing, sample rate=%d" + paSI = Pa_GetStreamInfo(stream->stream); + paRate = (unsigned)(paSI->sampleRate); + paLatency = (unsigned)(paSI->outputLatency * 1000); + + PJ_LOG(5,(THIS_FILE, "Opened device %d: %s(%s) for playing, sample rate=%d" ", ch=%d, " - "bits=%d, %d samples per frame", - (err==0 ? "Success" : "Error"), + "bits=%d, %d samples per frame, latency=%d ms", index, paDevInfo->name, paHostApiInfo->name, - clock_rate, channel_count, - bits_per_sample, samples_per_frame)); + paRate, channel_count, + bits_per_sample, samples_per_frame, paLatency)); *p_snd_strm = stream; @@ -436,7 +455,8 @@ PJ_DEF(pj_status_t) pjmedia_snd_open( int rec_id, const PaDeviceInfo *paPlayDevInfo = NULL; const PaHostApiInfo *paRecHostApiInfo = NULL; const PaHostApiInfo *paPlayHostApiInfo = NULL; - unsigned paFrames; + const PaStreamInfo *paSI; + unsigned paFrames, paRate, paInputLatency, paOutputLatency; PaError err; if (rec_id <= 0) { @@ -494,8 +514,11 @@ PJ_DEF(pj_status_t) pjmedia_snd_open( int rec_id, stream->pool = pool; pj_strdup2_with_null(pool, &stream->name, paRecDevInfo->name); stream->dir = PJMEDIA_DIR_CAPTURE_PLAYBACK; + stream->play_id = play_id; + stream->rec_id = rec_id; stream->user_data = user_data; stream->samples_per_sec = clock_rate; + stream->samples_per_frame = samples_per_frame; stream->bytes_per_sample = bits_per_sample / 8; stream->channel_count = channel_count; stream->rec_cb = rec_cb; @@ -530,14 +553,20 @@ PJ_DEF(pj_status_t) pjmedia_snd_open( int rec_id, return PJMEDIA_ERRNO_FROM_PORTAUDIO(err); } - PJ_LOG(5,(THIS_FILE, "%s opening device %s(%s)/%s(%s) for recording and " + paSI = Pa_GetStreamInfo(stream->stream); + paRate = (unsigned)(paSI->sampleRate); + paInputLatency = (unsigned)(paSI->inputLatency * 1000); + paOutputLatency = (unsigned)(paSI->outputLatency * 1000); + + PJ_LOG(5,(THIS_FILE, "Opened device %s(%s)/%s(%s) for recording and " "playback, sample rate=%d, ch=%d, " - "bits=%d, %d samples per frame", - (err==0 ? "Success" : "Error"), + "bits=%d, %d samples per frame, input latency=%d ms, " + "output latency=%d ms", paRecDevInfo->name, paRecHostApiInfo->name, paPlayDevInfo->name, paPlayHostApiInfo->name, - clock_rate, channel_count, - bits_per_sample, samples_per_frame)); + paRate, channel_count, + bits_per_sample, samples_per_frame, + paInputLatency, paOutputLatency)); *p_snd_strm = stream; @@ -545,6 +574,35 @@ PJ_DEF(pj_status_t) pjmedia_snd_open( int rec_id, return PJ_SUCCESS; } + +/* + * Get stream info. + */ +PJ_DEF(pj_status_t) pjmedia_snd_stream_get_info(pjmedia_snd_stream *strm, + pjmedia_snd_stream_info *pi) +{ + const PaStreamInfo *paSI; + + PJ_ASSERT_RETURN(strm && pi, PJ_EINVAL); + PJ_ASSERT_RETURN(strm->stream, PJ_EINVALIDOP); + + paSI = Pa_GetStreamInfo(strm->stream); + + pj_bzero(pi, sizeof(*pi)); + pi->dir = strm->dir; + pi->play_id = strm->play_id; + pi->rec_id = strm->rec_id; + pi->clock_rate = (unsigned)(paSI->sampleRate); + pi->channel_count = strm->channel_count; + pi->samples_per_frame = strm->samples_per_frame; + pi->bits_per_sample = strm->bytes_per_sample * 8; + pi->rec_latency = (unsigned)(paSI->inputLatency * paSI->sampleRate); + pi->play_latency = (unsigned)(paSI->outputLatency * paSI->sampleRate); + + return PJ_SUCCESS; +} + + /* * Start stream. */ diff --git a/pjmedia/src/pjmedia/sound_port.c b/pjmedia/src/pjmedia/sound_port.c index 170091c5..02478e06 100644 --- a/pjmedia/src/pjmedia/sound_port.c +++ b/pjmedia/src/pjmedia/sound_port.c @@ -124,6 +124,7 @@ static pj_status_t play_cb(/* in */ void *user_data, if (snd_port->ec_state) { if (snd_port->ec_suspended) { snd_port->ec_suspended = PJ_FALSE; + //pjmedia_echo_state_reset(snd_port->ec_state); PJ_LOG(4,(THIS_FILE, "EC activated")); } snd_port->ec_suspend_count = 0; @@ -141,6 +142,10 @@ no_frame: snd_port->ec_suspended = PJ_TRUE; PJ_LOG(4,(THIS_FILE, "EC suspended because of inactivity")); } + if (snd_port->ec_state) { + /* To maintain correct delay in EC */ + pjmedia_echo_playback(snd_port->ec_state, output); + } } /* Apply PLC */ @@ -353,7 +358,7 @@ PJ_DEF(pj_status_t) pjmedia_snd_port_create( pj_pool_t *pool, } /* - * Create sound recorder port. + * Create sound recorder AEC. */ PJ_DEF(pj_status_t) pjmedia_snd_port_create_rec( pj_pool_t *pool, int dev_id, @@ -456,6 +461,7 @@ PJ_DEF(pj_status_t) pjmedia_snd_port_set_ec( pjmedia_snd_port *snd_port, unsigned tail_ms, unsigned options) { + pjmedia_snd_stream_info si; pj_status_t status; /* Sound must be opened in full-duplex mode */ @@ -463,6 +469,10 @@ PJ_DEF(pj_status_t) pjmedia_snd_port_set_ec( pjmedia_snd_port *snd_port, snd_port->dir == PJMEDIA_DIR_CAPTURE_PLAYBACK, PJ_EINVALIDOP); + /* Sound port must have 16bits per sample */ + PJ_ASSERT_RETURN(snd_port->bits_per_sample == 16, + PJ_EINVALIDOP); + /* Destroy AEC */ if (snd_port->ec_state) { pjmedia_echo_destroy(snd_port->ec_state); @@ -472,6 +482,14 @@ PJ_DEF(pj_status_t) pjmedia_snd_port_set_ec( pjmedia_snd_port *snd_port, snd_port->aec_tail_len = tail_ms; if (tail_ms != 0) { + unsigned delay_ms; + + status = pjmedia_snd_stream_get_info(snd_port->snd_stream, &si); + if (status != PJ_SUCCESS) + si.rec_latency = si.play_latency = 0; + + delay_ms = (si.rec_latency + si.play_latency) * 1000 / + snd_port->clock_rate; status = pjmedia_echo_create(pool, snd_port->clock_rate, snd_port->samples_per_frame, tail_ms, options, &snd_port->ec_state); |