Index: /pjproject/trunk/pjmedia/build/pjmedia.dsp
===================================================================
--- /pjproject/trunk/pjmedia/build/pjmedia.dsp	(revision 531)
+++ /pjproject/trunk/pjmedia/build/pjmedia.dsp	(revision 532)
@@ -124,4 +124,12 @@
 # Begin Source File
 
+SOURCE=..\src\pjmedia\mem_capture.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjmedia\mem_player.c
+# End Source File
+# Begin Source File
+
 SOURCE=..\src\pjmedia\null_port.c
 # End Source File
@@ -249,4 +257,8 @@
 
 SOURCE=..\include\pjmedia\master_port.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\pjmedia\mem_port.h
 # End Source File
 # Begin Source File
Index: /pjproject/trunk/pjmedia/include/pjmedia/mem_port.h
===================================================================
--- /pjproject/trunk/pjmedia/include/pjmedia/mem_port.h	(revision 532)
+++ /pjproject/trunk/pjmedia/include/pjmedia/mem_port.h	(revision 532)
@@ -0,0 +1,121 @@
+/* $Id$ */
+/* 
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#ifndef __PJMEDIA_MEM_PORT_H__
+#define __PJMEDIA_MEM_PORT_H__
+
+/**
+ * @file mem_port.h
+ * @brief Memory based media playback/capture port
+ */
+#include <pjmedia/port.h>
+
+PJ_BEGIN_DECL
+
+
+/**
+ * @defgroup PJMEDIA_MEM_PLAYER Memory/Buffer-based Playback Port
+ * @ingroup PJMEDIA_PORT
+ * @brief Media playback from a fixed buffer
+ * @{
+ * A memory/buffer based playback port is used to play media from a fixed
+ * size buffer. This is useful over @ref PJMEDIA_FILE_PLAY for 
+ * situation where filesystems are not available in the target system.
+ */
+
+/**
+ * Create the buffer based playback to play the media from the specified
+ * buffer.
+ *
+ * @param pool		    Pool to allocate memory for the port structure.
+ * @param buffer	    The buffer to play the media from, which should
+ *			    be available throughout the life time of the port.
+ *			    The player plays the media directly from this
+ *			    buffer (i.e. no copying is done).
+ * @param size		    The size of the buffer, in bytes.
+ * @param clock_rate	    Sampling rate.
+ * @param channel_count	    Number of channels.
+ * @param samples_per_frame Number of samples per frame.
+ * @param bits_per_sample   Number of bits per sample.
+ * @param options	    Option flags.
+ * @param p_port	    Pointer to receive the port instance.
+ *
+ * @return		    PJ_SUCCESS on success, or the appropriate
+ *			    error code.
+ */
+PJ_DECL(pj_status_t) pjmedia_mem_player_create(pj_pool_t *pool,
+					       const void *buffer,
+					       pj_size_t size,
+					       unsigned clock_rate,
+					       unsigned channel_count,
+					       unsigned samples_per_frame,
+					       unsigned bits_per_sample,
+					       unsigned options,
+					       pjmedia_port **p_port );
+
+
+/**
+ * @}
+ */
+
+/**
+ * @defgroup PJMEDIA_MEM_CAPTURE Memory/Buffer-based Capture Port
+ * @ingroup PJMEDIA_PORT
+ * @brief Capture to fixed size buffer
+ * @{
+ * A memory based capture is used to save media streams to a fixed size
+ * buffer. This is useful over @ref PJMEDIA_FILE_REC for 
+ * situation where filesystems are not available in the target system.
+ */
+
+/**
+ * Create media port to capture/record media into a fixed size buffer.
+ *
+ * @param pool		    Pool to allocate memory for the port structure.
+ * @param buffer	    The buffer to record the media to, which should
+ *			    be available throughout the life time of the port.
+ * @param size		    The maximum size of the buffer, in bytes.
+ * @param clock_rate	    Sampling rate.
+ * @param channel_count	    Number of channels.
+ * @param samples_per_frame Number of samples per frame.
+ * @param bits_per_sample   Number of bits per sample.
+ * @param options	    Option flags.
+ * @param p_port	    Pointer to receive the port instance.
+ *
+ * @return		    PJ_SUCCESS on success, or the appropriate
+ *			    error code.
+ */
+PJ_DECL(pj_status_t) pjmedia_mem_capture_create(pj_pool_t *pool,
+						void *buffer,
+						pj_size_t size,
+						unsigned clock_rate,
+						unsigned channel_count,
+						unsigned samples_per_frame,
+						unsigned bits_per_sample,
+						unsigned options,
+						pjmedia_port **p_port);
+
+
+/**
+ * @}
+ */
+
+PJ_END_DECL
+
+
+#endif	/* __PJMEDIA_MEM_PORT_H__ */
Index: /pjproject/trunk/pjmedia/include/pjmedia/wav_port.h
===================================================================
--- /pjproject/trunk/pjmedia/include/pjmedia/wav_port.h	(revision 531)
+++ /pjproject/trunk/pjmedia/include/pjmedia/wav_port.h	(revision 532)
@@ -63,5 +63,4 @@
  *			negative, the port will use default buffer size (which
  *			is about 4KB).
