- Added pjmedia-audiodev/config.h
- Added a bit of doxygen documentation
- Added support for PCMA/PCMU codecs in wmme_dev.c



git-svn-id: https://svn.pjsip.org/repos/pjproject/branches/projects/aps-direct@2470 74dad513-b988-da41-8d7b-12977e46ad98
diff --git a/pjmedia/src/pjmedia-audiodev/audiodev.c b/pjmedia/src/pjmedia-audiodev/audiodev.c
index 275cf93..9746eb3 100644
--- a/pjmedia/src/pjmedia-audiodev/audiodev.c
+++ b/pjmedia/src/pjmedia-audiodev/audiodev.c
@@ -20,6 +20,7 @@
 #include <pjmedia-audiodev/audiodev_imp.h>
 #include <pj/errno.h>
 #include <pj/log.h>
+#include <pj/pool.h>
 #include <pj/string.h>
 
 #define THIS_FILE   "audiodev.c"
@@ -62,8 +63,14 @@
 
 
 /* extern functions to create factories */
+#if PJMEDIA_AUDIO_DEV_HAS_PORTAUDIO
 pjmedia_aud_dev_factory* pjmedia_pa_factory(pj_pool_factory *pf);
+#endif
+
+#if PJMEDIA_AUDIO_DEV_HAS_WMME
 pjmedia_aud_dev_factory* pjmedia_wmme_factory(pj_pool_factory *pf);
+#endif
+
 
 #define MAX_DRIVERS	16
 #define MAX_DEVS	64
@@ -218,8 +225,12 @@
     aud_subsys.dev_cnt = 0;
 
     /* Register creation functions */
+#if PJMEDIA_AUDIO_DEV_HAS_PORTAUDIO
     aud_subsys.drv[aud_subsys.drv_cnt++].create = &pjmedia_pa_factory;
+#endif
+#if PJMEDIA_AUDIO_DEV_HAS_WMME
     aud_subsys.drv[aud_subsys.drv_cnt++].create = &pjmedia_wmme_factory;
+#endif
 
     /* Initialize each factory and build the device ID list */
     for (i=0; i<aud_subsys.drv_cnt; ++i) {
diff --git a/pjmedia/src/pjmedia-audiodev/wmme_dev.c b/pjmedia/src/pjmedia-audiodev/wmme_dev.c
index 16f88d0..e2f6078 100644
--- a/pjmedia/src/pjmedia-audiodev/wmme_dev.c
+++ b/pjmedia/src/pjmedia-audiodev/wmme_dev.c
@@ -30,6 +30,7 @@
 
 #include <windows.h>
 #include <mmsystem.h>
+#include <mmreg.h>
 
 #ifdef _MSC_VER
 #   pragma warning(pop)
@@ -102,10 +103,14 @@
 
     void    		*buffer;	    /**< Temp. frame buffer.   */
     unsigned             clock_rate;        /**< Clock rate.           */
+    unsigned		 bytes_per_frame;   /**< Bytes per frame       */
     unsigned             samples_per_frame; /**< Samples per frame.    */
     unsigned             bits_per_sample;   /**< Bits per sample.      */
     unsigned             channel_count;     /**< Channel count.        */
 
+    pjmedia_frame_ext	*xfrm;		    /**< Extended frame buffer */
+    unsigned		 xfrm_size;	    /**< Total ext frm size    */
+
     pj_thread_t         *thread;            /**< Thread handle.        */
     HANDLE               thread_quit_event; /**< Quit signal to thread */
 };
@@ -192,7 +197,7 @@
 
 /* Internal: build device info from WAVEINCAPS/WAVEOUTCAPS */
 static void build_dev_info(UINT deviceId, struct wmme_dev_info *wdi, 
-			   WAVEINCAPS *wic, WAVEOUTCAPS *woc)
+			   const WAVEINCAPS *wic, const WAVEOUTCAPS *woc)
 {
 #define WIC_WOC(wic,woc,field)	(wic? wic->field : woc->field)
 
@@ -204,7 +209,7 @@
 	strncpy(wdi->info.name, "Wave mapper", sizeof(wdi->info.name));
 	wdi->info.name[sizeof(wdi->info.name)-1] = '\0';
     } else {
-	pj_char_t *szPname = WIC_WOC(wic, woc, szPname);
+	const pj_char_t *szPname = WIC_WOC(wic, woc, szPname);
 	PJ_DECL_ANSI_TEMP_BUF(wTmp, sizeof(wdi->info.name));
 	
 	strncpy(wdi->info.name, 
@@ -245,6 +250,16 @@
 	    wdi->info.output_count = 2;
 	}
     }
