File descriptor media port

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi,

some time ago I posted a patch that added audio put and get frame 
callback to PJSUA, even to the Python wrapper. It suffered from 
deadlocks and occasional crashes (especially with more calls) so 
yesterday I posted a new version that behaves better, but still 
deadlocks or crashes might occur.

I revised the idea of accessing audio frames at a high level and created 
file descriptor media port available in PJSUA, Python_PJSUA and PJSUA2, 
see attached patch.

Key features:
* It works similarly to audio recorder or player.
* It works with raw data -- no headers or format handling.
* It can read/write from/to a file descriptor or Windows file handle.
* File descriptor may be virtually everything: file, pipe, socket etc.
* On UNIX it can operate either in blocking mode or in non-blocking mode 
with buffering.
* It can be used to communicate the data with the same or different process.

Feel free to use it if you find it useful.
Examples are in
pjsip_apps/src/python/samples/audio_fd.py
and
pjsip_apps/src/swig/python/audio_fd.py.

Benny or other authors, do you think it can be integrated to the trunk? 
I would be helpful and grateful.

Cheers,
- Vali
-------------- next part --------------
Index: pjmedia/build/Makefile
===================================================================
--- pjmedia/build/Makefile	(revision 4770)
+++ pjmedia/build/Makefile	(working copy)
@@ -59,7 +59,7 @@
 export PJMEDIA_SRCDIR = ../src/pjmedia
 export PJMEDIA_OBJS += $(OS_OBJS) $(M_OBJS) $(CC_OBJS) $(HOST_OBJS) \
 			alaw_ulaw.o alaw_ulaw_table.o avi_player.o \
-			bidirectional.o clock_thread.o codec.o conference.o \
+			bidirectional.o fd_port.o clock_thread.o codec.o conference.o \
 			conf_switch.o converter.o  converter_libswscale.o \
 			delaybuf.o echo_common.o \
 			echo_port.o echo_suppress.o endpoint.o errno.o \
Index: pjmedia/build/pjmedia.vcproj
===================================================================
--- pjmedia/build/pjmedia.vcproj	(revision 4770)
+++ pjmedia/build/pjmedia.vcproj	(working copy)
@@ -3455,6 +3455,10 @@
 				</FileConfiguration>
 			</File>
 			<File
+				RelativePath="..\src\pjmedia\fd_port.c"
+				>
+			</File>
+			<File
 				RelativePath="..\src\pjmedia\clock_thread.c"
 				>
 				<FileConfiguration
@@ -7276,6 +7280,10 @@
 				>
 			</File>
 			<File
+				RelativePath="..\include\pjmedia\fd_port.h"
+				>
+			</File>
+			<File
 				RelativePath="..\include\pjmedia\bidirectional.h"
 				>
 			</File>
Index: pjmedia/include/pjmedia.h
===================================================================
--- pjmedia/include/pjmedia.h	(revision 4770)
+++ pjmedia/include/pjmedia.h	(working copy)
@@ -27,6 +27,7 @@
 #include <pjmedia/alaw_ulaw.h>
 #include <pjmedia/avi_stream.h>
 #include <pjmedia/bidirectional.h>
+#include <pjmedia/fd_port.h>
 #include <pjmedia/circbuf.h>
 #include <pjmedia/clock.h>
 #include <pjmedia/codec.h>
Index: pjmedia/include/pjmedia/fd_port.h
===================================================================
--- pjmedia/include/pjmedia/fd_port.h	(revision 0)
+++ pjmedia/include/pjmedia/fd_port.h	(working copy)
@@ -0,0 +1,113 @@
+/* $Id$ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny at 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_FD_PORT_H__
+#define __PJMEDIA_FD_PORT_H__
+
+/**
+ * @file fd_port.h
+ * @brief File descriptor media port.
+ */
+#include <pjmedia/port.h>
+
+
+
+/**
+ * @defgroup PJMEDIA_FD_PORT File Descriptor Port
+ * @ingroup PJMEDIA_PORT
+ * @brief Reads from or writes audio frames to a file descriptor.
+ * @{
+ *
+ * File descriptor port profides a simple way to pass audio frames
+ * to/from an open file descriptor (a file, socket, pipe etc.)
+ * which can be the same or a different process
+ * (e.g. a speech recognizer or synthesizer).
+ *
+ * It can operate either in blocking or non-blocking mode. In blocking
+ * mode the user must make sure the I/O operations complete quicky,
+ * in non-blocking mode the data is buffered automatically.
+ */
+
+
+PJ_BEGIN_DECL
+
+/**
+ * File descriptor media port options.
+ */
+enum pjmedia_file_descriptor_option
+{
+	/**
+	 * Use file descriptors in blocking mode.
+	 */
+	PJMEDIA_FD_BLOCK = 0,
+
+	/**
+	 * Use file-descriptors in non-blocking mode and buffer data.
+	 */
+	PJMEDIA_FD_NONBLOCK = 1,
+
+	/**
+	 * Parameters fd_in and fd_out are Windows file handles
+	 * instead of integer file descriptors (Windows only).
+	 */
+	PJMEDIA_FD_HANDLES = 2
+};
+
+
+/**
+ * Create file descriptor media port. 
+ *
+ * @param pool			Pool to allocate memory.
+ * @param sampling_rate		Sampling rate of the port.
+ * @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 fd_in		Descriptor open for reading (i.e. audio source)
+ *             		or -1 to disable audio input.
+ * @param fd_out		Descriptor open for writing (i.e. audio sink)
+ *             		or -1 to disable audio output.
+ * @param flags		0 or PJMEDIA_FD_BLOCK for default, i.e. blocking mode,
+ *             		or ORed pjmedia_file_descriptor_option flags:
+ *             		- PJMEDIA_FD_NONBLOCK (1) for non-blocking mode
+ *             		  (O_NONBLOCK is fcntl'ed on the descriptors if specified).
+ *             		- PJMEDIA_FD_HANDLES (2): Parameters fd_in and fd_out are Windows
+ *             		  file handles instead of integer file descriptors (Windows only).
+ * @param p_port		Pointer to receive the port instance.
+ *
+ * @return			PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_fd_port_create( pj_pool_t *pool,
+							unsigned sampling_rate,
+							unsigned channel_count,
+							unsigned samples_per_frame,
+							unsigned bits_per_sample,
+							int fd_in,
+							int fd_out,
+							unsigned flags,
+							pjmedia_port **p_port );
+
+
+PJ_END_DECL
+
+/**
+ * @}
+ */
+
+
+#endif	/* __PJMEDIA_FD_PORT_H__ */

Property changes on: pjmedia/include/pjmedia/fd_port.h
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+Id
\ No newline at end of property
Index: pjmedia/include/pjmedia/signatures.h
===================================================================
--- pjmedia/include/pjmedia/signatures.h	(revision 4770)
+++ pjmedia/include/pjmedia/signatures.h	(working copy)
@@ -147,6 +147,7 @@
 #define PJMEDIA_SIG_IS_CLASS_PORT_AUD(s) ((s)>>24=='P' && (s)>>16=='A')
 
 #define PJMEDIA_SIG_PORT_BIDIR		PJMEDIA_SIG_CLASS_PORT_AUD('B','D')
