- New convention about default audio device ID (now there is different ID for default capture/playback device. It should be backward compatible)
- Fixed crash if sound port is unable to open sound device
- Testing with the switchboard



git-svn-id: https://svn.pjsip.org/repos/pjproject/branches/projects/aps-direct@2469 74dad513-b988-da41-8d7b-12977e46ad98
diff --git a/pjmedia/include/pjmedia-audiodev/audiodev.h b/pjmedia/include/pjmedia-audiodev/audiodev.h
index e051f2f..88b3d13 100644
--- a/pjmedia/include/pjmedia-audiodev/audiodev.h
+++ b/pjmedia/include/pjmedia-audiodev/audiodev.h
@@ -56,10 +56,27 @@
  */
 typedef pj_int32_t pjmedia_aud_dev_index;
 
-/** 
- * Constant to denote default device 
+/**
+ * Device index constants.
  */
-#define PJMEDIA_AUD_DEV_DEFAULT  (-1)
+enum
+{
+    /** 
+     * Constant to denote default capture device 
+     */
+    PJMEDIA_AUD_DEFAULT_CAPTURE_DEV = -1,
+
+    /** 
+     * Constant to denote default playback device 
+     */
+    PJMEDIA_AUD_DEFAULT_PLAYBACK_DEV = -2,
+
+    /**
+     * Constant to denote invalid device index.
+     */
+    PJMEDIA_AUD_INVALID_DEV = -3
+};
+
 
 /** 
  * Type for device unique identifier. The unique device ID can be used to save
@@ -596,12 +613,18 @@
 PJ_DECL(pj_status_t) pjmedia_aud_stream_destroy(pjmedia_aud_stream *strm);
 
 
+/* Audio subsystem not initialized */
+#define PJMEDIA_EAUD_INIT	-1
+
 /* Invalid audio device */
 #define PJMEDIA_EAUD_INVDEV	-1
 
 /* Found no devices */
 #define PJMEDIA_EAUD_NODEV	-1
 
+/* Unable to find default device */
+#define PJMEDIA_EAUD_NODEFDEV	-1
+
 /* Device not ready */
 #define PJMEDIA_EAUD_NOTREADY	-1
 
diff --git a/pjmedia/include/pjmedia-audiodev/audiodev_imp.h b/pjmedia/include/pjmedia-audiodev/audiodev_imp.h
index a05b34c..b993c6b 100644
--- a/pjmedia/include/pjmedia-audiodev/audiodev_imp.h
+++ b/pjmedia/include/pjmedia-audiodev/audiodev_imp.h
@@ -100,10 +100,11 @@
  */
 struct pjmedia_aud_dev_factory
 {
-    /** Internal data to be initialized by the framework. */
+    /** Internal data to be initialized by audio subsystem. */
     struct {
-	unsigned    id;
-    } internal;
+	/** Driver index */
+	unsigned drv_idx;
+    } sys;
 
     /** Operations */
     pjmedia_aud_dev_factory_op *op;
@@ -158,8 +159,11 @@
  */
 struct pjmedia_aud_stream
 {
-    /** Factory id (internal) */
-    unsigned factory_id;
+    /** Internal data to be initialized by audio subsystem */
+    struct {
+	/** Driver index */
+	unsigned drv_idx;
+    } sys;
 
     /** Operations */
     pjmedia_aud_stream_op *op;
diff --git a/pjmedia/src/pjmedia-audiodev/audiodev.c b/pjmedia/src/pjmedia-audiodev/audiodev.c
index 33e333c..275cf93 100644
--- a/pjmedia/src/pjmedia-audiodev/audiodev.c
+++ b/pjmedia/src/pjmedia-audiodev/audiodev.c
@@ -79,6 +79,9 @@
     char		     name[32];	/* Driver name			    */
     unsigned		     dev_cnt;	/* Number of devices		    */
     unsigned		     start_idx;	/* Start index in global list	    */
+    int			     rec_dev_idx;/* Default capture device.	    */
+    int			     play_dev_idx;/* Default playback device	    */
+    int			     dev_idx;	/* Default device.		    */
 };
 
 /* The audio subsystem */
@@ -96,6 +99,106 @@
 } aud_subsys;
 
 