+
+    /* Extended formats */
+    wdi->info.caps |= PJMEDIA_AUD_DEV_CAP_EXT_FORMAT;
+    wdi->info.ext_fmt_cnt = 2;
+    wdi->info.ext_fmt[0].id = PJMEDIA_FORMAT_PCMU;
+    wdi->info.ext_fmt[0].bitrate = 64000;
+    wdi->info.ext_fmt[0].vad = 0;
+    wdi->info.ext_fmt[1].id = PJMEDIA_FORMAT_PCMA;
+    wdi->info.ext_fmt[1].bitrate = 64000;
+    wdi->info.ext_fmt[1].vad = 0;
 }
 
 /* API: init factory */
@@ -279,7 +294,7 @@
 		   pj_pool_calloc(wf->pool, devCount, 
 				  sizeof(struct wmme_dev_info));
 
-    if (devCount) {
+    if (inputDeviceCount && outputDeviceCount) {
 	/* Attempt to add WAVE_MAPPER as input and output device */
 	WAVEINCAPS wic;
 	MMRESULT mr;
@@ -434,36 +449,66 @@
 }
 
 /* Internal: init WAVEFORMATEX */
-static void init_waveformatex (LPWAVEFORMATEX pcmwf, 
-			       unsigned clock_rate,
-			       unsigned channel_count)
+static pj_status_t init_waveformatex(LPWAVEFORMATEX wfx, 
+				     const pjmedia_aud_param *prm)
 {
-    pj_bzero(pcmwf, sizeof(PCMWAVEFORMAT)); 
-    pcmwf->wFormatTag = WAVE_FORMAT_PCM; 
-    pcmwf->nChannels = (pj_uint16_t)channel_count;
-    pcmwf->nSamplesPerSec = clock_rate;
-    pcmwf->nBlockAlign = (pj_uint16_t)(channel_count * BYTES_PER_SAMPLE);
-    pcmwf->nAvgBytesPerSec = clock_rate * channel_count * BYTES_PER_SAMPLE;
-    pcmwf->wBitsPerSample = BITS_PER_SAMPLE;
+
+    pj_bzero(wfx, sizeof(PCMWAVEFORMAT)); 
+    if (prm->ext_fmt.id == PJMEDIA_FORMAT_L16) {
+	wfx->wFormatTag = WAVE_FORMAT_PCM; 
+	wfx->nChannels = (pj_uint16_t)prm->channel_count;
+	wfx->nSamplesPerSec = prm->clock_rate;
+	wfx->nBlockAlign = (pj_uint16_t)(prm->channel_count * 
+					 BYTES_PER_SAMPLE);
+	wfx->nAvgBytesPerSec = prm->clock_rate * prm->channel_count * 
+			       BYTES_PER_SAMPLE;
+	wfx->wBitsPerSample = BITS_PER_SAMPLE;
+
+	return PJ_SUCCESS;
+
+    } else if ((prm->flags & PJMEDIA_AUD_DEV_CAP_EXT_FORMAT) &&
+	       (prm->ext_fmt.id == PJMEDIA_FORMAT_PCMA ||
+	        prm->ext_fmt.id == PJMEDIA_FORMAT_PCMU))
+    {
+	unsigned ptime;
+
+	ptime = prm->samples_per_frame * 1000 / 
+		(prm->clock_rate * prm->channel_count);
+	wfx->wFormatTag = (pj_uint16_t)
+			  ((prm->ext_fmt.id==PJMEDIA_FORMAT_PCMA) ?
+			    WAVE_FORMAT_ALAW : WAVE_FORMAT_MULAW);  
+	wfx->nChannels = (pj_uint16_t)prm->channel_count;
+	wfx->nSamplesPerSec = prm->clock_rate;
+	wfx->nAvgBytesPerSec = prm->clock_rate * prm->channel_count;
+	wfx->nBlockAlign = (pj_uint16_t)(wfx->nAvgBytesPerSec * ptime /
+					 1000);
+	wfx->wBitsPerSample = 8;
+	wfx->cbSize = 0;
+
+	return PJ_SUCCESS;
+
+    } else {
+
+	return PJMEDIA_EAUD_BADFORMAT;
+
+    }
 }
 
 
 /* Internal: create WMME player device. */
 static pj_status_t init_player_stream(  struct wmme_factory *wf,
 				        pj_pool_t *pool,
+					struct wmme_stream *parent,
 				        struct wmme_channel *wmme_strm,
-					unsigned dev_id,
-					unsigned clock_rate,
-					unsigned channel_count,
-					unsigned samples_per_frame,
+					const pjmedia_aud_param *prm,
 					unsigned buffer_count)
 {
     MMRESULT mr;
-    WAVEFORMATEX pcmwf; 
-    unsigned bytes_per_frame;
-    unsigned i;
+    WAVEFORMATEX wfx; 
+    unsigned i, ptime;
+    pj_status_t status;
 
-    PJ_ASSERT_RETURN(dev_id < wf->dev_count, PJ_EINVAL);
+    PJ_ASSERT_RETURN(prm->play_id < (int)wf->dev_count, PJ_EINVAL);
 
     /*
      * Create a wait event.
@@ -475,14 +520,22 @@
     /*
      * Set up wave format structure for opening the device.
      */
-    init_waveformatex(&pcmwf, clock_rate, channel_count);
-    bytes_per_frame = samples_per_frame * BYTES_PER_SAMPLE;
+    status = init_waveformatex(&wfx, prm);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    ptime = prm->samples_per_frame * 1000 / 
+	    (prm->clock_rate * prm->channel_count);
+    ptime = prm->samples_per_frame * 1000 / 
+	    (prm->clock_rate * prm->channel_count);
+    parent->bytes_per_frame = wfx.nAvgBytesPerSec / ptime;
 
     /*
      * Open wave device.
      */
-    mr = waveOutOpen(&wmme_strm->hWave.Out, wf->dev_info[dev_id].deviceId, 
-		     &pcmwf, (DWORD)wmme_strm->hEvent, 0, CALLBACK_EVENT);
+    mr = waveOutOpen(&wmme_strm->hWave.Out, 
+		     wf->dev_info[prm->play_id].deviceId,
+		     &wfx, (DWORD)wmme_strm->hEvent, 0, CALLBACK_EVENT);
     if (mr != MMSYSERR_NOERROR) {
 	return CONVERT_MM_ERROR(mr);
     }
@@ -499,8 +552,9 @@
     wmme_strm->WaveHdr = (WAVEHDR*)
 			 pj_pool_zalloc(pool, sizeof(WAVEHDR) * buffer_count);
     for (i = 0; i < buffer_count; ++i) {
-	wmme_strm->WaveHdr[i].lpData = pj_pool_zalloc(pool, bytes_per_frame);
-	wmme_strm->WaveHdr[i].dwBufferLength = bytes_per_frame;
+	wmme_strm->WaveHdr[i].lpData = pj_pool_zalloc(pool, 
+						      parent->bytes_per_frame);
+	wmme_strm->WaveHdr[i].dwBufferLength = parent->bytes_per_frame;
 	mr = waveOutPrepareHeader(wmme_strm->hWave.Out, 
 				  &(wmme_strm->WaveHdr[i]),
 				  sizeof(WAVEHDR));
@@ -522,9 +576,9 @@
     PJ_LOG(5, (THIS_FILE, 
 	       " WaveAPI Sound player \"%s\" initialized (clock_rate=%d, "
 	       "channel_count=%d, samples_per_frame=%d (%dms))",
-	       wf->dev_info[dev_id].info.name,
-	       clock_rate, channel_count, samples_per_frame,
-	       samples_per_frame * 1000 / clock_rate));
+	       wf->dev_info[prm->play_id].info.name,
+	       prm->clock_rate, prm->channel_count, prm->samples_per_frame,
+	       prm->samples_per_frame * 1000 / prm->clock_rate));
 
     return PJ_SUCCESS;
 }
