Index: /pjproject/trunk/pjmedia/include/pjmedia/wav_port.h
===================================================================
--- /pjproject/trunk/pjmedia/include/pjmedia/wav_port.h	(revision 2269)
+++ /pjproject/trunk/pjmedia/include/pjmedia/wav_port.h	(revision 2270)
@@ -143,4 +143,25 @@
 
 
+/**
+ * WAV file writer options.
+ */
+enum pjmedia_file_writer_option
+{
+    /**
+     * Tell the file writer to save the audio in PCM format.
+     */
+    PJMEDIA_FILE_WRITE_PCM = 0,
+
+    /**
+     * Tell the file writer to save the audio in G711 Alaw format.
+     */
+    PJMEDIA_FILE_WRITE_ALAW = 1,
+
+    /**
+     * Tell the file writer to save the audio in G711 Alaw format.
+     */
+    PJMEDIA_FILE_WRITE_ULAW = 2,
+};
+
 
 /**
@@ -155,5 +176,5 @@
  * @param samples_per_frame Number of samples per frame.
  * @param bits_per_sample   Number of bits per sample (eg 16).
- * @param flags		    Port creation flags (must be 0 at present).
+ * @param flags		    Port creation flags.
  * @param buff_size	    Buffer size to be allocated. If the value is 
  *			    zero or negative, the port will use default buffer
Index: /pjproject/trunk/pjmedia/include/pjmedia/wave.h
===================================================================
--- /pjproject/trunk/pjmedia/include/pjmedia/wave.h	(revision 2269)
+++ /pjproject/trunk/pjmedia/include/pjmedia/wave.h	(revision 2270)
@@ -67,4 +67,19 @@
 #define PJMEDIA_DATA_TAG	('a'<<24|'t'<<16|'a'<<8|'d')
 
+/**
+ * Standard FACT tag to identify fact chunks.
+ */
+#define PJMEDIA_FACT_TAG	('t'<<24|'c'<<16|'a'<<8|'f')
+
+
+/**
+ * Enumeration of format compression tag.
+ */
+typedef enum {
+    PJMEDIA_WAVE_FMT_TAG_PCM	= 1,
+    PJMEDIA_WAVE_FMT_TAG_ALAW	= 6,
+    PJMEDIA_WAVE_FMT_TAG_ULAW	= 7
+} pjmedia_wave_fmt_tag;
+
 
 /**
@@ -72,4 +87,5 @@
  * It does not support the full RIFF format specification.
  */
+#pragma pack(2)
 struct pjmedia_wave_hdr
 {
@@ -99,4 +115,5 @@
     } data_hdr;
 };
+#pragma pack()
 
 /**
Index: /pjproject/trunk/pjmedia/src/pjmedia/wav_player.c
===================================================================
--- /pjproject/trunk/pjmedia/src/pjmedia/wav_player.c	(revision 2269)
+++ /pjproject/trunk/pjmedia/src/pjmedia/wav_player.c	(revision 2270)
@@ -18,4 +18,5 @@
  */
 #include <pjmedia/wav_port.h>
+#include <pjmedia/alaw_ulaw.h>
 #include <pjmedia/errno.h>
 #include <pjmedia/wave.h>
@@ -32,6 +33,5 @@
 
 #define SIGNATURE	    PJMEDIA_PORT_SIGNATURE('F', 'P', 'l', 'y')
-#define BYTES_PER_SAMPLE    2
-
+#define BITS_PER_SAMPLE	    16
 
 #if 1
@@ -57,4 +57,6 @@
     pjmedia_port     base;
     unsigned	     options;
+    pjmedia_wave_fmt_tag fmt_tag;
+    pj_uint16_t	     bytes_per_sample;
     pj_bool_t	     eof;
     pj_size_t	     bufsize;
@@ -146,5 +148,6 @@
 
     /* Convert samples to host rep */
-    samples_to_host((pj_int16_t*)fport->buf, fport->bufsize/BYTES_PER_SAMPLE);
+    samples_to_host((pj_int16_t*)fport->buf, 
+		    fport->bufsize/fport->bytes_per_sample);
 
     return PJ_SUCCESS;
@@ -166,5 +169,5 @@
     struct file_reader_port *fport;
     pj_off_t pos;
-    pj_status_t status;
+    pj_status_t status = PJ_SUCCESS;
 
 
@@ -236,17 +239,31 @@
     }
 