+#define PJMEDIA_SIG_PORT_FD			PJMEDIA_SIG_CLASS_PORT_AUD('F','D')
 #define PJMEDIA_SIG_PORT_CONF		PJMEDIA_SIG_CLASS_PORT_AUD('C','F')
 #define PJMEDIA_SIG_PORT_CONF_PASV	PJMEDIA_SIG_CLASS_PORT_AUD('C','P')
 #define PJMEDIA_SIG_PORT_CONF_SWITCH	PJMEDIA_SIG_CLASS_PORT_AUD('C','S')
Index: pjmedia/src/pjmedia/fd_port.c
===================================================================
--- pjmedia/src/pjmedia/fd_port.c	(revision 0)
+++ pjmedia/src/pjmedia/fd_port.c	(working copy)
@@ -0,0 +1,383 @@
+/* $Id$ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny at 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/fd_port.h>
+#include <pjmedia/errno.h>
+#include <pj/assert.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+
+#if PJ_WIN32
+#	define WIN32_LEAN_AND_MEAN
+#	include <Windows.h>
+#	include <io.h>
+#else
+#	include <unistd.h>
+#	include <fcntl.h>
+#	include <errno.h>
+#endif
+
+#define SIGNATURE   PJMEDIA_SIG_PORT_FD
+
+#ifdef O_NONBLOCK
+
+/** Buffer size */
+#define FD_BUF_SIZE 131072U  /* 128 KiB */
+
+/** Audio data cyclic buffer */
+typedef struct fd_buf_t {
+	/**
+	 * Position in the buffer to write next data to.
+	 * Position to read data from is ((FD_BUF_SIZE + pos - len) % FD_BUF_SIZE).
+	 */
+	pj_size_t       pos;
+
+	/** Bytes stored in the buffer. */
+	pj_size_t       len;
+	char            data[FD_BUF_SIZE];
+} fd_buf_t;
+
+#endif  /* O_NONBLOCK */
+
+/** File descriptor port implementation. */
+struct fd_port {
+	pjmedia_port base;
+
+	int          fd_in;
+	int          fd_out;
+#ifdef O_NONBLOCK
+	fd_buf_t    *buf_in;
+	fd_buf_t    *buf_out;
+#endif  /* O_NONBLOCK */
+	pj_timestamp timestamp;
+};
+
+/** Get frame (blocking mode) */
+static pj_status_t fd_port_get_frame_b(pjmedia_port *this_port, 
+				  pjmedia_frame *frame);
+/** Put frame (blocking mode) */
+static pj_status_t fd_port_put_frame_b(pjmedia_port *this_port, 
+				  pjmedia_frame *frame);
+
+#ifdef O_NONBLOCK
+/** Get frame (non-blocking mode) */
+static pj_status_t fd_port_get_frame_nb(pjmedia_port *this_port, 
+				  pjmedia_frame *frame);
+/** Put frame (non-blocking mode) */
+static pj_status_t fd_port_put_frame_nb(pjmedia_port *this_port, 
+				  pjmedia_frame *frame);
+#endif  /* O_NONBLOCK */
+
+/** Destroy and free buffers */
+static pj_status_t fd_port_on_destroy(pjmedia_port *this_port);
+
+
+PJ_DEF(pj_status_t) pjmedia_fd_port_create( pj_pool_t *pool,
+							unsigned sampling_rate,
+							unsigned channel_count,
+							unsigned samples_per_frame,
+							unsigned bits_per_sample,
+							int fd_in,
+							int fd_out,
+							unsigned flags,
+							pjmedia_port **p_port )
+{
+	struct fd_port *fdport;
+	const pj_str_t name = pj_str("fd-port");
+
+	PJ_ASSERT_RETURN(pool && sampling_rate && channel_count &&
+			samples_per_frame && bits_per_sample && p_port &&
+			((fd_in >= 0) || (fd_out >= 0)),
+		PJ_EINVAL);
+#ifndef O_NONBLOCK
+	PJ_ASSERT_RETURN(!(flags & PJMEDIA_FD_NONBLOCK), PJ_ENOTSUP);
+#endif
+#if !PJ_WIN32
+	PJ_ASSERT_RETURN(!(flags & PJMEDIA_FD_HANDLES), PJ_EINVAL);
+#endif
+
+	fdport = PJ_POOL_ZALLOC_T(pool, struct fd_port);
+	PJ_ASSERT_RETURN(fdport != NULL, PJ_ENOMEM);
+
+	/* Create the port */
+	pjmedia_port_info_init(&fdport->base.info, &name, SIGNATURE, sampling_rate,
+		channel_count, bits_per_sample, samples_per_frame);
+
+#if PJ_WIN32
+	if (!(flags & PJMEDIA_FD_HANDLES))
+	{
+		/* PJMEDIA_FD_UNUSED should be equal to INVALID_HANLDE_VALUE */
+		if (fd_in >= 0) fdport->fd_in = (int)_get_osfhandle(fd_in);
+		if (fd_out >= 0) fdport->fd_out = (int)_get_osfhandle(fd_out);
+	}
+	else
+#endif  /* PJ_WIN32 */
+	{
+		fdport->fd_in = fd_in;
+		fdport->fd_out = fd_out;
+	}
+
+	if (fdport->fd_in >= 0) {
+#ifdef O_NONBLOCK
+		if (flags & PJMEDIA_FD_NONBLOCK) {
+			int fl = fcntl(fdport->fd_in, F_GETFL);
+			if (fl < 0) return pj_get_os_error();
+			fl |= O_NONBLOCK;
+			fl = fcntl(fdport->fd_in, F_SETFL, fl);
+			if (fl < 0) return pj_get_os_error();
+			fdport->buf_in = PJ_POOL_ALLOC_T(pool, fd_buf_t);
+			PJ_ASSERT_RETURN(fdport->buf_in, PJ_ENOMEM);
+			fdport->buf_in->pos = 0;
+			fdport->buf_in->len = 0;
+			fdport->base.get_frame = &fd_port_get_frame_nb;
+		}
+		else
+#endif  /* O_NONBLOCK */
+		{
+			fdport->base.get_frame = &fd_port_get_frame_b;
+		}
+	}
+
+	if (fdport->fd_out >= 0) {
+#ifdef O_NONBLOCK
+		if (flags & PJMEDIA_FD_NONBLOCK) {
+			int fl = fcntl(fdport->fd_out, F_GETFL);
+			if (fl < 0) return pj_get_os_error();
+			fl |= O_NONBLOCK;
+			fl = fcntl(fdport->fd_out, F_SETFL, fl);
+			if (fl < 0) return pj_get_os_error();
+			fdport->buf_out = PJ_POOL_ALLOC_T(pool, fd_buf_t);
+			PJ_ASSERT_RETURN(fdport->buf_out, PJ_ENOMEM);
+			fdport->buf_out->pos = 0;
+			fdport->buf_out->len = 0;
+			fdport->base.put_frame = &fd_port_put_frame_nb;
+		}
+		else
+#endif  /* O_NONBLOCK */
+		{
+			fdport->base.put_frame = &fd_port_put_frame_b;
+		}
+	}
+
+	fdport->base.on_destroy = &fd_port_on_destroy;
+
+	*p_port = &fdport->base;
+
+	return PJ_SUCCESS;
+}
+
+
+static pj_status_t fd_port_put_frame_b(pjmedia_port *this_port,
+							pjmedia_frame *frame)
+{
+	const struct fd_port *fdport;
+
+	PJ_ASSERT_RETURN(this_port->info.signature == SIGNATURE,
+		PJ_EINVALIDOP);
+
+	fdport = (struct fd_port*) this_port;
+	if (frame->type == PJMEDIA_FRAME_TYPE_AUDIO) {
+#if PJ_WIN32
+		DWORD written;
+		BOOL succ = WriteFile((HANDLE)(pj_size_t)fdport->fd_out, frame->buf, (DWORD)frame->size, &written, NULL);
+		if (!succ) return pj_get_os_error();
+		if (written != frame->size) return PJ_EUNKNOWN;
+#else  /* PJ_WIN32 */
+		int ret = write(fdport->fd_out, frame->buf, frame->size);
+		if (ret < 0) return pj_get_os_error();
+		if (ret != frame->size) return PJ_EUNKNOWN;
+#endif  /* PJ_WIN32 else */
+	}
+
+	return PJ_SUCCESS;
+}
+
+
+static pj_status_t fd_port_get_frame_b(pjmedia_port *this_port, 
+							pjmedia_frame *frame)
+{
+	struct fd_port *fdport;
+	pj_size_t size;
+#if PJ_WIN32
+	BOOL succ;
+	DWORD ret;
+#else  /* PJ_WIN32 */
+	int ret;
+#endif  /* PJ_WIN32 else */
+
+	PJ_ASSERT_RETURN(this_port->info.signature == SIGNATURE,
+		PJ_EINVALIDOP);
+
+	fdport = (struct fd_port*) this_port;
+	size = PJMEDIA_PIA_AVG_FSZ(&this_port->info);
+#if PJ_WIN32
+	succ = ReadFile((HANDLE)(pj_size_t)fdport->fd_in, frame->buf, (DWORD)size, &ret, NULL);
+	if (!succ) ret = 0;
+	if (succ) {
+#else  /* PJ_WIN32 */
+	ret = read(fdport->fd_in, frame->buf, size);
+	if (ret > 0) {
+#endif  /* PJ_WIN32 else */
+		frame->type = PJMEDIA_FRAME_TYPE_AUDIO;
+		frame->size = ret;
+		/* Is this the correct timestamp calculation? */
+		frame->timestamp.u64 = fdport->timestamp.u64;
+		fdport->timestamp.u64 += PJMEDIA_PIA_SPF(&this_port->info);
+	} else {
+		frame->type = PJMEDIA_FRAME_TYPE_NONE;
+		frame->size = 0;
+		return pj_get_os_error();
+	}
+
+	return PJ_SUCCESS;
+}
+
+
+#ifdef O_NONBLOCK
+
+/** Assumes buf->len + len <= FD_BUF_SIZE */
+static void fd_buf_append(fd_buf_t *buf, const char *data, pj_size_t len)
+{
+	pj_size_t sz = len;
+	if (buf->pos + sz > FD_BUF_SIZE)
+		sz = FD_BUF_SIZE - buf->pos;
+	memcpy(buf->data + buf->pos, data, sz);
+	buf->pos += sz;
+	buf->len += len;
+	len -= sz;
+	if (buf->pos >= FD_BUF_SIZE)
+		buf->pos = 0;
+	memcpy(buf->data, data + sz, len);
+}
+
+
+static pj_status_t fd_port_put_frame_nb(pjmedia_port *this_port,
+							pjmedia_frame *frame)
+{
+	struct fd_port *fdport;
+
+	PJ_ASSERT_RETURN(this_port->info.signature == SIGNATURE,
+		PJ_EINVALIDOP);
+
+	fdport = (struct fd_port*) this_port;
+	/* Write out the buffer first */
+	while (fdport->buf_out->len) {
+		int ret;
+		pj_size_t sz = fdport->buf_out->len;
+		pj_size_t pos = (FD_BUF_SIZE + fdport->buf_out->pos - sz) % FD_BUF_SIZE;
+		if (pos + sz > FD_BUF_SIZE) sz = FD_BUF_SIZE - pos;
+		ret = write(fdport->fd_out, fdport->buf_out->data + pos, sz);
+		if (ret <= 0) {
+			if ((ret == 0) || (errno == EAGAIN) || (errno == EWOULDBLOCK))
+				break;
+			return pj_get_os_error();
+		}
+		fdport->buf_out->len -= ret;
+	}
+	if (frame->type == PJMEDIA_FRAME_TYPE_AUDIO) {
+		if (fdport->buf_out->len) {
+			/* Append the frame to the buffer */
+			if (fdport->buf_out->len + frame->size > FD_BUF_SIZE)
+				return PJ_ETOOMANY;
+			fd_buf_append(fdport->buf_out, (char*)frame->buf, frame->size);
+		} else {
+			/* Try to write the frame immediately */
+			int ret = write(fdport->fd_out, frame->buf, frame->size);
+			if ((ret < 0) && (errno != EAGAIN) && (errno != EWOULDBLOCK))
+				return pj_get_os_error();
+			if (ret < 0) ret = 0;
+			if (ret < frame->size) {
+				/* Buffer the rest */
+				if (fdport->buf_out->len + frame->size - ret > FD_BUF_SIZE)
+					return PJ_ETOOMANY;
+				fd_buf_append(fdport->buf_out, (char*)frame->buf + ret, frame->size - ret);
+			}
+		}
+	}
+
+	return PJ_SUCCESS;
+}
+
+
+static pj_status_t fd_port_get_frame_nb(pjmedia_port *this_port, 
+							pjmedia_frame *frame)
+{
+	struct fd_port *fdport;
+	pj_size_t size;
+
+	PJ_ASSERT_RETURN(this_port->info.signature == SIGNATURE,
+		PJ_EINVALIDOP);
+
+	fdport = (struct fd_port*) this_port;
+	/* Buffer as much data first */
+	while (fdport->buf_in->len < FD_BUF_SIZE) {
+		int ret;
+		pj_size_t sz = FD_BUF_SIZE - fdport->buf_in->len;
+		if (fdport->buf_in->pos + sz > FD_BUF_SIZE)
+			sz = FD_BUF_SIZE - fdport->buf_in->pos;
+		ret = read(fdport->fd_in,
+			fdport->buf_in->data + fdport->buf_in->pos, sz);
+		if (ret <= 0) {
+			if ((ret == 0) || (errno == EAGAIN) || (errno == EWOULDBLOCK))
+				break;
+			return pj_get_os_error();
+		}
+		fdport->buf_in->pos += ret;
+		fdport->buf_in->len -= ret;
+		if (fdport->buf_in->pos >= FD_BUF_SIZE) fdport->buf_in->pos = 0;
+	}
+	size = PJMEDIA_PIA_AVG_FSZ(&this_port->info);
+	if (fdport->buf_in->len >= size) {
+		/* Enough data for whole frame */
+		pj_size_t sz = size;
+		pj_size_t pos = (FD_BUF_SIZE + fdport->buf_in->pos - fdport->buf_in->len) % FD_BUF_SIZE;
+		if (pos + sz > FD_BUF_SIZE) sz = FD_BUF_SIZE - pos;
+		memcpy(frame->buf, fdport->buf_in->data + pos, sz);
+		if (sz < size)
+			memcpy((char*)frame->buf + sz, fdport->buf_in->data, size - sz);
+		fdport->buf_in->len -= size;
+		frame->type = PJMEDIA_FRAME_TYPE_AUDIO;
+		frame->size = size;
+		/* Is this the correct timestamp calculation? */
+		frame->timestamp.u64 = fdport->timestamp.u64;
+		fdport->timestamp.u64 += PJMEDIA_PIA_SPF(&this_port->info);
+	} else {
+		frame->type = PJMEDIA_FRAME_TYPE_NONE;
+		frame->size = 0;
+	}
+
+	return PJ_SUCCESS;
+}
+
+#endif  /* O_NONBLOCK */
+
+
+/*
+ * Destroy port.
+ */
+static pj_status_t fd_port_on_destroy(pjmedia_port *this_port)
+{
+	PJ_ASSERT_RETURN(this_port->info.signature == SIGNATURE,
+		PJ_EINVALIDOP);
+
+	/* Destroy signature */
+	this_port->info.signature = 0;
+
+	return PJ_SUCCESS;
+}

Property changes on: pjmedia/src/pjmedia/fd_port.c
___________________________________________________________________
Added: svn:keywords
## -0,0 +1 ##
+Id
\ No newline at end of property
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: pjsip-apps/src/python/_pjsua.c
===================================================================
--- pjsip-apps/src/python/_pjsua.c	(revision 4770)
+++ pjsip-apps/src/python/_pjsua.c	(working copy)
@@ -2670,6 +2670,62 @@
 }
 
 /*
+ * py_pjsua_audio_fd_create
+ */
+static PyObject *py_pjsua_audio_fd_create(PyObject *pSelf, PyObject *pArgs)
+{
+	pj_status_t status;
+	int id = PJSUA_INVALID_ID;
+	int fd_in, fd_out;
+	unsigned options;
+	PJ_UNUSED_ARG(pSelf);
+
+	if (!PyArg_ParseTuple(pArgs, "iiI", &fd_in, &fd_out, &options))
+		return NULL;
+
+	status = pjsua_audio_fd_create(fd_in, fd_out, options, &id);
+	return Py_BuildValue("ii", status, id);
+}
+
+/*
+ * py_pjsua_audio_fd_get_conf_port
+ */
+static PyObject *py_pjsua_audio_fd_get_conf_port(PyObject *pSelf, 
+						 PyObject *pArgs)
+{
+
+	int id, port_id;
+
+	PJ_UNUSED_ARG(pSelf);
+
+	if (!PyArg_ParseTuple(pArgs, "i", &id)) {
+		return NULL;
+	}
+
+	port_id = pjsua_audio_fd_get_conf_port(id);
+
+	return Py_BuildValue("i", port_id);
+}
+
+/*
+ * py_pjsua_audio_fd_destroy
+ */
+static PyObject *py_pjsua_audio_fd_destroy(PyObject *pSelf, PyObject *pArgs)
+{
+	int id;
+	int status;
+
+	PJ_UNUSED_ARG(pSelf);
+
+	if (!PyArg_ParseTuple(pArgs, "i", &id)) {
+		return NULL;
+	}
+
+	status = pjsua_audio_fd_destroy(id);
+	return Py_BuildValue("i", status);
+}
+
+/*
  * py_pjsua_enum_snd_devs
  */
 static PyObject *py_pjsua_enum_snd_devs(PyObject *pSelf, PyObject *pArgs)