@@ -533,19 +587,16 @@
 /* Internal: create Windows Multimedia recorder device */
 static pj_status_t init_capture_stream( struct wmme_factory *wf,
 				        pj_pool_t *pool,
+					struct wmme_stream *parent,
 					struct wmme_channel *wmme_strm,
-					unsigned dev_id,
-					unsigned clock_rate,
-					unsigned channel_count,
-					unsigned samples_per_frame,
+					const pjmedia_aud_param *prm,
 					unsigned buffer_count)
 {
     MMRESULT mr;
-    WAVEFORMATEX pcmwf; 
-    unsigned bytes_per_frame;
-    unsigned i;
+    WAVEFORMATEX wfx; 
+    unsigned i, ptime;
 
-    PJ_ASSERT_RETURN(dev_id < wf->dev_count, PJ_EINVAL);
+    PJ_ASSERT_RETURN(prm->rec_id < (int)wf->dev_count, PJ_EINVAL);
 
     /*
     * Create a wait event.
@@ -558,14 +609,17 @@
     /*
      * Set up wave format structure for opening the device.
      */
-    init_waveformatex(&pcmwf, clock_rate, channel_count);
-    bytes_per_frame = samples_per_frame * BYTES_PER_SAMPLE;
+    init_waveformatex(&wfx, prm);
+    ptime = prm->samples_per_frame * 1000 / 
+	    (prm->clock_rate * prm->channel_count);
+    parent->bytes_per_frame = wfx.nAvgBytesPerSec / ptime;
 
     /*
      * Open wave device.
      */
-    mr = waveInOpen(&wmme_strm->hWave.In, wf->dev_info[dev_id].deviceId, 
-		    &pcmwf, (DWORD)wmme_strm->hEvent, 0, CALLBACK_EVENT);
+    mr = waveInOpen(&wmme_strm->hWave.In, 
+		    wf->dev_info[prm->rec_id].deviceId, 
+		    &wfx, (DWORD)wmme_strm->hEvent, 0, CALLBACK_EVENT);
     if (mr != MMSYSERR_NOERROR) {
 	return CONVERT_MM_ERROR(mr);
     }
@@ -576,8 +630,9 @@
     wmme_strm->WaveHdr = (WAVEHDR*)
 			 pj_pool_zalloc(pool, sizeof(WAVEHDR) * buffer_count);
     for (i = 0; i < buffer_count; ++i) {
-	wmme_strm->WaveHdr[i].lpData = pj_pool_zalloc(pool, bytes_per_frame);
-	wmme_strm->WaveHdr[i].dwBufferLength = bytes_per_frame;
+	wmme_strm->WaveHdr[i].lpData = pj_pool_zalloc(pool, 
+						      parent->bytes_per_frame);
+	wmme_strm->WaveHdr[i].dwBufferLength = parent->bytes_per_frame;
 	mr = waveInPrepareHeader(wmme_strm->hWave.In, 
 				 &(wmme_strm->WaveHdr[i]),
 				 sizeof(WAVEHDR));
@@ -599,9 +654,9 @@
     PJ_LOG(5,(THIS_FILE, 
 	" WaveAPI Sound recorder \"%s\" initialized (clock_rate=%d, "
 	"channel_count=%d, samples_per_frame=%d (%dms))",
-	wf->dev_info[dev_id].info.name,
-	clock_rate, channel_count, samples_per_frame,
-	samples_per_frame * 1000 / clock_rate));
+	wf->dev_info[prm->rec_id].info.name,
+	prm->clock_rate, prm->channel_count, prm->samples_per_frame,
+	prm->samples_per_frame * 1000 / prm->clock_rate));
 
     return PJ_SUCCESS;
 }