-    /* Must be PCM with 16bits per sample */
-    if (wave_hdr.fmt_hdr.fmt_tag != 1 ||
-	wave_hdr.fmt_hdr.bits_per_sample != 16)
-    {
-	pj_file_close(fport->fd);
-	return PJMEDIA_EWAVEUNSUPP;
-    }
-
-    /* Block align must be 2*nchannels */
-    if (wave_hdr.fmt_hdr.block_align != wave_hdr.fmt_hdr.nchan*BYTES_PER_SAMPLE) {
-	pj_file_close(fport->fd);
-	return PJMEDIA_EWAVEUNSUPP;
-    }
+    /* Validate format and its attributes (i.e: bits per sample, block align) */
+    switch (wave_hdr.fmt_hdr.fmt_tag) {
+    case PJMEDIA_WAVE_FMT_TAG_PCM:
+	if (wave_hdr.fmt_hdr.bits_per_sample != 16 || 
+	    wave_hdr.fmt_hdr.block_align != 2 * wave_hdr.fmt_hdr.nchan)
+	    status = PJMEDIA_EWAVEUNSUPP;
+	break;
+
+    case PJMEDIA_WAVE_FMT_TAG_ALAW:
+    case PJMEDIA_WAVE_FMT_TAG_ULAW:
+	if (wave_hdr.fmt_hdr.bits_per_sample != 8 ||
+	    wave_hdr.fmt_hdr.block_align != wave_hdr.fmt_hdr.nchan)
+	    status = PJMEDIA_ENOTVALIDWAVE;
+	break;
+
+    default:
+	status = PJMEDIA_EWAVEUNSUPP;
+	break;
+    }
+
+    if (status != PJ_SUCCESS) {
+	pj_file_close(fport->fd);
+	return status;
+    }
+
+    fport->fmt_tag = wave_hdr.fmt_hdr.fmt_tag;
+    fport->bytes_per_sample = wave_hdr.fmt_hdr.bits_per_sample / 8;
 
     /* If length of fmt_header is greater than 16, skip the remaining
@@ -300,5 +317,7 @@
 	return PJMEDIA_EWAVEUNSUPP;
     }
-    if (wave_hdr.data_hdr.len < 200) {
+    if (wave_hdr.data_hdr.len < ptime * wave_hdr.fmt_hdr.sample_rate *
+				wave_hdr.fmt_hdr.nchan / 1000)
+    {
 	pj_file_close(fport->fd);
 	return PJMEDIA_EWAVETOOSHORT;
@@ -313,5 +332,5 @@
     fport->base.info.channel_count = wave_hdr.fmt_hdr.nchan;
     fport->base.info.clock_rate = wave_hdr.fmt_hdr.sample_rate;
-    fport->base.info.bits_per_sample = wave_hdr.fmt_hdr.bits_per_sample;
+    fport->base.info.bits_per_sample = BITS_PER_SAMPLE;
     fport->base.info.samples_per_frame = fport->base.info.clock_rate *
 					 wave_hdr.fmt_hdr.nchan *
@@ -338,5 +357,5 @@
      * doesn't handle this case).
      */
-    if (fport->base.info.samples_per_frame * BYTES_PER_SAMPLE >=
+    if (fport->base.info.samples_per_frame * fport->bytes_per_sample >=
 	fport->bufsize)
     {
@@ -524,11 +543,19 @@
     }
 
-    //frame_size = fport->base.info.bytes_per_frame;
-    //pj_assert(frame->size == frame_size);
-    frame_size = frame->size;
+    //pj_assert(frame->size == fport->base.info.bytes_per_frame);
+    if (fport->fmt_tag == PJMEDIA_WAVE_FMT_TAG_PCM) {
+	frame_size = frame->size;
+	//frame->size = frame_size;
+    } else {
+	/* Must be ULAW or ALAW */
+	pj_assert(fport->fmt_tag == PJMEDIA_WAVE_FMT_TAG_ULAW || 
+		  fport->fmt_tag == PJMEDIA_WAVE_FMT_TAG_ALAW);
+
+	frame_size = frame->size >> 1;
+	frame->size = frame_size << 1;
+    }
 
     /* Copy frame from buffer. */
     frame->type = PJMEDIA_FRAME_TYPE_AUDIO;
-    frame->size = frame_size;
     frame->timestamp.u64 = 0;
 
@@ -580,4 +607,25 @@
     }
 
+    if (fport->fmt_tag == PJMEDIA_WAVE_FMT_TAG_ULAW ||
+	fport->fmt_tag == PJMEDIA_WAVE_FMT_TAG_ALAW)
+    {
+	unsigned i;
+	pj_uint16_t *dst;
+	pj_uint8_t *src;
+
+	dst = (pj_uint16_t*)frame->buf + frame_size - 1;
+	src = (pj_uint8_t*)frame->buf + frame_size - 1;
+
+	if (fport->fmt_tag == PJMEDIA_WAVE_FMT_TAG_ULAW) {
+	    for (i = 0; i < frame_size; ++i) {
+		*dst-- = (pj_uint16_t) pjmedia_ulaw2linear(*src--);
+	    }
+	} else {
+	    for (i = 0; i < frame_size; ++i) {
+		*dst-- = (pj_uint16_t) pjmedia_alaw2linear(*src--);
+	    }
+	}
+    }
+
     return PJ_SUCCESS;
 }