@@ -2998,6 +3054,20 @@
 static char pjsua_recorder_destroy_doc[] =
     "int _pjsua.recorder_destroy (int id) "
     "Destroy recorder (this will complete recording).";
+static char pjsua_audio_fd_create_doc[] =
+    "int, int _pjsua.audio_fd_create (int fd_in, int fd_out, unsigned flags) "
+    "Create a file descriptor port, and automatically connect this port "
+    "to the conference bridge. Descriptors may be -1 to disable the direction. "
+    "Flags may be zero for blocking I/O or ORed 1 for non-blocking I/O, "
+    "2 for accepting Windows file handles insted of integer descriptors. "
+    "Handles are preferred on Windows platform. Use "
+    "msvcrt.get_osfhandle(a.fileno()) or win32file._get_osfhandle(a.fileno()).";
+static char pjsua_audio_fd_get_conf_port_doc[] =
+    "int _pjsua.audio_fd_get_conf_port (int id) "
+    "Get conference port associated with file descriptor port.";
+static char pjsua_audio_fd_destroy_doc[] =
+    "int _pjsua.audio_fd_destroy (int id) "
+    "Destroy audio file descriptor port.";
 static char pjsua_enum_snd_devs_doc[] =
     "_pjsua.PJMedia_Snd_Dev_Info[] _pjsua.enum_snd_devs (int count) "
     "Enum sound devices.";