@@ -613,10 +668,8 @@
     struct wmme_stream *strm = (struct wmme_stream*)arg;
     HANDLE events[3];
     unsigned eventCount;
-    unsigned bytes_per_frame;
     pj_status_t status = PJ_SUCCESS;
 
-
     eventCount = 0;
     events[eventCount++] = strm->thread_quit_event;
     if (strm->dir & PJMEDIA_DIR_PLAYBACK)
@@ -637,9 +690,6 @@
     SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
 #endif
 
-    /* Calculate bytes per frame */
-    bytes_per_frame = strm->samples_per_frame * BYTES_PER_SAMPLE;
-
     /*
      * Loop while not signalled to quit, wait for event objects to be 
      * signalled by WMME capture and play buffer.
@@ -676,7 +726,7 @@
 	if (signalled_dir == PJMEDIA_DIR_PLAYBACK)
 	{
 	    struct wmme_channel *wmme_strm = &strm->play_strm;
-	    MMRESULT mr = MMSYSERR_NOERROR;
+
 	    status = PJ_SUCCESS;
 
 	    /*
@@ -687,35 +737,76 @@
 	    while (wmme_strm->WaveHdr[wmme_strm->dwBufIdx].dwFlags & WHDR_DONE)
 	    {
 		void *buffer = wmme_strm->WaveHdr[wmme_strm->dwBufIdx].lpData;
-		pjmedia_frame frame;
+		pjmedia_frame pcm_frame, *frame;
+		pj_bool_t has_frame = PJ_FALSE;
 
 		//PJ_LOG(5,(THIS_FILE, "Finished writing buffer %d", 
 		//	  wmme_strm->dwBufIdx));
 
-		frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
-		frame.size = bytes_per_frame;
-		frame.buf = buffer;
-		frame.timestamp.u64 = wmme_strm->timestamp.u64;
-		frame.bit_info = 0;
+		if (strm->xfrm == NULL) {
+		    /* PCM mode */
+		    frame = &pcm_frame;
+
+		    frame->type = PJMEDIA_FRAME_TYPE_AUDIO;
+		    frame->size = strm->bytes_per_frame;
+		    frame->buf = buffer;
+		    frame->timestamp.u64 = wmme_strm->timestamp.u64;
+		    frame->bit_info = 0;
+		} else {
+		    /* Codec mode */
+		    frame = &strm->xfrm->base;
+
+		    strm->xfrm->base.type = PJMEDIA_FRAME_TYPE_EXTENDED;
+		    strm->xfrm->base.size = strm->bytes_per_frame;
+		    strm->xfrm->base.buf = NULL;
+		    strm->xfrm->base.timestamp.u64 = wmme_strm->timestamp.u64;
+		    strm->xfrm->base.bit_info = 0;
+		}
 
 		/* Get frame from application. */