Index: /pjproject/trunk/pjmedia/src/pjmedia/wav_writer.c
===================================================================
--- /pjproject/trunk/pjmedia/src/pjmedia/wav_writer.c	(revision 2269)
+++ /pjproject/trunk/pjmedia/src/pjmedia/wav_writer.c	(revision 2270)
@@ -18,4 +18,5 @@
  */
 #include <pjmedia/wav_port.h>
+#include <pjmedia/alaw_ulaw.h>
 #include <pjmedia/errno.h>
 #include <pjmedia/wave.h>
@@ -30,5 +31,4 @@
 #define THIS_FILE	    "wav_writer.c"
 #define SIGNATURE	    PJMEDIA_PORT_SIGNATURE('F', 'W', 'R', 'T')
-#define BYTES_PER_SAMPLE    2
 
 
@@ -36,4 +36,7 @@
 {
     pjmedia_port     base;
+    pjmedia_wave_fmt_tag fmt_tag;
+    pj_uint16_t	     bytes_per_sample;
+
     pj_size_t	     bufsize;
     char	    *buf;
@@ -73,6 +76,4 @@
     pj_status_t status;
 
-    PJ_UNUSED_ARG(flags);
-
     /* Check arguments. */
     PJ_ASSERT_RETURN(pool && filename && p_port, PJ_EINVAL);
@@ -97,4 +98,14 @@
     fport->base.on_destroy = &file_on_destroy;
 
+    if (flags == PJMEDIA_FILE_WRITE_ALAW) {
+	fport->fmt_tag = PJMEDIA_WAVE_FMT_TAG_ALAW;
+	fport->bytes_per_sample = 1;
+    } else if (flags == PJMEDIA_FILE_WRITE_ULAW) {
+	fport->fmt_tag = PJMEDIA_WAVE_FMT_TAG_ULAW;
+	fport->bytes_per_sample = 1;
+    } else {
+	fport->fmt_tag = PJMEDIA_WAVE_FMT_TAG_PCM;
+	fport->bytes_per_sample = 2;
+    }
 
     /* Open file in write and read mode.
@@ -114,12 +125,11 @@
     wave_hdr.fmt_hdr.fmt = PJMEDIA_FMT_TAG;
     wave_hdr.fmt_hdr.len = 16;
-    wave_hdr.fmt_hdr.fmt_tag = 1;
+    wave_hdr.fmt_hdr.fmt_tag = fport->fmt_tag;
     wave_hdr.fmt_hdr.nchan = (pj_int16_t)channel_count;
     wave_hdr.fmt_hdr.sample_rate = sampling_rate;
     wave_hdr.fmt_hdr.bytes_per_sec = sampling_rate * channel_count * 
-				     bits_per_sample / 8;
-    wave_hdr.fmt_hdr.block_align = (pj_int16_t) (channel_count * 
-						 bits_per_sample / 8);
-    wave_hdr.fmt_hdr.bits_per_sample = (pj_int16_t)bits_per_sample;
+				     fport->bytes_per_sample;
+    wave_hdr.fmt_hdr.block_align = fport->bytes_per_sample * channel_count;
+    wave_hdr.fmt_hdr.bits_per_sample = fport->bytes_per_sample * 8;
 
     wave_hdr.data_hdr.data = PJMEDIA_DATA_TAG;
@@ -134,9 +144,49 @@
 
     /* Write WAVE header */
-    size = sizeof(pjmedia_wave_hdr);
-    status = pj_file_write(fport->fd, &wave_hdr, &size);
-    if (status != PJ_SUCCESS) {
-	pj_file_close(fport->fd);
-	return status;
+    if (fport->fmt_tag != PJMEDIA_WAVE_FMT_TAG_PCM) {
+	pjmedia_wave_subchunk fact_chunk;
+	pj_uint32_t tmp = 0;
+
+	fact_chunk.id = PJMEDIA_FACT_TAG;
+	fact_chunk.len = 4;
+
+	PJMEDIA_WAVE_NORMALIZE_SUBCHUNK(&fact_chunk);
+
+	/* Write WAVE header without DATA chunk header */
+	size = sizeof(pjmedia_wave_hdr) - sizeof(wave_hdr.data_hdr);
+	status = pj_file_write(fport->fd, &wave_hdr, &size);
+	if (status != PJ_SUCCESS) {
+	    pj_file_close(fport->fd);
+	    return status;
+	}
+
+	/* Write FACT chunk if it stores compressed data */
+	size = sizeof(fact_chunk);
+	status = pj_file_write(fport->fd, &fact_chunk, &size);
+	if (status != PJ_SUCCESS) {
+	    pj_file_close(fport->fd);
+	    return status;
+	}
+	size = 4;
+	status = pj_file_write(fport->fd, &tmp, &size);
+	if (status != PJ_SUCCESS) {
+	    pj_file_close(fport->fd);
+	    return status;
+	}
+
+	/* Write DATA chunk header */
+	size = sizeof(wave_hdr.data_hdr);
+	status = pj_file_write(fport->fd, &wave_hdr.data_hdr, &size);
+	if (status != PJ_SUCCESS) {
+	    pj_file_close(fport->fd);
+	    return status;
+	}
+    } else {
+	size = sizeof(pjmedia_wave_hdr);
+	status = pj_file_write(fport->fd, &wave_hdr, &size);
+	if (status != PJ_SUCCESS) {
+	    pj_file_close(fport->fd);
+	    return status;
+	}
     }
 
@@ -259,7 +309,13 @@
 {
     struct file_port *fport = (struct file_port *)this_port;
+    unsigned frame_size;
+
+    if (fport->fmt_tag == PJMEDIA_WAVE_FMT_TAG_PCM)
+	frame_size = frame->size;
+    else
+	frame_size = frame->size >> 1;
 
     /* Flush buffer if we don't have enough room for the frame. */
-    if (fport->writepos + frame->size > fport->buf + fport->bufsize) {
+    if (fport->writepos + frame_size > fport->buf + fport->bufsize) {
 	pj_status_t status;
 	status = flush_buffer(fport);
@@ -269,13 +325,30 @@
 
     /* Check if frame is not too large. */
-    PJ_ASSERT_RETURN(fport->writepos+frame->size <= fport->buf+fport->bufsize,
+    PJ_ASSERT_RETURN(fport->writepos+frame_size <= fport->buf+fport->bufsize,
 		     PJMEDIA_EFRMFILETOOBIG);
 
     /* Copy frame to buffer. */
-    pj_memcpy(fport->writepos, frame->buf, frame->size);
-    fport->writepos += frame->size;
+    if (fport->fmt_tag == PJMEDIA_WAVE_FMT_TAG_PCM) {
+	pj_memcpy(fport->writepos, frame->buf, frame->size);
+    } else {
+	unsigned i;
+	pj_int16_t *src = (pj_int16_t*)frame->buf;
+	pj_uint8_t *dst = (pj_uint8_t*)fport->writepos;
+
+	if (fport->fmt_tag == PJMEDIA_WAVE_FMT_TAG_ULAW) {
+	    for (i = 0; i < frame_size; ++i) {
+		*dst++ = pjmedia_linear2ulaw(*src++);
+	    }
+	} else {
+	    for (i = 0; i < frame_size; ++i) {
+		*dst++ = pjmedia_linear2alaw(*src++);
+	    }
+	}
+
+    }
+    fport->writepos += frame_size;
 
     /* Increment total written, and check if we need to call callback */
-    fport->total += frame->size;
+    fport->total += frame_size;
     if (fport->cb && fport->total >= fport->cb_size) {
 	pj_status_t (*cb)(pjmedia_port*, void*);
@@ -315,4 +388,5 @@
     pj_uint32_t wave_data_len;
     pj_status_t status;
+    pj_uint32_t data_len_pos = DATA_LEN_POS;
 
     /* Flush remaining buffers. */
@@ -342,6 +416,26 @@
     PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
 
+    /* Write samples_len in FACT chunk */
+    if (fport->fmt_tag != PJMEDIA_WAVE_FMT_TAG_PCM) {
+	enum { SAMPLES_LEN_POS = 44};
+	pj_uint32_t wav_samples_len;
+
+	/* Adjust wave_data_len & data_len_pos since there is FACT chunk */
+	wave_data_len -= 12;
+	data_len_pos += 12;
+	wav_samples_len = wave_data_len;
+
+	/* Seek to samples_len field. */
+	status = pj_file_setpos(fport->fd, SAMPLES_LEN_POS, PJ_SEEK_SET);
+	PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
+
+	/* Write samples_len */
+	bytes = sizeof(wav_samples_len);
+	status = pj_file_write(fport->fd, &wav_samples_len, &bytes);
+	PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
+    }
+
     /* Seek to data_len field. */
-    status = pj_file_setpos(fport->fd, DATA_LEN_POS, PJ_SEEK_SET);
+    status = pj_file_setpos(fport->fd, data_len_pos, PJ_SEEK_SET);
     PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
 