@@ -4281,6 +4351,18 @@
         pjsua_recorder_destroy_doc
     },
     {
+        "audio_fd_create", py_pjsua_audio_fd_create, METH_VARARGS,
+        pjsua_audio_fd_create_doc
+    },
+    {
+        "audio_fd_get_conf_port", py_pjsua_audio_fd_get_conf_port, METH_VARARGS,
+        pjsua_audio_fd_get_conf_port_doc
+    },
+    {
+        "audio_fd_destroy", py_pjsua_audio_fd_destroy, METH_VARARGS,
+        pjsua_audio_fd_destroy_doc
+    },
+    {
         "enum_snd_devs", py_pjsua_enum_snd_devs, METH_VARARGS,
         pjsua_enum_snd_devs_doc
     },
Index: pjsip-apps/src/python/pjsua.py
===================================================================
--- pjsip-apps/src/python/pjsua.py	(revision 4770)
+++ pjsip-apps/src/python/pjsua.py	(working copy)
@@ -2694,7 +2694,66 @@
         err = _pjsua.recorder_destroy(rec_id)
         self._err_check("recorder_destroy()", self, err)
 
+    def create_audio_fd(self, fd_in = -1, fd_out = -1, flags = 0):
+        """Create audio file descriptor port.
 
+        Keyword arguments
+        fd_in  -- Input file descriptor/handle or -1 to disable
+        fd_out -- Output file descriptor/handle or -1 to disable
+        flags  -- 0 for blocking I/O (default) or ORed
+                   - 1 for non-blocking I/O with buffering (UNIX only)
+                   - 2 fd_in and fd_out are Windows file handle_events
+                       instead of integer descriptors (Windows only)
+
+        File descriptors and handles can be obtained as follows:
+            f = io.open("file.wav", "rb")
+            descriptor = f.fileno()
+            handle = msvcrt.get_osfhandle(descriptor)
+            # or
+            handle = win32file._get_osfhandle(descriptor)
+
+        On Windows, passing handles is preferred to descriptors, since
+        descriptors only work with the same MSVCRT DLL (not static)
+        version across all libraries.
+
+        Return:
+            File descriptor audio port ID
+
+        """
+        lck = self.auto_lock()
+        err, fdp_id = _pjsua.audio_fd_create(fd_in, fd_out, flags)
+        self._err_check("create_audio_fd()", self, err)
+        return fdp_id
+
+    def audio_fd_get_slot(self, fdp_id):
+        """Get the conference port ID for the specified file descriptor port.
+
+        Keyword arguments:
+        fdp_id  -- the file descriptor port ID
+
+        Return:
+            Conference slot number for the file descriptor port
+
+        """
+        lck = self.auto_lock()
+        slot = _pjsua.audio_fd_get_conf_port(fdp_id)
+        if slot < 1:
+            self._err_check("audio_fd_get_slot()", self, -1,
+                            "Invalid file descriptor port id")
+        return slot
+
+    def audio_fd_destroy(self, fdp_id):
+        """Destroy the file descriptor port.
+
+        Keyword arguments:
+        fdp_id   -- the audio callback ID.
+
+        """
+        lck = self.auto_lock()
+        err = _pjsua.audio_fd_destroy(fdp_id)
+        self._err_check("audio_fd_destroy()", self, err)
+
+
     # Internal functions
 
     @staticmethod