+/* Internal: init driver */
+static pj_status_t init_driver(unsigned drv_idx)
+{
+    struct driver *drv = &aud_subsys.drv[drv_idx];
+    pjmedia_aud_dev_factory *f;
+    unsigned i, dev_cnt;
+    pj_status_t status;
+
+    /* Create the factory */
+    f = (*drv->create)(aud_subsys.pf);
+    if (!f)
+	return PJ_EUNKNOWN;
+
+    /* Call factory->init() */
+    status = f->op->init(f);
+    if (status != PJ_SUCCESS) {
+	f->op->destroy(f);
+	return status;
+    }
+
+    /* Get number of devices */
+    dev_cnt = f->op->get_dev_count(f);
+    if (dev_cnt + aud_subsys.dev_cnt > MAX_DEVS) {
+	PJ_LOG(4,(THIS_FILE, "%d device(s) cannot be registered because"
+			      " there are too many devices",
+			      aud_subsys.dev_cnt + dev_cnt - MAX_DEVS));
+	dev_cnt = MAX_DEVS - aud_subsys.dev_cnt;
+    }
+    if (dev_cnt == 0) {
+	f->op->destroy(f);
+	return PJMEDIA_EAUD_NODEV;
+    }
+
+    /* Fill in default devices */
+    drv->play_dev_idx = drv->rec_dev_idx = drv->dev_idx = -1;
+    for (i=0; i<dev_cnt; ++i) {
+	pjmedia_aud_dev_info info;
+
+	status = f->op->get_dev_info(f, i, &info);
+	if (status != PJ_SUCCESS) {
+	    f->op->destroy(f);
+	    return status;
+	}
+
+	if (drv->name[0]=='\0') {
+	    /* Set driver name */
+	    pj_ansi_strncpy(drv->name, info.driver, sizeof(drv->name));
+	    drv->name[sizeof(drv->name)-1] = '\0';
+	}
+
+	if (drv->play_dev_idx < 0 && info.output_count) {
+	    /* Set default playback device */
+	    drv->play_dev_idx = i;
+	}
+	if (drv->rec_dev_idx < 0 && info.input_count) {
+	    /* Set default capture device */
+	    drv->rec_dev_idx = i;
+	}
+	if (drv->dev_idx < 0 && info.input_count &&
+	    info.output_count)
+	{
+	    /* Set default capture and playback device */
+	    drv->dev_idx = i;
+	}
+
+	if (drv->play_dev_idx >= 0 && drv->rec_dev_idx >= 0 && 
+	    drv->dev_idx >= 0) 
+	{
+	    /* Done. */
+	    break;
+	}
+    }
+
+    /* Register the factory */
+    drv->f = f;
+    drv->f->sys.drv_idx = drv_idx;
+    drv->start_idx = aud_subsys.dev_cnt;
+    drv->dev_cnt = dev_cnt;
+
+    /* Register devices to global list */
+    for (i=0; i<dev_cnt; ++i) {
+	aud_subsys.dev_list[aud_subsys.dev_cnt++] = MAKE_DEV_ID(drv_idx, i);
+    }
+
+    return PJ_SUCCESS;
+}
+
+/* Internal: deinit driver */
+static void deinit_driver(unsigned drv_idx)
+{
+    struct driver *drv = &aud_subsys.drv[drv_idx];
+
+    if (drv->f) {
+	drv->f->op->destroy(drv->f);
+	drv->f = NULL;
+    }
+
+    drv->dev_cnt = 0;
+    drv->play_dev_idx = drv->rec_dev_idx = drv->dev_idx = -1;
+}
 
 /* API: Initialize the audio subsystem. */
 PJ_DEF(pj_status_t) pjmedia_aud_subsys_init(pj_pool_factory *pf)