-		status = (*strm->play_cb)(strm->user_data, &frame);
+		status = (*strm->play_cb)(strm->user_data, frame);
 
 		if (status != PJ_SUCCESS)
 		    break;
 
-		if (frame.type != PJMEDIA_FRAME_TYPE_AUDIO) {
-		    pj_bzero(buffer, bytes_per_frame);
+		if (strm->xfrm == NULL) {
+		    /* PCM mode */
+		    if (frame->type == PJMEDIA_FRAME_TYPE_NONE) {
+			pj_bzero(buffer, strm->bytes_per_frame);
+			has_frame = PJ_TRUE;
+		    } else if (frame->type == PJMEDIA_FRAME_TYPE_EXTENDED) {
+			pj_assert(!"Frame type not supported");
+		    } else if (frame->type == PJMEDIA_FRAME_TYPE_AUDIO) {
+			has_frame = PJ_TRUE;
+		    } else {
+			pj_assert(!"Frame type not supported");
+		    }
+		} else {
+		    /* Codec mode */
+		    if (frame->type == PJMEDIA_FRAME_TYPE_NONE) {
+			/* Not supported */
+		    } else if (frame->type == PJMEDIA_FRAME_TYPE_EXTENDED) {
+			unsigned sz;
+			sz = pjmedia_frame_ext_copy_payload(strm->xfrm,
+							    buffer,
+							    strm->bytes_per_frame);
+			pj_assert(sz == strm->bytes_per_frame);
+		    } else {
+			pj_assert(!"Frame type not supported");
+		    }
 		}
 
 		/* Write to the device. */
-		mr = waveOutWrite(wmme_strm->hWave.Out, 
-				  &(wmme_strm->WaveHdr[wmme_strm->dwBufIdx]), 
-				  sizeof(WAVEHDR));
-		if (mr != MMSYSERR_NOERROR)
-		{
-		    status = CONVERT_MM_ERROR(mr);
-		    break;
+		if (has_frame) {
+		    MMRESULT mr = MMSYSERR_NOERROR;
+
+		    mr = waveOutWrite(wmme_strm->hWave.Out, 
+				      &(wmme_strm->WaveHdr[wmme_strm->dwBufIdx]),
+				      sizeof(WAVEHDR));
+		    if (mr != MMSYSERR_NOERROR) {
+			status = CONVERT_MM_ERROR(mr);
+			break;
+		    }
 		}
 
 		/* Increment position. */
@@ -765,18 +856,46 @@
 			       wmme_strm->WaveHdr[wmme_strm->dwBufIdx].lpData;
 		unsigned cap_len = 
 			wmme_strm->WaveHdr[wmme_strm->dwBufIdx].dwBytesRecorded;
-		pjmedia_frame frame;
+		pjmedia_frame pcm_frame, *frame;
 
 		/*
 		PJ_LOG(5,(THIS_FILE, "Read %d bytes from buffer %d", cap_len, 
 			  wmme_strm->dwBufIdx));
 		*/
+	    
+		if (strm->xfrm == NULL) {
+		    /* PCM mode */
+		    if (cap_len < strm->bytes_per_frame)
+			pj_bzero(buffer + cap_len, 
+				 strm->bytes_per_frame - cap_len);
 
-		if (cap_len < bytes_per_frame)
-		    pj_bzero(buffer + cap_len, bytes_per_frame - cap_len);
+		    /* Copy the audio data out of the wave buffer. */
+		    pj_memcpy(strm->buffer, buffer, strm->bytes_per_frame);
 
-		/* Copy the audio data out of the wave buffer. */
-		pj_memcpy(strm->buffer, buffer, bytes_per_frame);
+		    /* Prepare frame */
+		    frame = &pcm_frame;
+		    frame->type = PJMEDIA_FRAME_TYPE_AUDIO;
+		    frame->buf = strm->buffer;
+		    frame->size = strm->bytes_per_frame;
+		    frame->timestamp.u64 = wmme_strm->timestamp.u64;
+		    frame->bit_info = 0;
+
+		} else {
+		    /* Codec mode */
+		    frame = &strm->xfrm->base;
+
+		    frame->type = PJMEDIA_FRAME_TYPE_EXTENDED;
+		    frame->buf = NULL;
+		    frame->size = strm->bytes_per_frame;
+		    frame->timestamp.u64 = wmme_strm->timestamp.u64;
+		    frame->bit_info = 0;
+
+		    strm->xfrm->samples_cnt = 0;
+		    strm->xfrm->subframe_cnt = 0;
+		    pjmedia_frame_ext_append_subframe(strm->xfrm, buffer,
+						      strm->bytes_per_frame *8,
+						      strm->samples_per_frame);
+		}
 
 		/* Re-add the buffer to the device. */
 		mr = waveInAddBuffer(wmme_strm->hWave.In, 
@@ -787,15 +906,9 @@
 		    break;
 		}
 
-		/* Prepare frame */
-		frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
-		frame.buf = strm->buffer;
-		frame.size = bytes_per_frame;
-		frame.timestamp.u64 = wmme_strm->timestamp.u64;
-		frame.bit_info = 0;
 
 		/* Call callback */
-		status = (*strm->rec_cb)(strm->user_data, &frame);
+		status = (*strm->rec_cb)(strm->user_data, frame);
 		if (status != PJ_SUCCESS)
 		    break;
 
@@ -865,11 +978,9 @@
 		    param->channel_count / param->samples_per_frame / 1000;
 
 	status = init_player_stream(wf, strm->pool,
+				    strm,
 				    &strm->play_strm,
-				    param->play_id,
-				    param->clock_rate,
-				    param->channel_count,
-				    param->samples_per_frame,
+				    param,
 				    buf_count);
 
 	if (status != PJ_SUCCESS) {
@@ -891,11 +1002,9 @@
 		    param->channel_count / param->samples_per_frame / 1000;
 
 	status = init_capture_stream(wf, strm->pool,
+				     strm,
 				     &strm->rec_strm,
-				     param->rec_id,
-				     param->clock_rate,
-				     param->channel_count,
-				     param->samples_per_frame,
+				     param,
 				     buf_count);
 
 	if (status != PJ_SUCCESS) {
@@ -904,6 +1013,17 @@
 	}
     }
 
+    /* If format is extended, must create buffer for the extended frame. */
+    if (param->ext_fmt.id != PJMEDIA_FORMAT_L16) {
+	unsigned ptime = param->samples_per_frame * 1000 /
+			 (param->clock_rate * param->channel_count);
+	strm->xfrm_size = sizeof(pjmedia_frame_ext) + 
+			  32 * sizeof(pjmedia_frame_ext_subframe) +
+			  (8000 * ptime / 1000) + 4;
+	strm->xfrm = (pjmedia_frame_ext*)
+		     pj_pool_alloc(pool, strm->xfrm_size);
+    }
+
     /* Create the stop event */
     strm->thread_quit_event = CreateEvent(NULL, FALSE, FALSE, NULL);
     if (strm->thread_quit_event == NULL) {