Index: pjsip-apps/src/python/samples/audio_fd.py
===================================================================
--- pjsip-apps/src/python/samples/audio_fd.py	(revision 0)
+++ pjsip-apps/src/python/samples/audio_fd.py	(working copy)
@@ -0,0 +1,152 @@
+# $Id: audio_cb.py 2171 2008-07-24 09:01:33Z bennylp $
+#
+# SIP account and registration sample. In this sample, the program
+# will block to wait until registration is complete
+#
+# Copyright (C) 2003-2008 Benny Prijono <benny at 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
+#
+import sys
+import pjsua as pj
+import io
+try:
+    # On Windows, pass handles rather than descriptors
+    import msvcrt
+except:
+    pass
+
+# File name to read 8kHz PCM from
+FN_IN = "in.pcm"
+# File name to write 8kHz PCM to
+FN_OUT = "out.pcm"
+# Flags passed to FD port
+FD_NONBLOCK = 1
+FD_HANDLES  = 2
+
+
+def log_cb(level, str, len):
+    print str,
+
+
+class MyCallCallback(pj.CallCallback):
+
+    def __init__(self, call=None):
+        pj.CallCallback.__init__(self, call)
+
+    def on_state(self):
+        if self.call.info().state == pj.CallState.DISCONNECTED:
+            global g_current_call
+            g_current_call = None
+            print "Call hung up"
+
+    def on_media_state(self):
+        info = self.call.info()
+        call_slot = info.conf_slot
+        if (info.media_state == pj.MediaState.ACTIVE) and (call_slot >= 0):
+            print "Call slot:", call_slot
+            global g_fdp_id
+            fdp_slot = lib.audio_fd_get_slot(g_fdp_id)
+            print "Audio FD:", g_fdp_id, " slot:", fdp_slot
+            lib.conf_connect(call_slot, fdp_slot)
+            lib.conf_connect(fdp_slot, call_slot)
+
+
+class MyAccountCallback(pj.AccountCallback):
+
+    def __init__(self, account=None):
+        pj.AccountCallback.__init__(self, account)
+
+    # Notification on incoming call
+    def on_incoming_call(self, call):
+        global g_current_call
+        if g_current_call:
+            call.answer(486, "Busy")
+            return
+
+        call.set_callback(MyCallCallback(call))
+        info = call.info()
+        print "Incoming call from", info.remote_uri
+        call.answer()
+        g_current_call = call
+
+
+lib = pj.Lib()
+
+try:
+    mcfg = pj.MediaConfig()
+    mcfg.clock_rate = 8000
+    mcfg.no_vad = True
+    lib.init(log_cfg = pj.LogConfig(level=4, callback=log_cb),
+             media_cfg = mcfg)
+
+    # This is a MUST if not using a HW sound
+    lib.set_null_snd_dev()
+
+    # Create UDP transport which listens to any available port
+    transport = lib.create_transport(pj.TransportType.UDP,
+                                     pj.TransportConfig(0))
+    print "\nListening on", transport.info().host,
+    print "port", transport.info().port, "\n"
+
+    lib.start(True)
+
+    # Create local account
+    acc = lib.create_account_for_transport(transport, cb=MyAccountCallback())
+
+    try:
+        f_in = io.open(FN_IN, "rb")
+        fd_in = f_in.fileno()
+    except:
+        fd_in = -1
+        print "\nInput file", FN_IN, "cannot be opened for reading\n"
+
+    try:
+        f_out = io.open(FN_OUT, "wb")
+        fd_out = f_out.fileno()
+    except:
+        fd_out = -1
+        print "\nOutput file", FN_OUT, "cannot be opened for writing\n"
+
+    # Try to pass Windows handles instead of descriptors
+    flags = 0
+    if 'msvcrt' in sys.modules:
+        if fd_in >= 0: fd_in = msvcrt.get_osfhandle(fd_in)
+        if fd_out >= 0: fd_out = msvcrt.get_osfhandle(fd_out)
+        flags |= FD_HANDLES
+
+    g_current_call = None
+    g_fdp_id = lib.create_audio_fd(fd_in, fd_out, flags)
+    print "Audio file descriptor port ID:", g_fdp_id
+
+    print "\nWaiting for incoming call"
+    my_sip_uri = "sip:" + transport.info().host + \
+                 ":" + str(transport.info().port)
+    print "My SIP URI is", my_sip_uri
+    print "\nPress ENTER to quit"
+    sys.stdin.readline()
+
+    # Shutdown the library
+    lib.audio_fd_destroy(g_fdp_id)
+    transport = None
+    acc.delete()
+    acc = None
+    lib.destroy()
+    lib = None
+
+except pj.Error, e:
+    print "Exception: " + str(e)
+    lib.destroy()
+
Index: pjsip-apps/src/swig/python/audio_fd.py
===================================================================
--- pjsip-apps/src/swig/python/audio_fd.py	(revision 0)
+++ pjsip-apps/src/swig/python/audio_fd.py	(working copy)
@@ -0,0 +1,155 @@
+import pjsua2 as pj
+import sys
+import io
+import traceback
+try:
+	# On Windows, pass handles rather than descriptors
+	import msvcrt
+except:
+	pass
+
+# File name to read 8kHz PCM from
+FN_IN = "in.pcm"
+# File name to write 8kHz PCM to
+FN_OUT = "out.pcm"
+
+
+#
+# Custom log writer
+#
+class MyLogWriter(pj.LogWriter):
+	def write(self, entry):
+		print entry.msg,
+
+
+#
+# Class to receive call events
+#
+class MyCall(pj.Call):
+	def onCallState(self, prm):
+		info = self.getInfo()
+		if info.state == pj.PJSIP_INV_STATE_DISCONNECTED:
+			global g_current_call
+			g_current_call = None
+			print "Call hung up"
+
+	def onCallMediaState(self, prm):
+		if not self.hasMedia(): return
+		info = self.getInfo()
+
+		# Do not use iterators, may cause crash!
+		#for mi in info.media:
+
+		for i in range(0, len(info.media)):
+			mi = info.media[i]
+			if mi.type != pj.PJMEDIA_TYPE_AUDIO or \
+					mi.status != pj.PJSUA_CALL_MEDIA_ACTIVE:
+				continue
+			m = self.getMedia(mi.index)
+			if not m: continue
+			global g_fdp
+			am = pj.AudioMedia.typecastFromMedia(m)
+			g_fdp.startTransmit(am)
+			am.startTransmit(g_fdp)
+
+
+#
+# Class to receive account events
+#
+class MyAccount(pj.Account):
+    def onIncomingCall(self, prm):
+	global g_current_call
+	call = MyCall(self, prm.callId)
+	op = pj.CallOpParam(True)
+	if g_current_call:
+		op.statusCode = pj.PJSIP_SC_BUSY_HERE
+		call.hangup(op)
+		return
+	info = call.getInfo()
+	print "Incoming call from", info.remoteUri
+	op.statusCode = pj.PJSIP_SC_OK
+	g_current_call = call
+	call.answer(op)
+
+
+#
+# Write PJ Error info
+#
+def handleError(error):
+	print
+	print 'Exception:'
+	print '  ', error.info()
+	print 'Traceback:'
+	print traceback.print_stack()
+	print
+
+
+ep = pj.Endpoint()
+try:
+	ep.libCreate()
+except pj.Error, error:
+	handleError(error)
+	sys.exit(1)
+
+acc = None
+g_current_call = None
+g_fdp = None
+try:
+	logger = MyLogWriter()
+	cfg = pj.EpConfig()
+	cfg.logConfig.msgLogging = False
+	cfg.logConfig.writer = logger
+	cfg.medConfig.clockRate = 8000
+	cfg.medConfig.noVad = True
+	ep.libInit(cfg)
+	ep.audDevManager().setNullDev()
+
+	tcfg = pj.TransportConfig()
+	tcfg.port = 0  # Any available
+	tid = ep.transportCreate(pj.PJSIP_TRANSPORT_UDP, tcfg)
+	tinfo = ep.transportGetInfo(tid)
+
+	acfg = pj.AccountConfig()
+	acfg.idUri = "sip:" + tinfo.localName
+	acc = MyAccount()
+	acc.create(acfg)
+
+	ep.libStart()
+
+	try:
+		f_in = io.open(FN_IN, "rb")
+		fd_in = f_in.fileno()
+	except:
+		fd_in = -1
+		print "\nInput file", FN_IN, "cannot be opened for reading\n"
+
+	try:
+		f_out = io.open(FN_OUT, "wb")
+		fd_out = f_out.fileno()
+	except:
+		fd_out = -1
+		print "\nOutput file", FN_OUT, "cannot be opened for writing\n"
+
+	# Try to pass Windows handles instead of descriptors
+	flags = 0
+	if 'msvcrt' in sys.modules:
+		if fd_in >= 0: fd_in = msvcrt.get_osfhandle(fd_in)
+		if fd_out >= 0: fd_out = msvcrt.get_osfhandle(fd_out)
+		flags |= pj.PJMEDIA_FD_HANDLES
+
+	g_fdp = pj.AudioMediaFD()
+	g_fdp.createFD(fd_in, fd_out, flags)
+	print "Audio file descriptor port ID:", g_fdp.getPortId()
+
+	print "\nWaiting for incoming call"
+	print "My SIP URI is", acfg.idUri
+	print "\nPress ENTER to quit"
+	sys.stdin.readline()
+except pj.Error, error:
+	handleError(error)
+
+# Shutdown the library -- destroy objects manually, do not let GC
+if g_fdp: del g_fdp
+if acc: del acc
+ep.libDestroy()
+del ep
Index: pjsip-apps/src/swig/python/Makefile
===================================================================
--- pjsip-apps/src/swig/python/Makefile	(revision 4770)
+++ pjsip-apps/src/swig/python/Makefile	(working copy)
@@ -22,7 +22,7 @@
 	cp gcc.exe g++.exe
 
 pjsua2_wrap.cpp: ../pjsua2.i ../symbols.i Makefile $(SRCS)