@@ -120,58 +223,14 @@
 
     /* Initialize each factory and build the device ID list */
     for (i=0; i<aud_subsys.drv_cnt; ++i) {
-	pjmedia_aud_dev_factory *f;
-	pjmedia_aud_dev_info info;
-	unsigned j, dev_cnt;
-
-	/* Create the factory */
-	f = (*aud_subsys.drv[i].create)(pf);
-	if (!f)
-	    continue;
-
-	/* Call factory->init() */
-	status = f->op->init(f);
+	status = init_driver(i);
 	if (status != PJ_SUCCESS) {
-	    f->op->destroy(f);
+	    deinit_driver(i);
 	    continue;
 	}
-
-	/* Build device list */
-	dev_cnt = f->op->get_dev_count(f);
-	if (dev_cnt == 0) {
-	    f->op->destroy(f);
-	    continue;
-	}
-
-	/* Get one device info */
-	status = f->op->get_dev_info(f, 0, &info);
-	if (status != PJ_SUCCESS) {
-	    f->op->destroy(f);
-	    continue;
-	}
-
-	/* Register the factory */
-	aud_subsys.drv[i].f = f;
-	aud_subsys.drv[i].f->internal.id = i;
-	aud_subsys.drv[i].start_idx = aud_subsys.dev_cnt;
-	pj_ansi_strncpy(aud_subsys.drv[i].name, info.driver,
-			sizeof(aud_subsys.drv[i].name));
-	aud_subsys.drv[i].name[sizeof(aud_subsys.drv[i].name)-1] = '\0';
-
-	/* Register devices */
-	if (aud_subsys.dev_cnt + dev_cnt > MAX_DEVS) {
-	    PJ_LOG(4,(THIS_FILE, "%d device(s) cannot be registered because"
-				  " there are too many sound devices",
-				  aud_subsys.dev_cnt + dev_cnt - MAX_DEVS));
-	    dev_cnt = MAX_DEVS - aud_subsys.dev_cnt;
-	}
-	for (j=0; j<dev_cnt; ++j) {
-	    aud_subsys.dev_list[aud_subsys.dev_cnt++] = MAKE_DEV_ID(i, j);
-	}
-
     }
 
-    return aud_subsys.drv_cnt ? PJ_SUCCESS : status;
+    return aud_subsys.dev_cnt ? PJ_SUCCESS : status;
 }
 
 /* API: get the pool factory registered to the audio subsystem. */
@@ -194,15 +253,10 @@
     --aud_subsys.init_count;
 
     for (i=0; i<aud_subsys.drv_cnt; ++i) {
-	pjmedia_aud_dev_factory *f = aud_subsys.drv[i].f;
-
-	if (!f)
-	    continue;
-
-	f->op->destroy(f);
-	aud_subsys.drv[i].f = NULL;
+	deinit_driver(i);
     }
 
+    aud_subsys.pf = NULL;
     return PJ_SUCCESS;
 }
 
@@ -235,6 +289,25 @@
     return aud_subsys.dev_cnt;
 }
 
