From c44da2d6d7f8a991cd8143f97acda117a4e0a422 Mon Sep 17 00:00:00 2001 From: Liong Sauw Ming Date: Fri, 11 Mar 2011 06:57:24 +0000 Subject: Fixed #1204: Support for refreshing audio device list. git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@3438 74dad513-b988-da41-8d7b-12977e46ad98 --- pjmedia/include/pjmedia-audiodev/audiodev.h | 14 ++ pjmedia/include/pjmedia-audiodev/audiodev_imp.h | 7 + pjmedia/include/pjmedia/sound_port.h | 37 ++++- pjmedia/src/pjmedia-audiodev/alsa_dev.c | 106 +++++++++---- pjmedia/src/pjmedia-audiodev/audiodev.c | 19 +++ pjmedia/src/pjmedia-audiodev/coreaudio_dev.c | 3 +- pjmedia/src/pjmedia-audiodev/legacy_dev.c | 11 +- pjmedia/src/pjmedia-audiodev/null_dev.c | 11 +- pjmedia/src/pjmedia-audiodev/pa_dev.c | 12 +- pjmedia/src/pjmedia-audiodev/symb_aps_dev.cpp | 11 +- pjmedia/src/pjmedia-audiodev/symb_mda_dev.cpp | 11 +- pjmedia/src/pjmedia-audiodev/symb_vas_dev.cpp | 11 +- pjmedia/src/pjmedia-audiodev/wmme_dev.c | 199 +++++++++++++++++++++++- pjmedia/src/pjmedia/sound_port.c | 77 ++++----- 14 files changed, 450 insertions(+), 79 deletions(-) (limited to 'pjmedia') diff --git a/pjmedia/include/pjmedia-audiodev/audiodev.h b/pjmedia/include/pjmedia-audiodev/audiodev.h index ba28bc0f..1d1c39c0 100644 --- a/pjmedia/include/pjmedia-audiodev/audiodev.h +++ b/pjmedia/include/pjmedia-audiodev/audiodev.h @@ -545,6 +545,20 @@ PJ_DECL(pj_status_t) pjmedia_aud_unregister_factory(pjmedia_aud_dev_factory_create_func_ptr adf); +/** + * Refresh the list of sound devices installed in the system. This function + * will only refresh the list of audio device so all active audio streams will + * be unaffected. After refreshing the device list, application MUST make sure + * to update all index references to audio devices (i.e. all variables of type + * pjmedia_aud_dev_index) before calling any function that accepts audio device + * index as its parameter. + * + * @return PJ_SUCCESS on successful operation or the appropriate + * error code. + */ +PJ_DECL(pj_status_t) pjmedia_aud_dev_refresh(void); + + /** * Get the number of sound devices installed in the system. * diff --git a/pjmedia/include/pjmedia-audiodev/audiodev_imp.h b/pjmedia/include/pjmedia-audiodev/audiodev_imp.h index e12c0f21..172c1279 100644 --- a/pjmedia/include/pjmedia-audiodev/audiodev_imp.h +++ b/pjmedia/include/pjmedia-audiodev/audiodev_imp.h @@ -92,6 +92,13 @@ typedef struct pjmedia_aud_dev_factory_op void *user_data, pjmedia_aud_stream **p_aud_strm); + /** + * Refresh the list of audio devices installed in the system. + * + * @param f The audio device factory. + */ + pj_status_t (*refresh)(pjmedia_aud_dev_factory *f); + } pjmedia_aud_dev_factory_op; diff --git a/pjmedia/include/pjmedia/sound_port.h b/pjmedia/include/pjmedia/sound_port.h index 7293a4d7..4d2d4aff 100644 --- a/pjmedia/include/pjmedia/sound_port.h +++ b/pjmedia/include/pjmedia/sound_port.h @@ -62,6 +62,33 @@ PJ_BEGIN_DECL */ +/** + * Sound port options. + */ +enum pjmedia_snd_port_option +{ + /** + * Don't start the audio device when creating a sound port. + */ + PJMEDIA_SND_PORT_NO_AUTO_START = 1 +}; + +/** + * This structure specifies the parameters to create the sound port. + */ +typedef struct pjmedia_snd_port_param +{ + /** + * Base structure. + */ + pjmedia_aud_param base; + + /** + * Sound port creation options. + */ + unsigned options; +} pjmedia_snd_port_param; + /** * This opaque type describes sound device port connection. * Sound device port is not a media port, but it is used to connect media @@ -86,7 +113,7 @@ typedef struct pjmedia_snd_port pjmedia_snd_port; * @param samples_per_frame Number of samples per frame. * @param bits_per_sample Set the number of bits per sample. The normal * value for this parameter is 16 bits per sample. - * @param options Options flag, currently must be zero. + * @param options Options flag. * @param p_port Pointer to receive the sound device port instance. * * @return PJ_SUCCESS on success, or the appropriate error @@ -116,7 +143,7 @@ PJ_DECL(pj_status_t) pjmedia_snd_port_create( pj_pool_t *pool, * @param samples_per_frame Number of samples per frame. * @param bits_per_sample Set the number of bits per sample. The normal * value for this parameter is 16 bits per sample. - * @param options Options flag, currently must be zero. + * @param options Options flag. * @param p_port Pointer to receive the sound device port instance. * * @return PJ_SUCCESS on success, or the appropriate error @@ -145,7 +172,7 @@ PJ_DECL(pj_status_t) pjmedia_snd_port_create_rec(pj_pool_t *pool, * @param samples_per_frame Number of samples per frame. * @param bits_per_sample Set the number of bits per sample. The normal * value for this parameter is 16 bits per sample. - * @param options Options flag, currently must be zero. + * @param options Options flag. * @param p_port Pointer to receive the sound device port instance. * * @return PJ_SUCCESS on success, or the appropriate error @@ -165,14 +192,14 @@ PJ_DECL(pj_status_t) pjmedia_snd_port_create_player(pj_pool_t *pool, * Create sound device port according to the specified parameters. * * @param pool Pool to allocate sound port structure. - * @param prm Sound device settings. + * @param prm Sound port parameter. * @param p_port Pointer to receive the sound device port instance. * * @return PJ_SUCCESS on success, or the appropriate error * code. */ PJ_DECL(pj_status_t) pjmedia_snd_port_create2(pj_pool_t *pool, - const pjmedia_aud_param *prm, + const pjmedia_snd_port_param *prm, pjmedia_snd_port **p_port); diff --git a/pjmedia/src/pjmedia-audiodev/alsa_dev.c b/pjmedia/src/pjmedia-audiodev/alsa_dev.c index 90eca542..c2cc4e5e 100644 --- a/pjmedia/src/pjmedia-audiodev/alsa_dev.c +++ b/pjmedia/src/pjmedia-audiodev/alsa_dev.c @@ -57,6 +57,7 @@ */ static pj_status_t alsa_factory_init(pjmedia_aud_dev_factory *f); static pj_status_t alsa_factory_destroy(pjmedia_aud_dev_factory *f); +static pj_status_t alsa_factory_refresh(pjmedia_aud_dev_factory *f); static unsigned alsa_factory_get_dev_count(pjmedia_aud_dev_factory *f); static pj_status_t alsa_factory_get_dev_info(pjmedia_aud_dev_factory *f, unsigned index, @@ -92,6 +93,7 @@ struct alsa_factory pjmedia_aud_dev_factory base; pj_pool_factory *pf; pj_pool_t *pool; + pj_pool_t *base_pool; unsigned dev_cnt; pjmedia_aud_dev_info devs[MAX_DEVICES]; @@ -133,7 +135,8 @@ static pjmedia_aud_dev_factory_op alsa_factory_op = &alsa_factory_get_dev_count, &alsa_factory_get_dev_info, &alsa_factory_default_param, - &alsa_factory_create_stream + &alsa_factory_create_stream, + &alsa_factory_refresh }; static pjmedia_aud_stream_op alsa_stream_op = @@ -146,6 +149,20 @@ static pjmedia_aud_stream_op alsa_stream_op = &alsa_stream_destroy }; +static void null_alsa_error_handler (const char *file, + int line, + const char *function, + int err, + const char *fmt, + ...) +{ + PJ_UNUSED_ARG(file); + PJ_UNUSED_ARG(line); + PJ_UNUSED_ARG(function); + PJ_UNUSED_ARG(err); + PJ_UNUSED_ARG(fmt); +} + static void alsa_error_handler (const char *file, int line, const char *function, @@ -174,10 +191,9 @@ static void alsa_error_handler (const char *file, } -static pj_status_t add_dev (struct alsa_factory *af, int card, int device) +static pj_status_t add_dev (struct alsa_factory *af, const char *dev_name) { pjmedia_aud_dev_info *adi; - char dev_name[32]; snd_pcm_t* pcm; int pb_result, ca_result; @@ -186,8 +202,7 @@ static pj_status_t add_dev (struct alsa_factory *af, int card, int device) adi = &af->devs[af->dev_cnt]; - TRACE_((THIS_FILE, "add_dev (%d, %d): Enter", card, device)); - sprintf (dev_name, ALSA_DEVICE_NAME, card, device); + TRACE_((THIS_FILE, "add_dev (%s): Enter", dev_name)); /* Try to open the device in playback mode */ pb_result = snd_pcm_open (&pcm, dev_name, SND_PCM_STREAM_PLAYBACK, 0); @@ -245,10 +260,10 @@ pjmedia_aud_dev_factory* pjmedia_alsa_factory(pj_pool_factory *pf) struct alsa_factory *af; pj_pool_t *pool; - pool = pj_pool_create(pf, "alsa_aud", 256, 256, NULL); + pool = pj_pool_create(pf, "alsa_aud_base", 256, 256, NULL); af = PJ_POOL_ZALLOC_T(pool, struct alsa_factory); af->pf = pf; - af->pool = pool; + af->base_pool = pool; af->base.op = &alsa_factory_op; return &af->base; @@ -258,23 +273,11 @@ pjmedia_aud_dev_factory* pjmedia_alsa_factory(pj_pool_factory *pf) /* API: init factory */ static pj_status_t alsa_factory_init(pjmedia_aud_dev_factory *f) { - struct alsa_factory *af = (struct alsa_factory*)f; - int card, device; - - TRACE_((THIS_FILE, "pjmedia_snd_init: Enumerate sound devices")); - /* Enumerate sound devices */ - for (card=0; carddev_cnt)); + PJ_LOG(4,(THIS_FILE, "ALSA initialized")); return PJ_SUCCESS; } @@ -284,9 +287,12 @@ static pj_status_t alsa_factory_destroy(pjmedia_aud_dev_factory *f) { struct alsa_factory *af = (struct alsa_factory*)f; - if (af->pool) { - pj_pool_t *pool = af->pool; - af->pool = NULL; + if (af->pool) + pj_pool_release(af->pool); + + if (af->base_pool) { + pj_pool_t *pool = af->base_pool; + af->base_pool = NULL; pj_pool_release(pool); } @@ -297,6 +303,54 @@ static pj_status_t alsa_factory_destroy(pjmedia_aud_dev_factory *f) } +/* API: refresh the device list */ +static pj_status_t alsa_factory_refresh(pjmedia_aud_dev_factory *f) +{ + struct alsa_factory *af = (struct alsa_factory*)f; + char **hints, **n; + int err; + + TRACE_((THIS_FILE, "pjmedia_snd_init: Enumerate sound devices")); + + if (af->pool != NULL) { + pj_pool_release(af->pool); + af->pool = NULL; + } + + af->pool = pj_pool_create(af->pf, "alsa_aud", 256, 256, NULL); + af->dev_cnt = 0; + + /* Enumerate sound devices */ + err = snd_device_name_hint(-1, "pcm", (void***)&hints); + if (err != 0) + return PJMEDIA_EAUD_SYSERR; + + /* Set a null error handler prior to enumeration to suppress errors */ + snd_lib_error_set_handler(null_alsa_error_handler); + + n = hints; + while (*n != NULL) { + char *name = snd_device_name_get_hint(*n, "NAME"); + if (name != NULL && 0 != strcmp("null", name)) { + add_dev(af, name); + free(name); + } + n++; + } + + /* Install error handler after enumeration, otherwise we'll get many + * error messages about invalid card/device ID. + */ + snd_lib_error_set_handler(alsa_error_handler); + + err = snd_device_name_free_hint((void**)hints); + + PJ_LOG(4,(THIS_FILE, "ALSA driver found %d devices", af->dev_cnt)); + + return PJ_SUCCESS; +} + + /* API: get device count */ static unsigned alsa_factory_get_dev_count(pjmedia_aud_dev_factory *f) { diff --git a/pjmedia/src/pjmedia-audiodev/audiodev.c b/pjmedia/src/pjmedia-audiodev/audiodev.c index 2a5a8e67..b9d960fe 100644 --- a/pjmedia/src/pjmedia-audiodev/audiodev.c +++ b/pjmedia/src/pjmedia-audiodev/audiodev.c @@ -491,6 +491,25 @@ PJ_DEF(pj_status_t) pjmedia_aud_subsys_shutdown(void) return PJ_SUCCESS; } +/* API: Refresh the list of sound devices installed in the system. */ +PJ_DEF(pj_status_t) pjmedia_aud_dev_refresh(void) +{ + unsigned i; + + for (i=0; if && drv->f->op->refresh) { + pj_status_t status = drv->f->op->refresh(drv->f); + if (status != PJ_SUCCESS) { + PJ_PERROR(4, (THIS_FILE, status, "Unable to refresh device " + "list for %s", drv->name)); + } + } + } + return PJ_SUCCESS; +} + /* API: Get the number of sound devices installed in the system. */ PJ_DEF(unsigned) pjmedia_aud_dev_count(void) { diff --git a/pjmedia/src/pjmedia-audiodev/coreaudio_dev.c b/pjmedia/src/pjmedia-audiodev/coreaudio_dev.c index 2cebbb34..de320488 100644 --- a/pjmedia/src/pjmedia-audiodev/coreaudio_dev.c +++ b/pjmedia/src/pjmedia-audiodev/coreaudio_dev.c @@ -178,7 +178,8 @@ static pjmedia_aud_dev_factory_op factory_op = &ca_factory_get_dev_count, &ca_factory_get_dev_info, &ca_factory_default_param, - &ca_factory_create_stream + &ca_factory_create_stream, + &ca_factory_refresh }; static pjmedia_aud_stream_op stream_op = diff --git a/pjmedia/src/pjmedia-audiodev/legacy_dev.c b/pjmedia/src/pjmedia-audiodev/legacy_dev.c index 66fad9b0..a7680f1e 100644 --- a/pjmedia/src/pjmedia-audiodev/legacy_dev.c +++ b/pjmedia/src/pjmedia-audiodev/legacy_dev.c @@ -51,6 +51,7 @@ struct legacy_stream /* Prototypes */ static pj_status_t factory_init(pjmedia_aud_dev_factory *f); static pj_status_t factory_destroy(pjmedia_aud_dev_factory *f); +static pj_status_t factory_refresh(pjmedia_aud_dev_factory *f); static unsigned factory_get_dev_count(pjmedia_aud_dev_factory *f); static pj_status_t factory_get_dev_info(pjmedia_aud_dev_factory *f, unsigned index, @@ -86,7 +87,8 @@ static pjmedia_aud_dev_factory_op factory_op = &factory_get_dev_count, &factory_get_dev_info, &factory_default_param, - &factory_create_stream + &factory_create_stream, + &factory_refresh }; static pjmedia_aud_stream_op stream_op = @@ -147,6 +149,13 @@ static pj_status_t factory_destroy(pjmedia_aud_dev_factory *f) return status; } +/* API: refresh the list of devices */ +static pj_status_t factory_refresh(pjmedia_aud_dev_factory *f) +{ + PJ_UNUSED_ARG(f); + return PJ_ENOTSUP; +} + /* API: get number of devices */ static unsigned factory_get_dev_count(pjmedia_aud_dev_factory *f) { diff --git a/pjmedia/src/pjmedia-audiodev/null_dev.c b/pjmedia/src/pjmedia-audiodev/null_dev.c index feb96d22..44ce6412 100644 --- a/pjmedia/src/pjmedia-audiodev/null_dev.c +++ b/pjmedia/src/pjmedia-audiodev/null_dev.c @@ -59,6 +59,7 @@ struct null_audio_stream /* Prototypes */ static pj_status_t null_factory_init(pjmedia_aud_dev_factory *f); static pj_status_t null_factory_destroy(pjmedia_aud_dev_factory *f); +static pj_status_t null_factory_refresh(pjmedia_aud_dev_factory *f); static unsigned null_factory_get_dev_count(pjmedia_aud_dev_factory *f); static pj_status_t null_factory_get_dev_info(pjmedia_aud_dev_factory *f, unsigned index, @@ -93,7 +94,8 @@ static pjmedia_aud_dev_factory_op factory_op = &null_factory_get_dev_count, &null_factory_get_dev_info, &null_factory_default_param, - &null_factory_create_stream + &null_factory_create_stream, + &null_factory_refresh }; static pjmedia_aud_stream_op stream_op = @@ -166,6 +168,13 @@ static pj_status_t null_factory_destroy(pjmedia_aud_dev_factory *f) return PJ_SUCCESS; } +/* API: refresh the list of devices */ +static pj_status_t null_factory_refresh(pjmedia_aud_dev_factory *f) +{ + PJ_UNUSED_ARG(f); + return PJ_SUCCESS; +} + /* API: get number of devices */ static unsigned null_factory_get_dev_count(pjmedia_aud_dev_factory *f) { diff --git a/pjmedia/src/pjmedia-audiodev/pa_dev.c b/pjmedia/src/pjmedia-audiodev/pa_dev.c index 1188b8ab..fb33a6aa 100644 --- a/pjmedia/src/pjmedia-audiodev/pa_dev.c +++ b/pjmedia/src/pjmedia-audiodev/pa_dev.c @@ -106,6 +106,7 @@ struct pa_aud_stream /* Factory prototypes */ static pj_status_t pa_init(pjmedia_aud_dev_factory *f); static pj_status_t pa_destroy(pjmedia_aud_dev_factory *f); +static pj_status_t pa_refresh(pjmedia_aud_dev_factory *f); static unsigned pa_get_dev_count(pjmedia_aud_dev_factory *f); static pj_status_t pa_get_dev_info(pjmedia_aud_dev_factory *f, unsigned index, @@ -141,7 +142,8 @@ static pjmedia_aud_dev_factory_op pa_op = &pa_get_dev_count, &pa_get_dev_info, &pa_default_param, - &pa_create_stream + &pa_create_stream, + &pa_refresh }; static pjmedia_aud_stream_op pa_strm_op = @@ -487,6 +489,14 @@ static pj_status_t pa_destroy(pjmedia_aud_dev_factory *f) } +/* API: Refresh the device list. */ +static pj_status_t pa_refresh(pjmedia_aud_dev_factory *f) +{ + PJ_UNUSED_ARG(f); + return PJ_ENOTSUP; +} + + /* API: Get device count. */ static unsigned pa_get_dev_count(pjmedia_aud_dev_factory *f) { diff --git a/pjmedia/src/pjmedia-audiodev/symb_aps_dev.cpp b/pjmedia/src/pjmedia-audiodev/symb_aps_dev.cpp index 80ca7c51..aeb5cb3b 100644 --- a/pjmedia/src/pjmedia-audiodev/symb_aps_dev.cpp +++ b/pjmedia/src/pjmedia-audiodev/symb_aps_dev.cpp @@ -120,6 +120,7 @@ struct aps_stream /* Prototypes */ static pj_status_t factory_init(pjmedia_aud_dev_factory *f); static pj_status_t factory_destroy(pjmedia_aud_dev_factory *f); +static pj_status_t factory_refresh(pjmedia_aud_dev_factory *f); static unsigned factory_get_dev_count(pjmedia_aud_dev_factory *f); static pj_status_t factory_get_dev_info(pjmedia_aud_dev_factory *f, unsigned index, @@ -155,7 +156,8 @@ static pjmedia_aud_dev_factory_op factory_op = &factory_get_dev_count, &factory_get_dev_info, &factory_default_param, - &factory_create_stream + &factory_create_stream, + &factory_refresh }; static pjmedia_aud_stream_op stream_op = @@ -1444,6 +1446,13 @@ static pj_status_t factory_destroy(pjmedia_aud_dev_factory *f) return PJ_SUCCESS; } +/* API: refresh the device list */ +static pj_status_t factory_refresh(pjmedia_aud_dev_factory *f) +{ + PJ_UNUSED_ARG(f); + return PJ_ENOTSUP; +} + /* API: get number of devices */ static unsigned factory_get_dev_count(pjmedia_aud_dev_factory *f) { diff --git a/pjmedia/src/pjmedia-audiodev/symb_mda_dev.cpp b/pjmedia/src/pjmedia-audiodev/symb_mda_dev.cpp index 2df93836..0c9d5e28 100644 --- a/pjmedia/src/pjmedia-audiodev/symb_mda_dev.cpp +++ b/pjmedia/src/pjmedia-audiodev/symb_mda_dev.cpp @@ -85,6 +85,7 @@ struct mda_stream /* Prototypes */ static pj_status_t factory_init(pjmedia_aud_dev_factory *f); static pj_status_t factory_destroy(pjmedia_aud_dev_factory *f); +static pj_status_t factory_refresh(pjmedia_aud_dev_factory *f); static unsigned factory_get_dev_count(pjmedia_aud_dev_factory *f); static pj_status_t factory_get_dev_info(pjmedia_aud_dev_factory *f, unsigned index, @@ -120,7 +121,8 @@ static pjmedia_aud_dev_factory_op factory_op = &factory_get_dev_count, &factory_get_dev_info, &factory_default_param, - &factory_create_stream + &factory_create_stream, + &factory_refresh }; static pjmedia_aud_stream_op stream_op = @@ -834,6 +836,13 @@ static pj_status_t factory_destroy(pjmedia_aud_dev_factory *f) return PJ_SUCCESS; } +/* API: refresh the device list */ +static pj_status_t factory_refresh(pjmedia_aud_dev_factory *f) +{ + PJ_UNUSED_ARG(f); + return PJ_ENOTSUP; +} + /* API: get number of devices */ static unsigned factory_get_dev_count(pjmedia_aud_dev_factory *f) { diff --git a/pjmedia/src/pjmedia-audiodev/symb_vas_dev.cpp b/pjmedia/src/pjmedia-audiodev/symb_vas_dev.cpp index 2f35a660..d58cef59 100644 --- a/pjmedia/src/pjmedia-audiodev/symb_vas_dev.cpp +++ b/pjmedia/src/pjmedia-audiodev/symb_vas_dev.cpp @@ -130,6 +130,7 @@ struct vas_stream /* Prototypes */ static pj_status_t factory_init(pjmedia_aud_dev_factory *f); static pj_status_t factory_destroy(pjmedia_aud_dev_factory *f); +static pj_status_t factory_refresh(pjmedia_aud_dev_factory *f); static unsigned factory_get_dev_count(pjmedia_aud_dev_factory *f); static pj_status_t factory_get_dev_info(pjmedia_aud_dev_factory *f, unsigned index, @@ -165,7 +166,8 @@ static pjmedia_aud_dev_factory_op factory_op = &factory_get_dev_count, &factory_get_dev_info, &factory_default_param, - &factory_create_stream + &factory_create_stream, + &factory_refresh }; static pjmedia_aud_stream_op stream_op = @@ -1476,6 +1478,13 @@ static pj_status_t factory_destroy(pjmedia_aud_dev_factory *f) return PJ_SUCCESS; } +/* API: refresh the device list */ +static pj_status_t factory_refresh(pjmedia_aud_dev_factory *f) +{ + PJ_UNUSED_ARG(f); + return PJ_ENOTSUP; +} + /* API: get number of devices */ static unsigned factory_get_dev_count(pjmedia_aud_dev_factory *f) { diff --git a/pjmedia/src/pjmedia-audiodev/wmme_dev.c b/pjmedia/src/pjmedia-audiodev/wmme_dev.c index 4a577c78..095aead9 100644 --- a/pjmedia/src/pjmedia-audiodev/wmme_dev.c +++ b/pjmedia/src/pjmedia-audiodev/wmme_dev.c @@ -38,6 +38,16 @@ # pragma warning(pop) #endif +#ifndef PJMEDIA_WMME_DEV_USE_MMDEVICE_API +# define PJMEDIAWMME_DEV_USE_MMDEVICE_API \ + (defined(_WIN32_WINNT) && (_WIN32_WINNT>=0x0600)) +#endif + +#if PJMEDIA_WMME_DEV_USE_MMDEVICE_API != 0 +# define DRV_QUERYFUNCTIONINSTANCEID (DRV_RESERVED + 17) +# define DRV_QUERYFUNCTIONINSTANCEIDSIZE (DRV_RESERVED + 18) +#endif + /* mingw lacks WAVE_FORMAT_ALAW/MULAW */ #ifndef WAVE_FORMAT_ALAW # define WAVE_FORMAT_ALAW 0x0006 @@ -60,12 +70,14 @@ struct wmme_dev_info { pjmedia_aud_dev_info info; unsigned deviceId; + const wchar_t *endpointId; }; /* WMME factory */ struct wmme_factory { pjmedia_aud_dev_factory base; + pj_pool_t *base_pool; pj_pool_t *pool; pj_pool_factory *pf; @@ -121,6 +133,7 @@ struct wmme_stream /* Prototypes */ static pj_status_t factory_init(pjmedia_aud_dev_factory *f); static pj_status_t factory_destroy(pjmedia_aud_dev_factory *f); +static pj_status_t factory_refresh(pjmedia_aud_dev_factory *f); static unsigned factory_get_dev_count(pjmedia_aud_dev_factory *f); static pj_status_t factory_get_dev_info(pjmedia_aud_dev_factory *f, unsigned index, @@ -156,7 +169,8 @@ static pjmedia_aud_dev_factory_op factory_op = &factory_get_dev_count, &factory_get_dev_info, &factory_default_param, - &factory_create_stream + &factory_create_stream, + &factory_refresh }; static pjmedia_aud_stream_op stream_op = @@ -181,15 +195,123 @@ pjmedia_aud_dev_factory* pjmedia_wmme_factory(pj_pool_factory *pf) struct wmme_factory *f; pj_pool_t *pool; - pool = pj_pool_create(pf, "WMME", 1000, 1000, NULL); + pool = pj_pool_create(pf, "WMME base", 1000, 1000, NULL); f = PJ_POOL_ZALLOC_T(pool, struct wmme_factory); f->pf = pf; - f->pool = pool; + f->base_pool = pool; f->base.op = &factory_op; return &f->base; } +/* Internal: Windows Vista and Windows 7 have their device + * names truncated when using the waveXXX api. The names + * should be acquired from the MMDevice APIs + */ +#if PJMEDIA_WMME_DEV_USE_MMDEVICE_API != 0 + +#define COBJMACROS +#include +#define INITGUID +#include +#include + +DEFINE_GUID(CLSID_MMDeviceEnumerator, 0xBCDE0395, 0xE52F, 0x467C, + 0x8E, 0x3D, 0xC4, 0x57, 0x92, 0x91, 0x69, 0x2E); +DEFINE_GUID(IID_IMMDeviceEnumerator, 0xA95664D2, 0x9614, 0x4F35, + 0xA7, 0x46, 0xDE, 0x8D, 0xB6, 0x36, 0x17, 0xE6); + +static void get_dev_names(pjmedia_aud_dev_factory *f) +{ + struct wmme_factory *wf = (struct wmme_factory*)f; + HRESULT coinit = S_OK; + HRESULT hr = S_OK; + IMMDeviceEnumerator *pEnumerator = NULL; + IMMDeviceCollection *pDevices = NULL; + UINT cDevices = 0; + UINT nDevice = 0; + + coinit = CoInitializeEx(NULL, COINIT_MULTITHREADED); + if (coinit == RPC_E_CHANGED_MODE) + coinit = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); + if (FAILED(coinit)) + goto on_error; + + hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, + CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, + (void**)&pEnumerator); + if (FAILED(hr)) + goto on_error; + hr = IMMDeviceEnumerator_EnumAudioEndpoints(pEnumerator, eAll, + DEVICE_STATE_ACTIVE, + &pDevices); + if (FAILED(hr)) + goto on_error; + hr = IMMDeviceCollection_GetCount(pDevices, &cDevices); + if (FAILED(hr)) + goto on_error; + + for (nDevice = 0; nDevice < cDevices; ++nDevice) { + IMMDevice *pDevice = NULL; + IPropertyStore *pProps = NULL; + LPWSTR pwszID = NULL; + PROPVARIANT varName; + unsigned i; + + PropVariantInit(&varName); + + hr = IMMDeviceCollection_Item(pDevices, nDevice, &pDevice); + if (FAILED(hr)) + goto cleanup; + hr = IMMDevice_GetId(pDevice, &pwszID); + if (FAILED(hr)) + goto cleanup; + hr = IMMDevice_OpenPropertyStore(pDevice, STGM_READ, &pProps); + if (FAILED(hr)) + goto cleanup; + hr = IPropertyStore_GetValue(pProps, &PKEY_Device_FriendlyName, + &varName); + if (FAILED(hr)) + goto cleanup; + + for (i = 0; i < wf->dev_count; ++i) { + if (0 == wcscmp(wf->dev_info[i].endpointId, pwszID)) { + wcstombs(wf->dev_info[i].info.name, varName.pwszVal, + sizeof(wf->dev_info[i].info.name)); + break; + } + } + + PropVariantClear(&varName); + + cleanup: + if (pProps) + IPropertyStore_Release(pProps); + if (pwszID) + CoTaskMemFree(pwszID); + if (pDevice) + hr = IMMDevice_Release(pDevice); + } + +on_error: + if (pDevices) + hr = IMMDeviceCollection_Release(pDevices); + + if (pEnumerator) + hr = IMMDeviceEnumerator_Release(pEnumerator); + + if (SUCCEEDED(coinit)) + CoUninitialize(); +} + +#else + +static void get_dev_names(pjmedia_aud_dev_factory *f) +{ + PJ_UNUSED_ARG(f); +} + +#endif /* Internal: build device info from WAVEINCAPS/WAVEOUTCAPS */ static void build_dev_info(UINT deviceId, struct wmme_dev_info *wdi, @@ -260,6 +382,17 @@ static void build_dev_info(UINT deviceId, struct wmme_dev_info *wdi, /* API: init factory */ static pj_status_t factory_init(pjmedia_aud_dev_factory *f) +{ + pj_status_t ret = factory_refresh(f); + if (ret != PJ_SUCCESS) + return ret; + + PJ_LOG(4, (THIS_FILE, "WMME initialized")); + return PJ_SUCCESS; +} + +/* API: refresh the device list */ +static pj_status_t factory_refresh(pjmedia_aud_dev_factory *f) { struct wmme_factory *wf = (struct wmme_factory*)f; unsigned c; @@ -267,8 +400,14 @@ static pj_status_t factory_init(pjmedia_aud_dev_factory *f) int inputDeviceCount, outputDeviceCount, devCount=0; pj_bool_t waveMapperAdded = PJ_FALSE; + if (wf->pool != NULL) { + pj_pool_release(wf->pool); + wf->pool = NULL; + } + /* Enumerate sound devices */ wf->dev_count = 0; + wf->pool = pj_pool_create(wf->pf, "WMME", 1000, 1000, NULL); inputDeviceCount = waveInGetNumDevs(); devCount += inputDeviceCount; @@ -314,6 +453,7 @@ static pj_status_t factory_init(pjmedia_aud_dev_factory *f) if (mr == MMSYSERR_NOERROR) { build_dev_info(WAVE_MAPPER, &wf->dev_info[wf->dev_count], &wic, &woc); + wf->dev_info[wf->dev_count].endpointId = L""; ++wf->dev_count; waveMapperAdded = PJ_TRUE; } @@ -327,6 +467,7 @@ static pj_status_t factory_init(pjmedia_aud_dev_factory *f) UINT uDeviceID = (UINT)((i==-1) ? WAVE_MAPPER : i); WAVEINCAPS wic; MMRESULT mr; + DWORD cbEndpointId; pj_bzero(&wic, sizeof(WAVEINCAPS)); @@ -340,6 +481,27 @@ static pj_status_t factory_init(pjmedia_aud_dev_factory *f) build_dev_info(uDeviceID, &wf->dev_info[wf->dev_count], &wic, NULL); + +#if PJMEDIA_WMME_DEV_USE_MMDEVICE_API != 0 + /* Try to get the endpoint id of the audio device */ + wf->dev_info[wf->dev_count].endpointId = L""; + + mr = waveInMessage((HWAVEIN)IntToPtr(uDeviceID), + DRV_QUERYFUNCTIONINSTANCEIDSIZE, + (DWORD_PTR)&cbEndpointId, (DWORD_PTR)NULL); + if (mr == MMSYSERR_NOERROR) { + const wchar_t **epid = &wf->dev_info[wf->dev_count].endpointId; + *epid = (const wchar_t*) pj_pool_calloc(wf->pool, + cbEndpointId, 1); + mr = waveInMessage((HWAVEIN)IntToPtr(uDeviceID), + DRV_QUERYFUNCTIONINSTANCEID, + (DWORD_PTR)*epid, + cbEndpointId); + } +#else + PJ_UNUSED_ARG(cbEndpointId); +#endif + ++wf->dev_count; } } @@ -351,6 +513,7 @@ static pj_status_t factory_init(pjmedia_aud_dev_factory *f) UINT uDeviceID = (UINT)((i==-1) ? WAVE_MAPPER : i); WAVEOUTCAPS woc; MMRESULT mr; + DWORD cbEndpointId; pj_bzero(&woc, sizeof(WAVEOUTCAPS)); @@ -364,11 +527,34 @@ static pj_status_t factory_init(pjmedia_aud_dev_factory *f) build_dev_info(uDeviceID, &wf->dev_info[wf->dev_count], NULL, &woc); + +#if PJMEDIA_WMME_DEV_USE_MMDEVICE_API != 0 + /* Try to get the endpoint id of the audio device */ + wf->dev_info[wf->dev_count].endpointId = L""; + + mr = waveOutMessage((HWAVEOUT)IntToPtr(uDeviceID), + DRV_QUERYFUNCTIONINSTANCEIDSIZE, + (DWORD_PTR)&cbEndpointId, (DWORD_PTR)NULL); + if (mr == MMSYSERR_NOERROR) { + const wchar_t **epid = &wf->dev_info[wf->dev_count].endpointId; + *epid = (const wchar_t*)pj_pool_calloc(wf->pool, + cbEndpointId, 1); + mr = waveOutMessage((HWAVEOUT)IntToPtr(uDeviceID), + DRV_QUERYFUNCTIONINSTANCEID, + (DWORD_PTR)*epid, cbEndpointId); + } +#else + PJ_UNUSED_ARG(cbEndpointId); +#endif + ++wf->dev_count; } } - PJ_LOG(4, (THIS_FILE, "WMME initialized, found %d devices:", + /* On Windows Vista and Windows 7 get the full device names */ + get_dev_names(f); + + PJ_LOG(4, (THIS_FILE, "WMME found %d devices:", wf->dev_count)); for (c = 0; c < wf->dev_count; ++c) { PJ_LOG(4, (THIS_FILE, " dev_id %d: %s (in=%d, out=%d)", @@ -385,9 +571,10 @@ static pj_status_t factory_init(pjmedia_aud_dev_factory *f) static pj_status_t factory_destroy(pjmedia_aud_dev_factory *f) { struct wmme_factory *wf = (struct wmme_factory*)f; - pj_pool_t *pool = wf->pool; + pj_pool_t *pool = wf->base_pool; - wf->pool = NULL; + pj_pool_release(wf->pool); + wf->base_pool = NULL; pj_pool_release(pool); return PJ_SUCCESS; diff --git a/pjmedia/src/pjmedia/sound_port.c b/pjmedia/src/pjmedia/sound_port.c index 1029fc3d..7720b630 100644 --- a/pjmedia/src/pjmedia/sound_port.c +++ b/pjmedia/src/pjmedia/sound_port.c @@ -48,6 +48,7 @@ struct pjmedia_snd_port unsigned channel_count; unsigned samples_per_frame; unsigned bits_per_sample; + unsigned options; /* software ec */ pjmedia_echo_state *ec_state; @@ -277,7 +278,9 @@ static pj_status_t start_sound_device( pj_pool_t *pool, } /* Start sound stream. */ - status = pjmedia_aud_stream_start(snd_port->aud_stream); + if (!(snd_port->options & PJMEDIA_SND_PORT_NO_AUTO_START)) { + status = pjmedia_aud_stream_start(snd_port->aud_stream); + } if (status != PJ_SUCCESS) { pjmedia_aud_stream_destroy(snd_port->aud_stream); snd_port->aud_stream = NULL; @@ -324,22 +327,23 @@ PJ_DEF(pj_status_t) pjmedia_snd_port_create( pj_pool_t *pool, unsigned options, pjmedia_snd_port **p_port) { - pjmedia_aud_param param; + pjmedia_snd_port_param param; pj_status_t status; PJ_UNUSED_ARG(options); - status = pjmedia_aud_dev_default_param(rec_id, ¶m); + status = pjmedia_aud_dev_default_param(rec_id, ¶m.base); if (status != PJ_SUCCESS) return status; - param.dir = PJMEDIA_DIR_CAPTURE_PLAYBACK; - param.rec_id = rec_id; - param.play_id = play_id; - param.clock_rate = clock_rate; - param.channel_count = channel_count; - param.samples_per_frame = samples_per_frame; - param.bits_per_sample = bits_per_sample; + param.base.dir = PJMEDIA_DIR_CAPTURE_PLAYBACK; + param.base.rec_id = rec_id; + param.base.play_id = play_id; + param.base.clock_rate = clock_rate; + param.base.channel_count = channel_count; + param.base.samples_per_frame = samples_per_frame; + param.base.bits_per_sample = bits_per_sample; + param.options = options; return pjmedia_snd_port_create2(pool, ¶m, p_port); } @@ -356,21 +360,22 @@ PJ_DEF(pj_status_t) pjmedia_snd_port_create_rec( pj_pool_t *pool, unsigned options, pjmedia_snd_port **p_port) { - pjmedia_aud_param param; + pjmedia_snd_port_param param; pj_status_t status; PJ_UNUSED_ARG(options); - status = pjmedia_aud_dev_default_param(dev_id, ¶m); + status = pjmedia_aud_dev_default_param(dev_id, ¶m.base); if (status != PJ_SUCCESS) return status; - param.dir = PJMEDIA_DIR_CAPTURE; - param.rec_id = dev_id; - param.clock_rate = clock_rate; - param.channel_count = channel_count; - param.samples_per_frame = samples_per_frame; - param.bits_per_sample = bits_per_sample; + param.base.dir = PJMEDIA_DIR_CAPTURE; + param.base.rec_id = dev_id; + param.base.clock_rate = clock_rate; + param.base.channel_count = channel_count; + param.base.samples_per_frame = samples_per_frame; + param.base.bits_per_sample = bits_per_sample; + param.options = options; return pjmedia_snd_port_create2(pool, ¶m, p_port); } @@ -388,21 +393,22 @@ PJ_DEF(pj_status_t) pjmedia_snd_port_create_player( pj_pool_t *pool, unsigned options, pjmedia_snd_port **p_port) { - pjmedia_aud_param param; + pjmedia_snd_port_param param; pj_status_t status; PJ_UNUSED_ARG(options); - status = pjmedia_aud_dev_default_param(dev_id, ¶m); + status = pjmedia_aud_dev_default_param(dev_id, ¶m.base); if (status != PJ_SUCCESS) return status; - param.dir = PJMEDIA_DIR_PLAYBACK; - param.play_id = dev_id; - param.clock_rate = clock_rate; - param.channel_count = channel_count; - param.samples_per_frame = samples_per_frame; - param.bits_per_sample = bits_per_sample; + param.base.dir = PJMEDIA_DIR_PLAYBACK; + param.base.play_id = dev_id; + param.base.clock_rate = clock_rate; + param.base.channel_count = channel_count; + param.base.samples_per_frame = samples_per_frame; + param.base.bits_per_sample = bits_per_sample; + param.options = options; return pjmedia_snd_port_create2(pool, ¶m, p_port); } @@ -412,7 +418,7 @@ PJ_DEF(pj_status_t) pjmedia_snd_port_create_player( pj_pool_t *pool, * Create sound port. */ PJ_DEF(pj_status_t) pjmedia_snd_port_create2(pj_pool_t *pool, - const pjmedia_aud_param *prm, + const pjmedia_snd_port_param *prm, pjmedia_snd_port **p_port) { pjmedia_snd_port *snd_port; @@ -423,15 +429,16 @@ PJ_DEF(pj_status_t) pjmedia_snd_port_create2(pj_pool_t *pool, snd_port = PJ_POOL_ZALLOC_T(pool, pjmedia_snd_port); PJ_ASSERT_RETURN(snd_port, PJ_ENOMEM); - snd_port->dir = prm->dir; - snd_port->rec_id = prm->rec_id; - snd_port->play_id = prm->play_id; + snd_port->dir = prm->base.dir; + snd_port->rec_id = prm->base.rec_id; + snd_port->play_id = prm->base.play_id; snd_port->dir = PJMEDIA_DIR_CAPTURE_PLAYBACK; - snd_port->clock_rate = prm->clock_rate; - snd_port->channel_count = prm->channel_count; - snd_port->samples_per_frame = prm->samples_per_frame; - snd_port->bits_per_sample = prm->bits_per_sample; - pj_memcpy(&snd_port->aud_param, prm, sizeof(*prm)); + snd_port->clock_rate = prm->base.clock_rate; + snd_port->channel_count = prm->base.channel_count; + snd_port->samples_per_frame = prm->base.samples_per_frame; + snd_port->bits_per_sample = prm->base.bits_per_sample; + pj_memcpy(&snd_port->aud_param, prm, sizeof(snd_port->aud_param)); + snd_port->options = prm->options; /* Start sound device immediately. * If there's no port connected, the sound callback will return -- cgit v1.2.3