-	swig $(SWIG_FLAGS) -python  -o pjsua2_wrap.cpp ../pjsua2.i
+	swig $(SWIG_FLAGS) -python -threads -o pjsua2_wrap.cpp ../pjsua2.i
 
 clean distclean realclean:
 	rm -rf $(PYTHON_SO) pjsua2_wrap.cpp pjsua2_wrap.h pjsua2.py build *.pyc
Index: pjsip-apps/src/swig/symbols.i
===================================================================
--- pjsip-apps/src/swig/symbols.i	(revision 4770)
+++ pjsip-apps/src/swig/symbols.i	(working copy)
@@ -53,6 +53,8 @@
 
 enum pjmedia_file_player_option {PJMEDIA_FILE_NO_LOOP = 1};
 
+enum pjmedia_file_descriptor_option {PJMEDIA_FD_BLOCK = 0, PJMEDIA_FD_NONBLOCK = 1, PJMEDIA_FD_HANDLES = 2};
+
 typedef enum pjmedia_type {PJMEDIA_TYPE_NONE, PJMEDIA_TYPE_AUDIO, PJMEDIA_TYPE_VIDEO, PJMEDIA_TYPE_APPLICATION, PJMEDIA_TYPE_UNKNOWN} pjmedia_type;
 
 typedef enum pjmedia_dir {PJMEDIA_DIR_NONE = 0, PJMEDIA_DIR_ENCODING = 1, PJMEDIA_DIR_CAPTURE = PJMEDIA_DIR_ENCODING, PJMEDIA_DIR_DECODING = 2, PJMEDIA_DIR_PLAYBACK = PJMEDIA_DIR_DECODING, PJMEDIA_DIR_RENDER = PJMEDIA_DIR_DECODING, PJMEDIA_DIR_ENCODING_DECODING = 3, PJMEDIA_DIR_CAPTURE_PLAYBACK = PJMEDIA_DIR_ENCODING_DECODING, PJMEDIA_DIR_CAPTURE_RENDER = PJMEDIA_DIR_ENCODING_DECODING} pjmedia_dir;