+/* Internal: convert local index to global device index */
+static pj_status_t make_global_index(unsigned drv_idx, 
+				     pjmedia_aud_dev_index *id)
+{
+    if (*id < 0) {
+	return PJ_SUCCESS;
+    }
+
+    /* Check that factory still exists */
+    PJ_ASSERT_RETURN(aud_subsys.drv[drv_idx].f, PJ_EBUG);
+
+    /* Check that device index is valid */
+    PJ_ASSERT_RETURN(*id>=0 && *id<(int)aud_subsys.drv[drv_idx].dev_cnt, 
+		     PJ_EBUG);
+
+    *id += aud_subsys.drv[drv_idx].start_idx;
+    return PJ_SUCCESS;
+}
+
 /* Internal: lookup device id */
 static pj_status_t lookup_dev(pjmedia_aud_dev_index id,
 			      pjmedia_aud_dev_factory **p_f,
@@ -242,11 +315,37 @@
 {
     int f_id, index;
 
-    if (id == PJMEDIA_AUD_DEV_DEFAULT)
-	id = DEFAULT_DEV_ID;
+    if (id < 0) {
+	unsigned i;
 
-    PJ_ASSERT_RETURN(id>=0 && id<(int)aud_subsys.dev_cnt, 
-		     PJMEDIA_EAUD_INVDEV);
+	if (id == PJMEDIA_AUD_INVALID_DEV)
+	    return PJMEDIA_EAUD_INVDEV;
+
+	for (i=0; i<aud_subsys.drv_cnt; ++i) {
+	    struct driver *drv = &aud_subsys.drv[i];
+	    if (drv->dev_idx >= 0) {
+		id = drv->dev_idx;
+		make_global_index(i, &id);
+		break;
+	    } else if (id==PJMEDIA_AUD_DEFAULT_CAPTURE_DEV && 
+		drv->rec_dev_idx >= 0) 
+	    {
+		id = drv->rec_dev_idx;
+		make_global_index(i, &id);
+		break;
+	    } else if (id==PJMEDIA_AUD_DEFAULT_PLAYBACK_DEV && 
+		drv->play_dev_idx >= 0) 
+	    {
+		id = drv->play_dev_idx;
+		make_global_index(i, &id);
+		break;
+	    }
+	}
+
+	if (id < 0) {
+	    return PJMEDIA_EAUD_NODEFDEV;
+	}
+    }
 
     f_id = GET_FID(aud_subsys.dev_list[id]);
     index = GET_INDEX(aud_subsys.dev_list[id]);
@@ -264,25 +363,6 @@
 
 }
 
-/* Internal: convert local index to global device index */
-static pj_status_t make_global_index(pjmedia_aud_dev_factory *f,
-				     pjmedia_aud_dev_index *id)
-{
-    unsigned f_id = f->internal.id;
-
-    if (*id == PJMEDIA_AUD_DEV_DEFAULT)
-	return PJ_SUCCESS;
-
-    /* Check that factory still exists */
-    PJ_ASSERT_RETURN(f, PJ_EBUG);
-
-    /* Check that device index is valid */
-    PJ_ASSERT_RETURN(*id>=0 && *id<(int)aud_subsys.drv[f_id].dev_cnt, PJ_EBUG);
-
-    *id += aud_subsys.drv[f_id].start_idx;
-    return PJ_SUCCESS;
-}
-
 /* API: Get device information. */
 PJ_DEF(pj_status_t) pjmedia_aud_dev_get_info(pjmedia_aud_dev_index id,
 					     pjmedia_aud_dev_info *info)
@@ -291,7 +371,8 @@
     unsigned index;
     pj_status_t status;
 
-    PJ_ASSERT_RETURN(info, PJ_EINVAL);
+    PJ_ASSERT_RETURN(info && id!=PJMEDIA_AUD_INVALID_DEV, PJ_EINVAL);
+    PJ_ASSERT_RETURN(aud_subsys.pf, PJMEDIA_EAUD_INIT);
 
     status = lookup_dev(id, &f, &index);
     if (status != PJ_SUCCESS)
@@ -306,13 +387,14 @@
 					    pjmedia_aud_dev_index *id)
 {
     pjmedia_aud_dev_factory *f = NULL;
-    unsigned i, j;
+    unsigned drv_idx, dev_idx;
 
     PJ_ASSERT_RETURN(drv_name && dev_name && id, PJ_EINVAL);
+    PJ_ASSERT_RETURN(aud_subsys.pf, PJMEDIA_EAUD_INIT);
 
-    for (i=0; i<aud_subsys.drv_cnt; ++i) {
-	if (!pj_ansi_stricmp(drv_name, aud_subsys.drv[i].name)) {
-	    f = aud_subsys.drv[i].f;
+    for (drv_idx=0; drv_idx<aud_subsys.drv_cnt; ++drv_idx) {
+	if (!pj_ansi_stricmp(drv_name, aud_subsys.drv[drv_idx].name)) {
+	    f = aud_subsys.drv[drv_idx].f;
 	    break;
 	}
     }
@@ -320,11 +402,11 @@
     if (!f)
 	return PJ_ENOTFOUND;
 
-    for (j=0; j<aud_subsys.drv[i].dev_cnt; ++j) {
+    for (dev_idx=0; dev_idx<aud_subsys.drv[drv_idx].dev_cnt; ++dev_idx) {
 	pjmedia_aud_dev_info info;
 	pj_status_t status;
 
-	status = f->op->get_dev_info(f, j, &info);
+	status = f->op->get_dev_info(f, dev_idx, &info);
 	if (status != PJ_SUCCESS)
 	    return status;
 
@@ -332,11 +414,11 @@
 	    break;
     }
 
-    if (j==aud_subsys.drv[i].dev_cnt)
+    if (dev_idx==aud_subsys.drv[drv_idx].dev_cnt)
 	return PJ_ENOTFOUND;
 
-    *id = j;
-    make_global_index(f, id);
+    *id = dev_idx;
+    make_global_index(drv_idx, id);
 
     return PJ_SUCCESS;
 }
@@ -351,7 +433,8 @@
     unsigned index;
     pj_status_t status;
 
-    PJ_ASSERT_RETURN(param, PJ_EINVAL);
+    PJ_ASSERT_RETURN(param && id!=PJMEDIA_AUD_INVALID_DEV, PJ_EINVAL);
+    PJ_ASSERT_RETURN(aud_subsys.pf, PJMEDIA_EAUD_INIT);
 
     status = lookup_dev(id, &f, &index);
     if (status != PJ_SUCCESS)
@@ -362,8 +445,8 @@
 	return status;
 
     /* Normalize device IDs */
-    make_global_index(f, &param->rec_id);
-    make_global_index(f, &param->play_id);
+    make_global_index(f->sys.drv_idx, &param->rec_id);
+    make_global_index(f->sys.drv_idx, &param->play_id);
 
     return PJ_SUCCESS;
 }
@@ -380,6 +463,7 @@
     pj_status_t status;
 
     PJ_ASSERT_RETURN(prm && prm->dir && p_aud_strm, PJ_EINVAL);
+    PJ_ASSERT_RETURN(aud_subsys.pf, PJMEDIA_EAUD_INIT);
 
     /* Must make copy of param because we're changing device ID */
     pj_memcpy(&param, prm, sizeof(param));
@@ -388,6 +472,9 @@
     if (param.dir & PJMEDIA_DIR_CAPTURE) {
 	unsigned index;
 
+	if (param.rec_id < 0)
+	    param.rec_id = PJMEDIA_AUD_DEFAULT_CAPTURE_DEV;
+
 	status = lookup_dev(param.rec_id, &rec_f, &index);
 	if (status != PJ_SUCCESS)
 	    return status;
@@ -400,6 +487,9 @@
     if (param.dir & PJMEDIA_DIR_PLAYBACK) {
 	unsigned index;
 
+	if (param.play_id < 0)
+	    param.play_id = PJMEDIA_AUD_DEFAULT_PLAYBACK_DEV;
+
 	status = lookup_dev(param.play_id, &play_f, &index);
 	if (status != PJ_SUCCESS)
 	    return status;
@@ -408,7 +498,7 @@
 	f = play_f;
 
 	/* For now, rec_id and play_id must belong to the same factory */
-	PJ_ASSERT_RETURN(rec_f == play_f, PJ_EINVAL);
+	PJ_ASSERT_RETURN(rec_f == play_f, PJMEDIA_EAUD_INVDEV);
     }
 
     
@@ -419,7 +509,7 @@
 	return status;
 
     /* Assign factory id to the stream */
-    (*p_aud_strm)->factory_id = f->internal.id;
+    (*p_aud_strm)->sys.drv_idx = f->sys.drv_idx;
     return PJ_SUCCESS;
 }
 
@@ -429,13 +519,16 @@
 {
     pj_status_t status;
 
+    PJ_ASSERT_RETURN(strm && param, PJ_EINVAL);
+    PJ_ASSERT_RETURN(aud_subsys.pf, PJMEDIA_EAUD_INIT);
+
     status = strm->op->get_param(strm, param);
     if (status != PJ_SUCCESS)
 	return status;
 
     /* Normalize device id's */
-    make_global_index(aud_subsys.drv[strm->factory_id].f, &param->rec_id);
-    make_global_index(aud_subsys.drv[strm->factory_id].f, &param->play_id);
+    make_global_index(strm->sys.drv_idx, &param->rec_id);
+    make_global_index(strm->sys.drv_idx, &param->play_id);
 
     return PJ_SUCCESS;
 }
diff --git a/pjmedia/src/pjmedia-audiodev/pa_dev.c b/pjmedia/src/pjmedia-audiodev/pa_dev.c
index 4750877..e3eb8e6 100644
--- a/pjmedia/src/pjmedia-audiodev/pa_dev.c
+++ b/pjmedia/src/pjmedia-audiodev/pa_dev.c
@@ -533,11 +533,11 @@
     } else if (adi.input_count) {
 	param->dir = PJMEDIA_DIR_CAPTURE;
 	param->rec_id = index;
-	param->play_id = PJMEDIA_AUD_DEV_DEFAULT;
+	param->play_id = PJMEDIA_AUD_INVALID_DEV;
     } else if (adi.output_count) {
 	param->dir = PJMEDIA_DIR_PLAYBACK;
 	param->play_id = index;
-	param->rec_id = PJMEDIA_AUD_DEV_DEFAULT;
+	param->rec_id = PJMEDIA_AUD_INVALID_DEV;
     } else {
 	return PJMEDIA_EAUD_INVDEV;
     }
diff --git a/pjmedia/src/pjmedia-audiodev/wmme_dev.c b/pjmedia/src/pjmedia-audiodev/wmme_dev.c
index 1458d30..16f88d0 100644
--- a/pjmedia/src/pjmedia-audiodev/wmme_dev.c
+++ b/pjmedia/src/pjmedia-audiodev/wmme_dev.c
@@ -413,11 +413,11 @@
     } else if (di->info.input_count) {
 	param->dir = PJMEDIA_DIR_CAPTURE;
 	param->rec_id = index;
-	param->play_id = PJMEDIA_AUD_DEV_DEFAULT;
+	param->play_id = PJMEDIA_AUD_INVALID_DEV;
     } else if (di->info.output_count) {
 	param->dir = PJMEDIA_DIR_PLAYBACK;
 	param->play_id = index;
-	param->rec_id = PJMEDIA_AUD_DEV_DEFAULT;
+	param->rec_id = PJMEDIA_AUD_INVALID_DEV;
     } else {
 	return PJMEDIA_EAUD_INVDEV;
     }