- * @param user_data	User data to be associated with the file player port.
  * @param p_port	Pointer to receive the file port instance.
  *
@@ -73,5 +72,4 @@
 						     unsigned flags,
 						     pj_ssize_t buff_size,
-						     void *user_data,
 						     pjmedia_port **p_port );
 
@@ -107,7 +105,5 @@
  *
  * @param port		The file player port.
- * @param user_data	User data to be specified in the callback. Note that
- *			this overwrites the user data previously set when
- *			the file port is created.
+ * @param user_data	User data to be specified in the callback
  * @param cb		Callback to be called. If the callback returns non-
  *			PJ_SUCCESS, the playback will stop. Note that if
@@ -152,6 +148,5 @@
  *			    zero or negative, the port will use default buffer
  *			    size (which is about 4KB).
- * @param user_data	    User data to be associated with the file port.
- * @param p_port	    Pointer to receive the file port instance.
+  * @param p_port	    Pointer to receive the file port instance.
  *
  * @return		    PJ_SUCCESS on success.
@@ -165,5 +160,4 @@
 						    unsigned flags,
 						    pj_ssize_t buff_size,
-						    void *user_data,
 						    pjmedia_port **p_port );
 
@@ -190,7 +184,6 @@
  * @param port		The file writer port.
  * @param pos		The file position on which the callback will be called.
- * @param user_data	User data to be specified in the callback. Note that
- *			this overwrites the user data previously set when
- *			the file port is created.
+ * @param user_data	User data to be specified in the callback, and will be
+ *			given on the callback.
  * @param cb		Callback to be called. If the callback returns non-
  *			PJ_SUCCESS, the writing will stop. Note that if 