Index: pjsip-apps/src/swig/symbols.lst
===================================================================
--- pjsip-apps/src/swig/symbols.lst	(revision 4770)
+++ pjsip-apps/src/swig/symbols.lst	(working copy)
@@ -13,6 +13,7 @@
 pjmedia-videodev/videodev.h	pjmedia_vid_dev_index pjmedia_vid_dev_std_index
 pjmedia-audiodev/audiodev.h 	pjmedia_aud_dev_route pjmedia_aud_dev_cap
 pjmedia/wav_port.h              pjmedia_file_writer_option pjmedia_file_player_option
+pjmedia/fd_port.h               pjmedia_file_descriptor_option
 pjmedia/types.h                 pjmedia_type pjmedia_dir pjmedia_tp_proto
 pjmedia/format.h		pjmedia_format_id
 
Index: pjsip/include/pjsua-lib/pjsua.h
===================================================================
--- pjsip/include/pjsua-lib/pjsua.h	(revision 4770)
+++ pjsip/include/pjsua-lib/pjsua.h	(working copy)
@@ -6161,6 +6161,76 @@
 
 
 /*****************************************************************************
+ * File descriptor audio I/O.
+ */
+
+/**
+ * Create a file descriptor I/O, and automatically connect this port to
+ * the conference bridge.
+ *
+ * File descriptor port profides a simple way to pass audio frames
+ * to/from an open file descriptor (a file, socket, pipe etc.)
+ * which can be the same or a different process
+ * (e.g. a speech recognizer or synthesizer).
+ *
+ * It can operate either in blocking or non-blocking mode. In blocking
+ * mode the user must make sure the I/O operations complete quicky,
+ * in non-blocking mode the data is buffered automatically.
+ *
+ * @param fd_in		Descriptor open for reading (i.e. audio source)
+ *             		or -1 to disable audio input.
+ * @param fd_out		Descriptor open for writing (i.e. audio sink)
+ *             		or -1 to disable audio output.
+ * @param flags		0 or PJMEDIA_FD_BLOCK for default, i.e. blocking mode,
+ *             		or ORed pjmedia_file_descriptor_option flags:
+ *             		- PJMEDIA_FD_NONBLOCK (1) for non-blocking mode
+ *             		  (O_NONBLOCK is fcntl'ed on the descriptors if specified).
+ *             		- PJMEDIA_FD_HANDLES (2): Parameters fd_in and fd_out are Windows
+ *             		  file handles instead of integer file descriptors (Windows only).
+ * @param p_id		   Pointer to receive the file descriptor port instance.
+ *                  The id space is shared with recorders.
+ *
+ * @return		PJ_SUCCESS on success, or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pjsua_audio_fd_create(int fd_in,
+                                           int fd_out,
+                                           unsigned flags,
+                                           pjsua_recorder_id *p_id);
+
+
+/**
+ * Get conference port associated with the file descriptor port.
+ *
+ * @param id		The file descriptor port (i.e. recorder) ID.
+ *
+ * @return		Conference port ID associated with this file descriptor port.
+ */
+PJ_DECL(pjsua_conf_port_id) pjsua_audio_fd_get_conf_port(pjsua_recorder_id id);
+
+
+/**
+ * Get the media port for the file descriptor port.
+ *
+ * @param id		The file descriptor port (i.e. recorder) ID.
+ * @param p_port	The media port associated with the file descriptor port.
+ *
+ * @return		PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjsua_audio_fd_get_port(pjsua_recorder_id id,
+					     pjmedia_port **p_port);
+
+
+/**
+ * Destroy file descriptor port.
+ *
+ * @param id		The file descriptor ID.
+ *
+ * @return		PJ_SUCCESS on success, or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pjsua_audio_fd_destroy(pjsua_recorder_id id);
+
+
+/*****************************************************************************
  * Sound devices.
  */
 
Index: pjsip/include/pjsua2/media.hpp
===================================================================
--- pjsip/include/pjsua2/media.hpp	(revision 4770)
+++ pjsip/include/pjsua2/media.hpp	(working copy)
@@ -444,6 +444,59 @@
     int	recorderId;
 };
 
+/**
+ * Audio File Descriptor I/O.
+ */
+class AudioMediaFD : public AudioMedia
+{
+public:
+    /**
+     * Constructor.
+     */
+    AudioMediaFD();
+
+	/**
+     * Create a file discriptor I/O, and automatically connect this port to
+     * the conference bridge.
+     *
+	 * @param fd_in		Descriptor open for reading (i.e. audio source)
+	 *             		or -1 to disable audio input.
+	 * @param fd_out		Descriptor open for writing (i.e. audio sink)
+	 *             		or -1 to disable audio output.
+     * @param options	 Optional options, which can be used to specify the
+     * 			 file descriptor types and behavidour.
+	 * 			 ORed pjmedia_file_descriptor_option flags:
+	 * 			  - PJMEDIA_FD_NONBLOCK (1) for non-blocking mode (UNIX only;
+	 * 			    O_NONBLOCK is fcntl'ed on the descriptors if specified).
+	 * 			  - PJMEDIA_FD_HANDLES (2): Parameters fd_in and fd_out are Windows
+	 * 			    file handles instead of integer file descriptors (Windows only).
+     */
+    void createFD(int fd_in = -1,
+			int fd_out = -1,
+			unsigned options = 0) throw(Error);
+
+    /**
+     * Typecast from base class AudioMedia. This is useful for application
+     * written in language that does not support downcasting such as Python.
+     *
+     * @param media		The object to be downcasted
+     *
+     * @return			The object as AudioMediaFD instance
+     */
+    static AudioMediaFD* typecastFromAudioMedia(AudioMedia *media);
+
+    /**
+     * Virtual destructor.
+     */
+    virtual ~AudioMediaFD();
+
+private:
+    /**
+     * File Descriptor I/O Id (i.e. shared with Recorder Id).
+     */
+    int	recorderId;
+};
+
 /*************************************************************************
 * Sound device management
 */