diff --git a/pjmedia/src/pjmedia/sound_port.c b/pjmedia/src/pjmedia/sound_port.c
index 3a09e9f..98b3b72 100644
--- a/pjmedia/src/pjmedia/sound_port.c
+++ b/pjmedia/src/pjmedia/sound_port.c
@@ -413,6 +413,7 @@
 					     pjmedia_snd_port **p_port)
 {
     pjmedia_snd_port *snd_port;
+    pj_status_t status;
 
     PJ_ASSERT_RETURN(pool && prm && p_port, PJ_EINVAL);
 
@@ -429,15 +430,18 @@
     snd_port->bits_per_sample = prm->bits_per_sample;
     pj_memcpy(&snd_port->aud_param, prm, sizeof(*prm));
     
-    *p_port = snd_port;
-
-
     /* Start sound device immediately.
      * If there's no port connected, the sound callback will return
      * empty signal.
      */
-    return start_sound_device( pool, snd_port );
+    status = start_sound_device( pool, snd_port );
+    if (status != PJ_SUCCESS) {
+	pjmedia_snd_port_destroy(snd_port);
+	return status;
+    }
 
+    *p_port = snd_port;
+    return PJ_SUCCESS;
 }
 
 
diff --git a/pjsip/src/pjsua-lib/pjsua_core.c b/pjsip/src/pjsua-lib/pjsua_core.c
index 24e3a34..00bf90e 100644
--- a/pjsip/src/pjsua-lib/pjsua_core.c
+++ b/pjsip/src/pjsua-lib/pjsua_core.c
@@ -583,7 +583,8 @@
     PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
 
     /* Set default sound device ID */