Index: /pjproject/trunk/pjmedia/src/pjmedia/mem_capture.c
===================================================================
--- /pjproject/trunk/pjmedia/src/pjmedia/mem_capture.c	(revision 532)
+++ /pjproject/trunk/pjmedia/src/pjmedia/mem_capture.c	(revision 532)
@@ -0,0 +1,163 @@
+/* $Id$ */
+/* 
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pjmedia/mem_port.h>
+#include <pj/assert.h>
+#include <pj/errno.h>
+#include <pj/pool.h>
+
+
+#define THIS_FILE	    "mem_capture.c"
+
+#define SIGNATURE	    PJMEDIA_PORT_SIGNATURE('M', 'R', 'e', 'c')
+#define BYTES_PER_SAMPLE    2
+
+struct mem_rec
+{
+    pjmedia_port     base;
+
+    unsigned	     options;
+
+    char	    *buffer;
+    pj_size_t	     buf_size;
+    char	    *write_pos;
+};
+
+
+static pj_status_t rec_put_frame(pjmedia_port *this_port, 
+				  const pjmedia_frame *frame);
+static pj_status_t rec_get_frame(pjmedia_port *this_port, 
+				  pjmedia_frame *frame);
+static pj_status_t rec_on_destroy(pjmedia_port *this_port);
+
+
+PJ_DECL(pj_status_t) pjmedia_mem_capture_create(pj_pool_t *pool,
+						void *buffer,
+						pj_size_t size,
+						unsigned clock_rate,
+						unsigned channel_count,
+						unsigned samples_per_frame,
+						unsigned bits_per_sample,
+						unsigned options,
+						pjmedia_port **p_port)
+{
+    struct mem_rec *rec;
+
+    /* Sanity check */
+    PJ_ASSERT_RETURN(pool && buffer && size && clock_rate && channel_count &&
+		     samples_per_frame && bits_per_sample && p_port,
+		     PJ_EINVAL);
+
+    /* Can only support 16bit PCM */
+    PJ_ASSERT_RETURN(bits_per_sample == 16, PJ_EINVAL);
+
+
+    rec = pj_pool_zalloc(pool, sizeof(struct mem_rec));
+    PJ_ASSERT_RETURN(rec != NULL, PJ_ENOMEM);
+
+    /* Create the rec */
+    rec->base.info.name = pj_str("memrec");
+    rec->base.info.signature = SIGNATURE;
+    rec->base.info.type = PJMEDIA_TYPE_AUDIO;
+    rec->base.info.has_info = PJ_TRUE;
+    rec->base.info.need_info = PJ_FALSE;
+    rec->base.info.pt = 0xFF;
+    rec->base.info.encoding_name = pj_str("pcm");
+
+    rec->base.put_frame = &rec_put_frame;
+    rec->base.get_frame = &rec_get_frame;
+    rec->base.on_destroy = &rec_on_destroy;
+
+    rec->base.info.clock_rate = clock_rate;
+    rec->base.info.channel_count = channel_count;
+    rec->base.info.bits_per_sample = bits_per_sample;
+    rec->base.info.samples_per_frame = samples_per_frame;
+    rec->base.info.bytes_per_frame = samples_per_frame * bits_per_sample / 2;
+
+    /* Save the buffer */
+    rec->buffer = rec->write_pos = (char*)buffer;
+    rec->buf_size = size;
+
+    /* Options */
+    rec->options = options;
+
+    *p_port = &rec->base;
+
+    return PJ_SUCCESS;
+}
+
+
+static pj_status_t rec_put_frame( pjmedia_port *this_port, 
+				  const pjmedia_frame *frame)
+{
+    struct mem_rec *rec;
+    char *endpos;
+    pj_size_t size_written;
+
+    PJ_ASSERT_RETURN(this_port->info.signature == SIGNATURE,
+		     PJ_EINVALIDOP);
+
+    rec = (struct mem_rec*) this_port;
+
+    size_written = 0;
+    endpos = rec->buffer + rec->buf_size;
+
+    while (size_written < frame->size) {
+	pj_size_t max;
+
+	max = frame->size - size_written;
+	if ((endpos - rec->write_pos) < (int)max)
+	    max = endpos - rec->write_pos;
+
+	pj_memcpy(rec->write_pos, ((char*)frame->buf)+size_written, max);
+	size_written += max;
+	rec->write_pos += max;
+
+	pj_assert(rec->write_pos <= endpos);
+
+	if (rec->write_pos == endpos)
+	    rec->write_pos = rec->buffer;
+    }
+    return PJ_SUCCESS;
+}
+
+
+static pj_status_t rec_get_frame( pjmedia_port *this_port, 
+				  pjmedia_frame *frame)
+{
+    PJ_ASSERT_RETURN(this_port->info.signature == SIGNATURE,
+		     PJ_EINVALIDOP);
+
+    PJ_UNUSED_ARG(this_port);
+
+    frame->size = 0;
+    frame->type = PJMEDIA_FRAME_TYPE_NONE;
+
+    return PJ_SUCCESS;
+}
+
+
+static pj_status_t rec_on_destroy(pjmedia_port *this_port)
+{
+    /* Nothing to do */
+    PJ_UNUSED_ARG(this_port);
+    return PJ_SUCCESS;
+}
+
+
+
Index: /pjproject/trunk/pjmedia/src/pjmedia/mem_player.c
===================================================================
--- /pjproject/trunk/pjmedia/src/pjmedia/mem_player.c	(revision 532)
+++ /pjproject/trunk/pjmedia/src/pjmedia/mem_player.c	(revision 532)
@@ -0,0 +1,167 @@
+/* $Id$ */
+/* 
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pjmedia/mem_port.h>
+#include <pj/assert.h>
+#include <pj/errno.h>
+#include <pj/pool.h>
+
+
+#define THIS_FILE	    "mem_player.c"
+
+#define SIGNATURE	    PJMEDIA_PORT_SIGNATURE('M', 'P', 'l', 'y')
+#define BYTES_PER_SAMPLE    2
+
+struct mem_player
+{
+    pjmedia_port     base;
+
+    unsigned	     options;
+    pj_timestamp     timestamp;
+
+    char	    *buffer;
+    pj_size_t	     buf_size;
+    char	    *read_pos;
+};
+
+
+static pj_status_t mem_put_frame(pjmedia_port *this_port, 
+				  const pjmedia_frame *frame);
+static pj_status_t mem_get_frame(pjmedia_port *this_port, 
+				  pjmedia_frame *frame);
+static pj_status_t mem_on_destroy(pjmedia_port *this_port);
+
+
+PJ_DEF(pj_status_t) pjmedia_mem_player_create( pj_pool_t *pool,
+					       const void *buffer,
+					       pj_size_t size,
+					       unsigned clock_rate,
+					       unsigned channel_count,
+					       unsigned samples_per_frame,
+					       unsigned bits_per_sample,
+					       unsigned options,
+					       pjmedia_port **p_port )
+{
+    struct mem_player *port;
+
+    /* Sanity check */
+    PJ_ASSERT_RETURN(pool && buffer && size && clock_rate && channel_count &&
+		     samples_per_frame && bits_per_sample && p_port,
+		     PJ_EINVAL);
+
+    /* Can only support 16bit PCM */
+    PJ_ASSERT_RETURN(bits_per_sample == 16, PJ_EINVAL);
+
+
+    port = pj_pool_zalloc(pool, sizeof(struct mem_player));
+    PJ_ASSERT_RETURN(port != NULL, PJ_ENOMEM);
+
+    /* Create the port */
+    port->base.info.name = pj_str("memplayer");
+    port->base.info.signature = SIGNATURE;
+    port->base.info.type = PJMEDIA_TYPE_AUDIO;
+    port->base.info.has_info = PJ_TRUE;
+    port->base.info.need_info = PJ_FALSE;
+    port->base.info.pt = 0xFF;
+    port->base.info.encoding_name = pj_str("pcm");
+
+    port->base.put_frame = &mem_put_frame;
+    port->base.get_frame = &mem_get_frame;
+    port->base.on_destroy = &mem_on_destroy;
+
+    port->base.info.clock_rate = clock_rate;
+    port->base.info.channel_count = channel_count;
+    port->base.info.bits_per_sample = bits_per_sample;
+    port->base.info.samples_per_frame = samples_per_frame;
+    port->base.info.bytes_per_frame = samples_per_frame * bits_per_sample / 2;
+
+    /* Save the buffer */
+    port->buffer = port->read_pos = (char*)buffer;
+    port->buf_size = size;
+
+    /* Options */
+    port->options = options;
+
+    *p_port = &port->base;
+
+    return PJ_SUCCESS;
+}
+
+
+static pj_status_t mem_put_frame( pjmedia_port *this_port, 
+				  const pjmedia_frame *frame)
+{
+    PJ_UNUSED_ARG(this_port);
+    PJ_UNUSED_ARG(frame);
+
+    return PJ_SUCCESS;
+}
+
+
+static pj_status_t mem_get_frame( pjmedia_port *this_port, 
+				  pjmedia_frame *frame)
+{
+    struct mem_player *player;
+    char *endpos;
+    pj_size_t size_needed, size_written;
+
+    PJ_ASSERT_RETURN(this_port->info.signature == SIGNATURE,
+		     PJ_EINVALIDOP);
+
+    player = (struct mem_player*) this_port;
+
+    size_needed = this_port->info.bytes_per_frame;
+    size_written = 0;
+    endpos = player->buffer + player->buf_size;
+
+    while (size_written < size_needed) {
+	char *dst = ((char*)frame->buf) + size_written;
+	pj_size_t max;
+	
+	max = size_needed - size_written;
+	if (endpos - player->read_pos < (int)max)
+	    max = endpos - player->read_pos;
+
+	pj_memcpy(dst, player->read_pos, max);
+	size_written += max;
+	player->read_pos += max;
+
+	pj_assert(player->read_pos <= endpos);
+
+	if (player->read_pos == endpos)
+	    player->read_pos = player->buffer;
+    }
+
+    player->timestamp.u64 += this_port->info.samples_per_frame;
+
+    frame->size = this_port->info.bytes_per_frame;
+    frame->timestamp.u64 = player->timestamp.u64;
+    frame->type = PJMEDIA_FRAME_TYPE_AUDIO;
+
+    return PJ_SUCCESS;
+}
+
+
+static pj_status_t mem_on_destroy(pjmedia_port *this_port)
+{
+    /* Nothing to do */
+    PJ_UNUSED_ARG(this_port);
+    return PJ_SUCCESS;
+}
+
+
Index: /pjproject/trunk/pjmedia/src/pjmedia/wav_player.c
===================================================================
--- /pjproject/trunk/pjmedia/src/pjmedia/wav_player.c	(revision 531)
+++ /pjproject/trunk/pjmedia/src/pjmedia/wav_player.c	(revision 532)
@@ -195,5 +195,4 @@
 						     unsigned options,
 						     pj_ssize_t buff_size,
-						     void *user_data,
 						     pjmedia_port **p_port )
 {
@@ -294,5 +293,4 @@
 
     /* Initialize */
-    fport->base.user_data = user_data;
     fport->options = options;
 
Index: /pjproject/trunk/pjmedia/src/pjmedia/wav_writer.c
===================================================================
--- /pjproject/trunk/pjmedia/src/pjmedia/wav_writer.c	(revision 531)
+++ /pjproject/trunk/pjmedia/src/pjmedia/wav_writer.c	(revision 532)
@@ -65,5 +65,4 @@
 						     unsigned flags,
 						     pj_ssize_t buff_size,
-						     void *user_data,
 						     pjmedia_port **p_port )
 {
@@ -74,5 +73,4 @@
 
     PJ_UNUSED_ARG(flags);
-    PJ_UNUSED_ARG(user_data);
 
     /* Check arguments. */