Index: pjsip/src/pjsua-lib/pjsua_aud.c
===================================================================
--- pjsip/src/pjsua-lib/pjsua_aud.c	(revision 4770)
+++ pjsip/src/pjsua-lib/pjsua_aud.c	(working copy)
@@ -1452,6 +1452,154 @@
 
 
 /*****************************************************************************
+ * File descriptor audio I/O.
+ */
+
+/*
+ * Create a file descriptor audio port, and automatically connect this port to
+ * the conference bridge.
+ * Warning! Shares ID space with recorders.
+ */
+PJ_DEF(pj_status_t) pjsua_audio_fd_create(int fd_in,
+                                          int fd_out,
+                                          unsigned flags,
+                                          pjsua_recorder_id *p_id)
+{
+    unsigned slot, rec_id;
+    pj_pool_t *pool = NULL;
+    pjmedia_port *port;
+	static const pj_str_t fd_name = {"fd-port", 7};
+    pj_status_t status = PJ_SUCCESS;
+
+    /* At least one FD must be present */
+    PJ_ASSERT_RETURN((fd_in >= 0) || (fd_out >= 0), PJ_EINVAL);
+
+    PJ_LOG(4,(THIS_FILE, "Creating file descriptor port for %s..",
+	      ((fd_in >= 0) && (fd_out >= 0) ? "RW" : ((fd_in >= 0) ? "R" : "W"))));
+    pj_log_push_indent();
+
+    if (pjsua_var.rec_cnt >= PJ_ARRAY_SIZE(pjsua_var.recorder)) {
+	pj_log_pop_indent();
+	return PJ_ETOOMANY;
+    }
+
+    PJSUA_LOCK();
+
+    for (rec_id=0; rec_id<PJ_ARRAY_SIZE(pjsua_var.recorder); ++rec_id) {
+	if (pjsua_var.recorder[rec_id].port == NULL)
+	    break;
+    }
+
+    if (rec_id == PJ_ARRAY_SIZE(pjsua_var.recorder)) {
+	/* This is unexpected */
+	pj_assert(0);
+	status = PJ_EBUG;
+	goto on_return;
+    }
+
+    pool = pjsua_pool_create("fd-port", 256, 16);
+    if (!pool) {
+	status = PJ_ENOMEM;
+	goto on_return;
+    }
+
+	status = pjmedia_fd_port_create(pool,
+						pjsua_var.media_cfg.clock_rate,
+						pjsua_var.mconf_cfg.channel_count,
+						pjsua_var.mconf_cfg.samples_per_frame,
+						pjsua_var.mconf_cfg.bits_per_sample,
+						fd_in,
+						fd_out,
+						flags,
+						&port);
+
+    if (status != PJ_SUCCESS) {
+	pjsua_perror(THIS_FILE, "Unable to create file descriptor port", status);
+	goto on_return;
+    }
+
+    status = pjmedia_conf_add_port(pjsua_var.mconf, pool,
+				   port, &fd_name, &slot);
+    if (status != PJ_SUCCESS) {
+	pjmedia_port_destroy(port);
+	goto on_return;
+    }
+
+    pjsua_var.recorder[rec_id].port = port;
+    pjsua_var.recorder[rec_id].slot = slot;
+    pjsua_var.recorder[rec_id].pool = pool;
+
+    if (p_id) *p_id = rec_id;
+
+    ++pjsua_var.rec_cnt;
+
+    PJSUA_UNLOCK();
+
+    PJ_LOG(4,(THIS_FILE, "File descriptor port created, id=%d, slot=%d", rec_id, slot));
+
+    pj_log_pop_indent();
+    return PJ_SUCCESS;
+
+on_return:
+    PJSUA_UNLOCK();
+    if (pool) pj_pool_release(pool);
+    pj_log_pop_indent();
+    return status;
+}
+
+
+/*
+ * Get conference port associated with a file descriptor port.
+ * Warning! Shares ID space with recorders.
+ */
+PJ_DEF(pjsua_conf_port_id) pjsua_audio_fd_get_conf_port(pjsua_recorder_id id)
+{
+    return pjsua_recorder_get_conf_port(id);
+}
+
+/*
+ * Get the media port for a file descriptor port.
+ * Warning! Shares ID space with recorders.
+ */
+PJ_DEF(pj_status_t) pjsua_audio_fd_get_port( pjsua_recorder_id id,
+					     pjmedia_port **p_port)
+{
+    return pjsua_recorder_get_port(id, p_port);
+}
+
+/*
+ * Destroy a file descriptor port.
+ * Warning! Shares ID space with recorders.
+ */
+PJ_DEF(pj_status_t) pjsua_audio_fd_destroy(pjsua_recorder_id id)
+{
+    PJ_ASSERT_RETURN(id>=0 && id<(int)PJ_ARRAY_SIZE(pjsua_var.recorder),
+		     PJ_EINVAL);
+    PJ_ASSERT_RETURN(pjsua_var.recorder[id].port != NULL, PJ_EINVAL);
+
+    PJ_LOG(4,(THIS_FILE, "Destroying file descriptor port (i.e. recorder) %d..", id));
+    pj_log_push_indent();
+
+    PJSUA_LOCK();
+
+    if (pjsua_var.recorder[id].port) {
+	pjsua_conf_remove_port(pjsua_var.recorder[id].slot);
+	pjmedia_port_destroy(pjsua_var.recorder[id].port);
+	pjsua_var.recorder[id].port = NULL;
+	pjsua_var.recorder[id].slot = 0xFFFF;
+	pj_pool_release(pjsua_var.recorder[id].pool);
+	pjsua_var.recorder[id].pool = NULL;
+	pjsua_var.rec_cnt--;
+    }
+
+    PJSUA_UNLOCK();
+    pj_log_pop_indent();
+
+    return PJ_SUCCESS;
+}
+
+
+/*****************************************************************************
  * Sound devices.
  */
 
Index: pjsip/src/pjsua2/media.cpp
===================================================================
--- pjsip/src/pjsua2/media.cpp	(revision 4770)
+++ pjsip/src/pjsua2/media.cpp	(working copy)
@@ -367,6 +367,47 @@
 }
 
 ///////////////////////////////////////////////////////////////////////////////
+AudioMediaFD::AudioMediaFD()
+: recorderId(PJSUA_INVALID_ID)
+{
+
+}
+
+AudioMediaFD::~AudioMediaFD()
+{
+    if (recorderId != PJSUA_INVALID_ID) {
+	unregisterMediaPort();
+	pjsua_audio_fd_destroy(recorderId);
+    }
+}
+
+void AudioMediaFD::createFD(int fd_in /* = -1 */,
+				        int fd_out /* = -1 */,
+				        unsigned options /* = 0 */)
+				        throw(Error)
+{
+    if (recorderId != PJSUA_INVALID_ID) {
+	PJSUA2_RAISE_ERROR(PJ_EEXISTS);
+    }
+
+    PJSUA2_CHECK_EXPR( pjsua_audio_fd_create(fd_in,
+					     fd_out,
+					     options,
+					     &recorderId) );
+
+    /* Get media port id. */
+    id = pjsua_audio_fd_get_conf_port(recorderId);
+
+    registerMediaPort(NULL);
+}
+
+AudioMediaFD* AudioMediaFD::typecastFromAudioMedia(
+						AudioMedia *media)
+{
+    return static_cast<AudioMediaFD*>(media);
+}
+
+///////////////////////////////////////////////////////////////////////////////
 void AudioDevInfo::fromPj(const pjmedia_aud_dev_info &dev_info)
 {
     name = dev_info.name;


[Index of Archives]     [Asterisk Users]     [Asterisk App Development]     [Linux ARM Kernel]     [Linux ARM]     [Linux Omap]     [Fedora ARM]     [IETF Annouce]     [Security]     [Bugtraq]     [Linux]     [Linux OMAP]     [Linux MIPS]     [Linux API]
  Powered by Linux