-    pjsua_var.cap_dev = pjsua_var.play_dev = -1;
+    pjsua_var.cap_dev = PJMEDIA_AUD_DEFAULT_CAPTURE_DEV;
+    pjsua_var.play_dev = PJMEDIA_AUD_DEFAULT_PLAYBACK_DEV;
 
     /* Init caching pool. */
     pj_caching_pool_init(&pjsua_var.cp, NULL, 0);
diff --git a/pjsip/src/pjsua-lib/pjsua_media.c b/pjsip/src/pjsua-lib/pjsua_media.c
index 1c3302b..036f720 100644
--- a/pjsip/src/pjsua-lib/pjsua_media.c
+++ b/pjsip/src/pjsua-lib/pjsua_media.c
@@ -2399,6 +2399,10 @@
 	return pjsua_set_null_snd_dev();
     }
 
+    /* Normalize device ID with new convention about default device ID */
+    if (playback_dev == PJMEDIA_AUD_DEFAULT_CAPTURE_DEV)
+	playback_dev = PJMEDIA_AUD_DEFAULT_PLAYBACK_DEV;
+
     /* Close existing sound port */
     close_snd_dev();
 
@@ -2418,6 +2422,8 @@
     /* Create default parameters for the device */
     status = pjmedia_aud_dev_default_param(capture_dev, &param);
     if (status != PJ_SUCCESS) {
+	pjsua_perror(THIS_FILE, "Error retrieving default audio "
+				"device parameters", status);
 	return status;
     }
     param.dir = PJMEDIA_DIR_CAPTURE_PLAYBACK;