summaryrefslogtreecommitdiff
path: root/pjmedia
diff options
context:
space:
mode:
authorBenny Prijono <bennylp@teluu.com>2006-03-24 20:41:20 +0000
committerBenny Prijono <bennylp@teluu.com>2006-03-24 20:41:20 +0000
commita8d9b65484437c311f3464e6401da3ae8332c9b3 (patch)
treea40d9d9a6b058ef7bd7ae6e3e7689c260ae01611 /pjmedia
parenta25f969dc96839e685e59ac3b4efd54a00c20f8d (diff)
Added WAVE writer and resample port, and also found out why audio quality is poor with DirectSound
git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@358 74dad513-b988-da41-8d7b-12977e46ad98
Diffstat (limited to 'pjmedia')
-rw-r--r--pjmedia/build/Makefile6
-rw-r--r--pjmedia/build/pjmedia.dsp16
-rw-r--r--pjmedia/include/pjmedia.h1
-rw-r--r--pjmedia/include/pjmedia/config.h8
-rw-r--r--pjmedia/include/pjmedia/errno.h5
-rw-r--r--pjmedia/include/pjmedia/file_port.h44
-rw-r--r--pjmedia/include/pjmedia/resample.h70
-rw-r--r--pjmedia/include/pjmedia/wave.h27
-rw-r--r--pjmedia/src/pjmedia/conference.c80
-rw-r--r--pjmedia/src/pjmedia/errno.c1
-rw-r--r--pjmedia/src/pjmedia/file_player.c (renamed from pjmedia/src/pjmedia/file_port.c)86
-rw-r--r--pjmedia/src/pjmedia/file_reader.c448
-rw-r--r--pjmedia/src/pjmedia/file_writer.c306
-rw-r--r--pjmedia/src/pjmedia/pasound.c45
-rw-r--r--pjmedia/src/pjmedia/port.c24
-rw-r--r--pjmedia/src/pjmedia/resample.c12
-rw-r--r--pjmedia/src/pjmedia/resample_port.c185
-rw-r--r--pjmedia/src/pjmedia/sound_port.c3
-rw-r--r--pjmedia/src/pjmedia/wave.c57
19 files changed, 863 insertions, 561 deletions
diff --git a/pjmedia/build/Makefile b/pjmedia/build/Makefile
index 7cf6012f..54a4f59e 100644
--- a/pjmedia/build/Makefile
+++ b/pjmedia/build/Makefile
@@ -64,9 +64,9 @@ export _LDFLAGS := $(subst /,$(HOST_PSEP),$(PJMEDIA_LIB)) \
#
export PJMEDIA_SRCDIR = ../src/pjmedia
export PJMEDIA_OBJS += $(OS_OBJS) $(M_OBJS) $(CC_OBJS) $(HOST_OBJS) \
- codec.o conference.o endpoint.o errno.o file_port.o \
- g711.o jbuf.o null_port.o port.o resample.o rtcp.o \
- rtp.o sdp.o sdp_cmp.o sdp_neg.o session.o silencedet.o \
+ codec.o conference.o endpoint.o errno.o file_player.o \
+ g711.o jbuf.o null_port.o port.o resample.o resample_port.o \
+ rtcp.o rtp.o sdp.o sdp_cmp.o sdp_neg.o session.o silencedet.o \
sound_port.o stream.o $(SOUND_OBJS) $(NULLSOUND_OBJS)
export PJMEDIA_CFLAGS += $(_CFLAGS)
diff --git a/pjmedia/build/pjmedia.dsp b/pjmedia/build/pjmedia.dsp
index a84395a0..43d09f7c 100644
--- a/pjmedia/build/pjmedia.dsp
+++ b/pjmedia/build/pjmedia.dsp
@@ -65,7 +65,7 @@ LIB32=link.exe -lib
# PROP Intermediate_Dir ".\output\pjmedia-i386-win32-vc6-debug"
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c
-# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "../include" /I "../../pjlib/include" /I "../../pjlib-util/include" /I "../src/pjmedia/portaudio" /D "_DEBUG" /D "PA_NO_ASIO" /D "PA_NO_WMME" /D "WIN32" /D "_MBCS" /D "_LIB" /D PJ_WIN32=1 /D PJ_M_I386=1 /FR /FD /GZ /c
+# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "../include" /I "../../pjlib/include" /I "../../pjlib-util/include" /I "../src/pjmedia/portaudio" /D "_DEBUG" /D "PA_NO_ASIO" /D "PA_NO_DS" /D "WIN32" /D "_MBCS" /D "_LIB" /D PJ_WIN32=1 /D PJ_M_I386=1 /FR /FD /GZ /c
# SUBTRACT CPP /YX
# ADD BASE RSC /l 0x409 /d "_DEBUG"
# ADD RSC /l 0x409 /d "_DEBUG"
@@ -108,7 +108,11 @@ SOURCE=..\src\pjmedia\errno.c
# End Source File
# Begin Source File
-SOURCE=..\src\pjmedia\file_port.c
+SOURCE=..\src\pjmedia\file_player.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjmedia\file_writer.c
# End Source File
# Begin Source File
@@ -140,6 +144,10 @@ SOURCE=..\src\pjmedia\resample.c
# End Source File
# Begin Source File
+SOURCE=..\src\pjmedia\resample_port.c
+# End Source File
+# Begin Source File
+
SOURCE=..\src\pjmedia\rtcp.c
# End Source File
# Begin Source File
@@ -174,6 +182,10 @@ SOURCE=..\src\pjmedia\sound_port.c
SOURCE=..\src\pjmedia\stream.c
# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjmedia\wave.c
+# End Source File
# End Group
# Begin Group "Header Files"
diff --git a/pjmedia/include/pjmedia.h b/pjmedia/include/pjmedia.h
index 8cdc0b72..34264b61 100644
--- a/pjmedia/include/pjmedia.h
+++ b/pjmedia/include/pjmedia.h
@@ -34,6 +34,7 @@
#include <pjmedia/jbuf.h>
#include <pjmedia/null_port.h>
#include <pjmedia/port.h>
+#include <pjmedia/resample.h>
#include <pjmedia/rtcp.h>
#include <pjmedia/rtp.h>
#include <pjmedia/sdp.h>
diff --git a/pjmedia/include/pjmedia/config.h b/pjmedia/include/pjmedia/config.h
index c81693a5..8e111045 100644
--- a/pjmedia/include/pjmedia/config.h
+++ b/pjmedia/include/pjmedia/config.h
@@ -68,5 +68,13 @@
#endif
+/**
+ * Default file player/writer buffer size.
+ */
+#ifndef PJMEDIA_FILE_PORT_BUFSIZE
+# define PJMEDIA_FILE_PORT_BUFSIZE 4000
+#endif
+
+
#endif /* __PJMEDIA_CONFIG_H__ */
diff --git a/pjmedia/include/pjmedia/errno.h b/pjmedia/include/pjmedia/errno.h
index 67c4d011..59e665d8 100644
--- a/pjmedia/include/pjmedia/errno.h
+++ b/pjmedia/include/pjmedia/errno.h
@@ -440,6 +440,11 @@ PJ_BEGIN_DECL
* Wave file too short.
*/
#define PJMEDIA_EWAVETOOSHORT (PJMEDIA_ERRNO_START+182) /* 220182 */
+/**
+ * @hideinitializer
+ * Sound frame is too large for file buffer.
+ */
+#define PJMEDIA_EFRMFILETOOBIG (PJMEDIA_ERRNO_START+183) /* 220183 */
/************************************************************
diff --git a/pjmedia/include/pjmedia/file_port.h b/pjmedia/include/pjmedia/file_port.h
index 75482c70..363f94e0 100644
--- a/pjmedia/include/pjmedia/file_port.h
+++ b/pjmedia/include/pjmedia/file_port.h
@@ -26,11 +26,23 @@
#include <pjmedia/port.h>
+
PJ_BEGIN_DECL
/**
- * Create file player port.
+ * Create a media port to play streams from a WAV file.
+ *
+ * @param pool Pool to create memory buffers for this port.
+ * @param filename File name to open.
+ * @param flags Port creation flags.
+ * @param buf_size Buffer size to be allocated. If the value is 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 player port.
+ * @param p_port Pointer to receive the file port instance.
+ *
+ * @return PJ_SUCCESS on success.
*/
PJ_DECL(pj_status_t) pjmedia_file_player_port_create( pj_pool_t *pool,
const char *filename,
@@ -41,6 +53,36 @@ PJ_DECL(pj_status_t) pjmedia_file_player_port_create( pj_pool_t *pool,
+/**
+ * Create a media port to record streams to a WAV file. Note that the port
+ * must be closed properly (with #pjmedia_port_destroy()) so that the WAV
+ * header can be filled with correct values (such as the file length).
+ *
+ * @param pool Pool to create memory buffers for this port.
+ * @param filename File name.
+ * @param flags Port creation flags.
+ * @param buf_size Buffer size to be allocated. If the value is 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 writer port.
+ * @param p_port Pointer to receive the file port instance.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_file_writer_port_create( pj_pool_t *pool,
+ const char *filename,
+ unsigned sampling_rate,
+ unsigned channel_count,
+ unsigned samples_per_frame,
+ unsigned bits_per_sample,
+ unsigned flags,
+ pj_ssize_t buff_size,
+ void *user_data,
+ pjmedia_port **p_port );
+
+
+
+
PJ_END_DECL
diff --git a/pjmedia/include/pjmedia/resample.h b/pjmedia/include/pjmedia/resample.h
index 63b3351d..1def54ce 100644
--- a/pjmedia/include/pjmedia/resample.h
+++ b/pjmedia/include/pjmedia/resample.h
@@ -25,10 +25,25 @@
* @file reample.h
* @brief Sample rate converter.
*/
-#include "pjmedia/types.h"
+#include <pjmedia/types.h>
+#include <pjmedia/port.h>
+
PJ_BEGIN_DECL
+/*
+ * This file declares two types of API:
+ *
+ * Application can use #pjmedia_resample_create() and #pjmedia_resample_run()
+ * to convert a frame from source rate to destination rate. The inpuit frame
+ * must have a constant length.
+ *
+ * Alternatively, application can create a resampling port with
+ * #pjmedia_resample_port_create() and connect the port to other ports to
+ * change the sampling rate of the samples.
+ */
+
+
/**
* Opaque resample session.
*/
@@ -59,7 +74,9 @@ PJ_DECL(pj_status_t) pjmedia_resample_create(pj_pool_t *pool,
/**
- * Resample a frame.
+ * Use the resample session to resample a frame. The frame must have the
+ * same size and settings as the resample session, or otherwise the
+ * behavior is undefined.
*
* @param resample The resample session.
* @param input Buffer containing the input samples.
@@ -70,6 +87,55 @@ PJ_DECL(void) pjmedia_resample_run( pjmedia_resample *resample,
pj_int16_t *output );
+/**
+ * Get the input frame size of a resample session.
+ *
+ * @param resample The resample session.
+ *
+ * @return The frame size, in number of samples.
+ */
+PJ_DECL(unsigned) pjmedia_resample_get_input_size(pjmedia_resample *resample);
+
+
+/**
+ * Create a resample port. This creates a bidirectional resample session,
+ * which will resample frames when the port's get_frame() and put_frame()
+ * is called.
+ *
+ * When the resample port's get_frame() is called, this port will get
+ * a frame from the downstream port and resample the frame to the upstream
+ * port's clock rate before returning it to the caller.
+ *
+ * When the resample port's put_frame() is called, this port will resample
+ * the frame to the downstream's port clock rate before giving the frame
+ * to the downstream port.
+ *
+ * @param pool Pool to allocate the structure and buffers.
+ * @param high_quality If true, then high quality conversion will be
+ * used, at the expense of more CPU and memory,
+ * because temporary buffer needs to be created.
+ * @param large_filter If true, large filter size will be used.
+ * @param downstream_rate The sampling rate of the downstream port.
+ * @param upstream_rate The sampling rate of the upstream port.
+ * @param channel_count The number of channels. This argument is only
+ * used for the port information. It does not
+ * change the behavior of the resample port.
+ * @param samples_per_frame Number of samples per frame from the downstream
+ * port.
+ * @param p_port Pointer to receive the resample port instance.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_resample_port_create( pj_pool_t *pool,
+ pj_bool_t high_quality,
+ pj_bool_t large_filter,
+ unsigned downstream_rate,
+ unsigned upstream_rate,
+ unsigned channel_count,
+ unsigned samples_per_frame,
+ pjmedia_port **p_port );
+
+
PJ_END_DECL
#endif /* __PJMEDIA_RESAMPLE_H__ */
diff --git a/pjmedia/include/pjmedia/wave.h b/pjmedia/include/pjmedia/wave.h
index cd4c3e61..201f9760 100644
--- a/pjmedia/include/pjmedia/wave.h
+++ b/pjmedia/include/pjmedia/wave.h
@@ -32,6 +32,7 @@ PJ_BEGIN_DECL
#define PJMEDIA_RIFF_TAG ('F'<<24|'F'<<16|'I'<<8|'R')
#define PJMEDIA_WAVE_TAG ('E'<<24|'V'<<16|'A'<<8|'W')
#define PJMEDIA_FMT_TAG (' '<<24|'t'<<16|'m'<<8|'f')
+#define PJMEDIA_DATA_TAG ('a'<<24|'t'<<16|'a'<<8|'d')
/**
@@ -69,6 +70,32 @@ struct pjmedia_wave_hdr
typedef struct pjmedia_wave_hdr pjmedia_wave_hdr;
+/**
+ * On big-endian hosts, this function swaps the byte order of the values
+ * in the WAVE header fields. On little-endian hosts, this function does
+ * nothing.
+ *
+ * Application SHOULD call this function after reading the WAVE header
+ * chunks from a file.
+ *
+ * @param hdr The WAVE header.
+ */
+PJ_DECL(void) pjmedia_wave_hdr_file_to_host( pjmedia_wave_hdr *hdr );
+
+
+/**
+ * On big-endian hosts, this function swaps the byte order of the values
+ * in the WAVE header fields. On little-endian hosts, this function does
+ * nothing.
+ *
+ * Application SHOULD call this function before writing the WAVE header
+ * to a file.
+ *
+ * @param hdr The WAVE header.
+ */
+PJ_DECL(void) pjmedia_wave_hdr_host_to_file( pjmedia_wave_hdr *hdr );
+
+
PJ_END_DECL
#endif /* __PJMEDIA_WAVE_H__ */
diff --git a/pjmedia/src/pjmedia/conference.c b/pjmedia/src/pjmedia/conference.c
index fad562c7..dfbe567c 100644
--- a/pjmedia/src/pjmedia/conference.c
+++ b/pjmedia/src/pjmedia/conference.c
@@ -28,15 +28,29 @@
#include <pj/pool.h>
#include <pj/string.h>
+/* CONF_DEBUG enables detailed operation of the conference bridge.
+ * Beware that it prints large amounts of logs (several lines per frame).
+ */
//#define CONF_DEBUG
#ifdef CONF_DEBUG
# include <stdio.h>
-# define TRACE_(x) {printf x; fflush(stdout); }
+# define TRACE_(x) PJ_LOG(5,x)
#else
# define TRACE_(x)
#endif
+/* REC_FILE macro enables recording of the samples written to the sound
+ * device. The file contains RAW PCM data with no header, and has the
+ * same settings (clock rate etc) as the conference bridge.
+ * This should only be enabled when debugging audio quality *only*.
+ */
+//#define REC_FILE "confrec.pcm"
+#ifdef REC_FILE
+static FILE *fhnd_rec;
+#endif
+
+
#define THIS_FILE "conference.c"
#define RX_BUF_COUNT 8
@@ -966,6 +980,10 @@ static pj_status_t read_port( pjmedia_conf *conf,
pj_assert(count == conf->samples_per_frame);
+ TRACE_((THIS_FILE, "read_port %.*s: count=%d",
+ (int)cport->name.slen, cport->name.ptr,
+ count));
+
/* If port's samples per frame and sampling rate matches conference
* bridge's settings, get the frame directly from the port.
*/
@@ -976,6 +994,10 @@ static pj_status_t read_port( pjmedia_conf *conf,
f.buf = frame;
f.size = count * BYTES_PER_SAMPLE;
+ TRACE_((THIS_FILE, " get_frame %.*s: count=%d",
+ (int)cport->name.slen, cport->name.ptr,
+ count));
+
status = (cport->port->get_frame)(cport->port, &f);
*type = f.type;
@@ -997,6 +1019,9 @@ static pj_status_t read_port( pjmedia_conf *conf,
f.buf = cport->rx_buf + cport->rx_buf_count;
f.size = cport->samples_per_frame * BYTES_PER_SAMPLE;
+ TRACE_((THIS_FILE, " get_frame, count=%d",
+ cport->samples_per_frame));
+
status = pjmedia_port_get_frame(cport->port, &f);
if (status != PJ_SUCCESS) {
@@ -1005,12 +1030,16 @@ static pj_status_t read_port( pjmedia_conf *conf,
}
if (f.type != PJMEDIA_FRAME_TYPE_AUDIO) {
+ TRACE_((THIS_FILE, " get_frame returned non-audio"));
zero_samples( cport->rx_buf + cport->rx_buf_count,
cport->samples_per_frame);
}
cport->rx_buf_count += cport->samples_per_frame;
+ TRACE_((THIS_FILE, " rx buffer size is now %d",
+ cport->rx_buf_count));
+
pj_assert(cport->rx_buf_count <= cport->rx_buf_cap);
}
@@ -1022,6 +1051,9 @@ static pj_status_t read_port( pjmedia_conf *conf,
unsigned src_count;
+ TRACE_((THIS_FILE, " resample, input count=%d",
+ pjmedia_resample_get_input_size(cport->rx_resample)));
+
pjmedia_resample_run( cport->rx_resample,cport->rx_buf, frame);
src_count = (unsigned)(count * 1.0 * cport->clock_rate /
@@ -1032,6 +1064,9 @@ static pj_status_t read_port( pjmedia_conf *conf,
cport->rx_buf_count);
}
+ TRACE_((THIS_FILE, " rx buffer size is now %d",
+ cport->rx_buf_count));
+
} else {
copy_samples(frame, cport->rx_buf, count);
@@ -1067,8 +1102,9 @@ static pj_status_t write_port(pjmedia_conf *conf, struct conf_port *cport,
frame.buf = NULL;
frame.size = 0;
- if (cport->port && cport->port->put_frame)
+ if (cport->port && cport->port->put_frame) {
pjmedia_port_put_frame(cport->port, &frame);
+ }
cport->tx_level = 0;
return PJ_SUCCESS;
@@ -1111,11 +1147,15 @@ static pj_status_t write_port(pjmedia_conf *conf, struct conf_port *cport,
buf[j] = (pj_int16_t) itemp;
}
- } else {
+ } else if (cport->sources) {
/* No need to adjust signal level. */
for (j=0; j<conf->samples_per_frame; ++j) {
buf[j] = unsigned2pcm(cport->mix_buf[j] / cport->sources);
}
+ } else {
+ // Not necessarry. Buffer has been zeroed before.
+ // zero_samples(buf, conf->samples_per_frame);
+ pj_assert(buf[0] == 0);
}
/* Calculate TX level if we need to do so.
@@ -1154,6 +1194,10 @@ static pj_status_t write_port(pjmedia_conf *conf, struct conf_port *cport,
frame.size = conf->samples_per_frame * BYTES_PER_SAMPLE;
frame.timestamp.u64 = timestamp;
+ TRACE_((THIS_FILE, "put_frame %.*s, count=%d",
+ (int)cport->name.slen, cport->name.ptr,
+ frame.size / BYTES_PER_SAMPLE));
+
return pjmedia_port_put_frame(cport->port, &frame);
} else
return PJ_SUCCESS;
@@ -1185,6 +1229,10 @@ static pj_status_t write_port(pjmedia_conf *conf, struct conf_port *cport,
pj_status_t status;
+ TRACE_((THIS_FILE, "write_port %.*s: count=%d",
+ (int)cport->name.slen, cport->name.ptr,
+ cport->samples_per_frame));
+
if (cport->port) {
pjmedia_frame frame;
@@ -1193,6 +1241,10 @@ static pj_status_t write_port(pjmedia_conf *conf, struct conf_port *cport,
frame.size = cport->samples_per_frame * BYTES_PER_SAMPLE;
frame.timestamp.u64 = timestamp;
+ TRACE_((THIS_FILE, "put_frame %.*s, count=%d",
+ (int)cport->name.slen, cport->name.ptr,
+ frame.size / BYTES_PER_SAMPLE));
+
status = pjmedia_port_put_frame(cport->port, &frame);
} else
@@ -1205,6 +1257,9 @@ static pj_status_t write_port(pjmedia_conf *conf, struct conf_port *cport,
cport->tx_buf_count);
}
+ TRACE_((THIS_FILE, " tx_buf count now is %d",
+ cport->tx_buf_count));
+
return status;
}
@@ -1221,6 +1276,8 @@ static pj_status_t get_frame(pjmedia_port *this_port,
pjmedia_conf *conf = this_port->user_data;
unsigned ci, cj, i, j;
+ TRACE_((THIS_FILE, "- clock -"));
+
/* Check that correct size is specified. */
pj_assert(frame->size == conf->samples_per_frame *
conf->bits_per_sample / 8);
@@ -1228,8 +1285,6 @@ static pj_status_t get_frame(pjmedia_port *this_port,
/* Must lock mutex (must we??) */
pj_mutex_lock(conf->mutex);
- TRACE_(("p"));
-
/* Zero all port's temporary buffers. */
for (i=0, ci=0; i<conf->max_ports && ci < conf->port_cnt; ++i) {
struct conf_port *conf_port = conf->ports[i];
@@ -1244,8 +1299,7 @@ static pj_status_t get_frame(pjmedia_port *this_port,
conf_port->sources = 0;
mix_buf = conf_port->mix_buf;
- for (j=0; j<conf->samples_per_frame; ++j)
- mix_buf[j] = 0;
+ for (j=0; j<conf->samples_per_frame; ++j) mix_buf[j] = 0;
}
/* Get frames from all ports, and "mix" the signal
@@ -1421,6 +1475,9 @@ static pj_status_t get_frame(pjmedia_port *this_port,
/* Return sound playback frame. */
if (conf->ports[0]->sources) {
+ TRACE_((THIS_FILE, "write to audio, count=%d",
+ conf->samples_per_frame));
+
copy_samples( frame->buf, (pj_int16_t*)conf->ports[0]->mix_buf,
conf->samples_per_frame);
} else {
@@ -1432,6 +1489,13 @@ static pj_status_t get_frame(pjmedia_port *this_port,
pj_mutex_unlock(conf->mutex);
+#ifdef REC_FILE
+ if (fhnd_rec == NULL)
+ fhnd_rec = fopen(REC_FILE, "wb");
+ if (fhnd_rec)
+ fwrite(frame->buf, frame->size, 1, fhnd_rec);
+#endif
+
return PJ_SUCCESS;
}
@@ -1448,8 +1512,6 @@ static pj_status_t put_frame(pjmedia_port *this_port,
pj_int16_t *target_snd_buf;
unsigned i;
- TRACE_(("r"));
-
/* Check for correct size. */
PJ_ASSERT_RETURN( frame->size == conf->samples_per_frame *
conf->bits_per_sample / 8,
diff --git a/pjmedia/src/pjmedia/errno.c b/pjmedia/src/pjmedia/errno.c
index 86c78d5f..e5759c1d 100644
--- a/pjmedia/src/pjmedia/errno.c
+++ b/pjmedia/src/pjmedia/errno.c
@@ -122,6 +122,7 @@ static const struct
{ PJMEDIA_ENOTVALIDWAVE, "Not a valid WAVE file" },
{ PJMEDIA_EWAVEUNSUPP, "Unsupported WAVE file format" },
{ PJMEDIA_EWAVETOOSHORT, "WAVE file too short" },
+ { PJMEDIA_EFRMFILETOOBIG, "Sound frame too large for file buffer"},
/* Sound device errors: */
{ PJMEDIA_ENOSNDREC, "No suitable sound capture device" },
diff --git a/pjmedia/src/pjmedia/file_port.c b/pjmedia/src/pjmedia/file_player.c
index f1b2e819..ca5a3e1e 100644
--- a/pjmedia/src/pjmedia/file_port.c
+++ b/pjmedia/src/pjmedia/file_player.c
@@ -30,12 +30,9 @@
#define THIS_FILE "file_port.c"
-#ifndef PJMEDIA_FILE_PORT_BUFSIZE
-# define PJMEDIA_FILE_PORT_BUFSIZE 4000
-#endif
-
+#define SIGNATURE ('F'<<24|'P'<<16|'L'<<8|'Y')
+#define BYTES_PER_SAMPLE 2
-#define SIGNATURE ('F'<<24|'I'<<16|'L'<<8|'E')
#if 1
# define TRACE_(x) PJ_LOG(4,x)
@@ -44,37 +41,14 @@
#endif
#if defined(PJ_IS_BIG_ENDIAN) && PJ_IS_BIG_ENDIAN!=0
- PJ_INLINE(pj_int16_t) swap16(pj_int16_t val)
- {
- pj_uint8_t *p = (pj_uint8_t*)&val;
- pj_uint8_t tmp = *p;
- *p = *(p+1);
- *(p+1) = tmp;
- return val;
- }
- PJ_INLINE(pj_int32_t) swap32(pj_int32_t val)
- {
- pj_uint8_t *p = (pj_uint8_t*)&val;
- pj_uint8_t tmp = *p;
- *p = *(p+3);
- *(p+3) = tmp;
- tmp = *(p+1);
- *(p+1) = *(p+2);
- *(p+2) = tmp;
- return val;
- }
-# define SWAP16(val16) swap16(val16)
-# define SWAP32(val32) swap32(val32)
static void samples_to_host(pj_int16_t *samples, unsigned count)
{
unsigned i;
for (i=0; i<count; ++i) {
- samples[i] = SWAP16(samples[i]);
+ samples[i] = pj_swap16(samples[i]);
}
}
#else
-# define SWAP16(val16) (val16)
-# define SWAP32(val32) (val32)
# define samples_to_host(samples,count)
#endif
@@ -170,43 +144,13 @@ static pj_status_t fill_buffer(struct file_port *fport)
}
/* Convert samples to host rep */
- samples_to_host((pj_int16_t*)fport->buf, fport->bufsize/2);
+ samples_to_host((pj_int16_t*)fport->buf, fport->bufsize/BYTES_PER_SAMPLE);
return PJ_SUCCESS;
}
/*
- * Change the endianness of WAVE header fields.
- */
-void pjmedia_wave_hdr_swap_bytes( pjmedia_wave_hdr *hdr )
-{
- hdr->riff_hdr.riff = SWAP32(hdr->riff_hdr.riff);
- hdr->riff_hdr.file_len = SWAP32(hdr->riff_hdr.file_len);
- hdr->riff_hdr.wave = SWAP32(hdr->riff_hdr.wave);
-
- hdr->fmt_hdr.fmt = SWAP32(hdr->fmt_hdr.fmt);
- hdr->fmt_hdr.len = SWAP32(hdr->fmt_hdr.len);
- hdr->fmt_hdr.fmt_tag = SWAP16(hdr->fmt_hdr.fmt_tag);
- hdr->fmt_hdr.nchan = SWAP16(hdr->fmt_hdr.nchan);
- hdr->fmt_hdr.sample_rate = SWAP32(hdr->fmt_hdr.sample_rate);
- hdr->fmt_hdr.bytes_per_sec = SWAP32(hdr->fmt_hdr.bytes_per_sec);
- hdr->fmt_hdr.block_align = SWAP16(hdr->fmt_hdr.block_align);
- hdr->fmt_hdr.bits_per_sample = SWAP16(hdr->fmt_hdr.bits_per_sample);
-
- hdr->data_hdr.data = SWAP32(hdr->data_hdr.data);
- hdr->data_hdr.len = SWAP32(hdr->data_hdr.len);
-}
-
-
-#if defined(PJ_IS_BIG_ENDIAN) && PJ_IS_BIG_ENDIAN!=0
-# define normalize_wave_hdr(hdr) pjmedia_wave_hdr_swap_bytes(hdr)
-#else
-# define normalize_wave_hdr(hdr)
-#endif
-
-
-/*
* Create WAVE player port.
*/
PJ_DEF(pj_status_t) pjmedia_file_player_port_create( pj_pool_t *pool,
@@ -223,7 +167,6 @@ PJ_DEF(pj_status_t) pjmedia_file_player_port_create( pj_pool_t *pool,
PJ_UNUSED_ARG(flags);
- PJ_UNUSED_ARG(buff_size);
/* Check arguments. */
PJ_ASSERT_RETURN(pool && filename && p_port, PJ_EINVAL);
@@ -265,8 +208,10 @@ PJ_DEF(pj_status_t) pjmedia_file_player_port_create( pj_pool_t *pool,
return PJMEDIA_ENOTVALIDWAVE;
}
- /* Normalize WAVE header fields value (only used in big-endian hosts) */
- normalize_wave_hdr(&wave_hdr);
+ /* Normalize WAVE header fields values from little-endian to host
+ * byte order.
+ */
+ pjmedia_wave_hdr_file_to_host(&wave_hdr);
/* Validate WAVE file. */
if (wave_hdr.riff_hdr.riff != PJMEDIA_RIFF_TAG ||
@@ -291,7 +236,7 @@ PJ_DEF(pj_status_t) pjmedia_file_player_port_create( pj_pool_t *pool,
}
/* Block align must be 2*nchannels */
- if (wave_hdr.fmt_hdr.block_align != wave_hdr.fmt_hdr.nchan*2) {
+ if (wave_hdr.fmt_hdr.block_align != wave_hdr.fmt_hdr.nchan*BYTES_PER_SAMPLE) {
pj_file_close(fport->fd);
return PJMEDIA_EWAVEUNSUPP;
}
@@ -326,7 +271,8 @@ PJ_DEF(pj_status_t) pjmedia_file_player_port_create( pj_pool_t *pool,
/* Create file buffer.
*/
- fport->bufsize = PJMEDIA_FILE_PORT_BUFSIZE;
+ if (buff_size < 1) buff_size = PJMEDIA_FILE_PORT_BUFSIZE;
+ fport->bufsize = buff_size;
/* Create buffer. */
@@ -354,11 +300,12 @@ PJ_DEF(pj_status_t) pjmedia_file_player_port_create( pj_pool_t *pool,
PJ_LOG(4,(THIS_FILE,
- "File port '%.*s' created: clock=%dKHz, bufsize=%uKB, "
+ "File player '%.*s' created: samp.rate=%d, ch=%d, bufsize=%uKB, "
"filesize=%luKB",
(int)fport->base.info.name.slen,
fport->base.info.name.ptr,
- fport->base.info.sample_rate/1000,
+ fport->base.info.sample_rate,
+ fport->base.info.channel_count,
fport->bufsize / 1000,
(unsigned long)(fport->fsize / 1000)));
@@ -389,8 +336,9 @@ static pj_status_t file_get_frame(pjmedia_port *this_port,
pj_assert(fport->base.info.signature == SIGNATURE);
- frame_size = fport->base.info.bytes_per_frame;
- pj_assert(frame->size == frame_size);
+ //frame_size = fport->base.info.bytes_per_frame;
+ //pj_assert(frame->size == frame_size);
+ frame_size = frame->size;
/* Copy frame from buffer. */
frame->type = PJMEDIA_FRAME_TYPE_AUDIO;
diff --git a/pjmedia/src/pjmedia/file_reader.c b/pjmedia/src/pjmedia/file_reader.c
deleted file mode 100644
index f1b2e819..00000000
--- a/pjmedia/src/pjmedia/file_reader.c
+++ /dev/null
@@ -1,448 +0,0 @@
-/* $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/file_port.h>
-#include <pjmedia/errno.h>
-#include <pjmedia/wave.h>
-#include <pj/assert.h>
-#include <pj/file_access.h>
-#include <pj/file_io.h>
-#include <pj/log.h>
-#include <pj/pool.h>
-#include <pj/string.h>
-
-
-#define THIS_FILE "file_port.c"
-
-
-#ifndef PJMEDIA_FILE_PORT_BUFSIZE
-# define PJMEDIA_FILE_PORT_BUFSIZE 4000
-#endif
-
-
-#define SIGNATURE ('F'<<24|'I'<<16|'L'<<8|'E')
-
-#if 1
-# define TRACE_(x) PJ_LOG(4,x)
-#else
-# define TRACE_(x)
-#endif
-
-#if defined(PJ_IS_BIG_ENDIAN) && PJ_IS_BIG_ENDIAN!=0
- PJ_INLINE(pj_int16_t) swap16(pj_int16_t val)
- {
- pj_uint8_t *p = (pj_uint8_t*)&val;
- pj_uint8_t tmp = *p;
- *p = *(p+1);
- *(p+1) = tmp;
- return val;
- }
- PJ_INLINE(pj_int32_t) swap32(pj_int32_t val)
- {
- pj_uint8_t *p = (pj_uint8_t*)&val;
- pj_uint8_t tmp = *p;
- *p = *(p+3);
- *(p+3) = tmp;
- tmp = *(p+1);
- *(p+1) = *(p+2);
- *(p+2) = tmp;
- return val;
- }
-# define SWAP16(val16) swap16(val16)
-# define SWAP32(val32) swap32(val32)
- static void samples_to_host(pj_int16_t *samples, unsigned count)
- {
- unsigned i;
- for (i=0; i<count; ++i) {
- samples[i] = SWAP16(samples[i]);
- }
- }
-#else
-# define SWAP16(val16) (val16)
-# define SWAP32(val32) (val32)
-# define samples_to_host(samples,count)
-#endif
-
-struct file_port
-{
- pjmedia_port base;
- pj_size_t bufsize;
- char *buf;
- char *readpos;
-
- pj_off_t fsize;
- pj_off_t fpos;
- pj_oshandle_t fd;
-
-};
-
-
-static pj_status_t file_put_frame(pjmedia_port *this_port,
- const pjmedia_frame *frame);
-static pj_status_t file_get_frame(pjmedia_port *this_port,
- pjmedia_frame *frame);
-static pj_status_t file_on_destroy(pjmedia_port *this_port);
-
-static struct file_port *create_file_port(pj_pool_t *pool)
-{
- struct file_port *port;
-
- port = pj_pool_zalloc(pool, sizeof(struct file_port));
- if (!port)
- return NULL;
-
- port->base.info.name = pj_str("file");
- 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 = &file_put_frame;
- port->base.get_frame = &file_get_frame;
- port->base.on_destroy = &file_on_destroy;
-
-
- /* Put in default values.
- * These will be overriden once the file is read.
- */
- port->base.info.sample_rate = 8000;
- port->base.info.bits_per_sample = 16;
- port->base.info.samples_per_frame = 160;
- port->base.info.bytes_per_frame = 320;
-
- return port;
-}
-
-/*
- * Fill buffer.
- */
-static pj_status_t fill_buffer(struct file_port *fport)
-{
- pj_ssize_t size_left = fport->bufsize;
- unsigned size_to_read;
- pj_ssize_t size;
- pj_status_t status;
-
- while (size_left > 0) {
-
- /* Calculate how many bytes to read in this run. */
- size = size_to_read = size_left;
- status = pj_file_read(fport->fd,
- &fport->buf[fport->bufsize-size_left],
- &size);
- if (status != PJ_SUCCESS)
- return status;
- if (size < 0) {
- /* Should return more appropriate error code here.. */
- return PJ_ECANCELLED;
- }
-
- size_left -= size;
- fport->fpos += size;
-
- /* If size is less than size_to_read, it indicates that we've
- * encountered EOF. Rewind the file.
- */
- if (size < (pj_ssize_t)size_to_read) {
- PJ_LOG(5,(THIS_FILE, "File port %.*s EOF, rewinding..",
- (int)fport->base.info.name.slen,
- fport->base.info.name.ptr));
- fport->fpos = sizeof(struct pjmedia_wave_hdr);
- pj_file_setpos( fport->fd, fport->fpos, PJ_SEEK_SET);
- }
- }
-
- /* Convert samples to host rep */
- samples_to_host((pj_int16_t*)fport->buf, fport->bufsize/2);
-
- return PJ_SUCCESS;
-}
-
-
-/*
- * Change the endianness of WAVE header fields.
- */
-void pjmedia_wave_hdr_swap_bytes( pjmedia_wave_hdr *hdr )
-{
- hdr->riff_hdr.riff = SWAP32(hdr->riff_hdr.riff);
- hdr->riff_hdr.file_len = SWAP32(hdr->riff_hdr.file_len);
- hdr->riff_hdr.wave = SWAP32(hdr->riff_hdr.wave);
-
- hdr->fmt_hdr.fmt = SWAP32(hdr->fmt_hdr.fmt);
- hdr->fmt_hdr.len = SWAP32(hdr->fmt_hdr.len);
- hdr->fmt_hdr.fmt_tag = SWAP16(hdr->fmt_hdr.fmt_tag);
- hdr->fmt_hdr.nchan = SWAP16(hdr->fmt_hdr.nchan);
- hdr->fmt_hdr.sample_rate = SWAP32(hdr->fmt_hdr.sample_rate);
- hdr->fmt_hdr.bytes_per_sec = SWAP32(hdr->fmt_hdr.bytes_per_sec);
- hdr->fmt_hdr.block_align = SWAP16(hdr->fmt_hdr.block_align);
- hdr->fmt_hdr.bits_per_sample = SWAP16(hdr->fmt_hdr.bits_per_sample);
-
- hdr->data_hdr.data = SWAP32(hdr->data_hdr.data);
- hdr->data_hdr.len = SWAP32(hdr->data_hdr.len);
-}
-
-
-#if defined(PJ_IS_BIG_ENDIAN) && PJ_IS_BIG_ENDIAN!=0
-# define normalize_wave_hdr(hdr) pjmedia_wave_hdr_swap_bytes(hdr)
-#else
-# define normalize_wave_hdr(hdr)
-#endif
-
-
-/*
- * Create WAVE player port.
- */
-PJ_DEF(pj_status_t) pjmedia_file_player_port_create( pj_pool_t *pool,
- const char *filename,
- unsigned flags,
- pj_ssize_t buff_size,
- void *user_data,
- pjmedia_port **p_port )
-{
- pjmedia_wave_hdr wave_hdr;
- pj_ssize_t size_read;
- struct file_port *fport;
- pj_status_t status;
-
-
- PJ_UNUSED_ARG(flags);
- PJ_UNUSED_ARG(buff_size);
-
- /* Check arguments. */
- PJ_ASSERT_RETURN(pool && filename && p_port, PJ_EINVAL);
-
- /* Check the file really exists. */
- if (!pj_file_exists(filename)) {
- return PJ_ENOTFOUND;
- }
-
- /* Create fport instance. */
- fport = create_file_port(pool);
- if (!fport) {
- return PJ_ENOMEM;
- }
-
-
- /* Get the file size. */
- fport->fsize = pj_file_size(filename);
-
- /* Size must be more than WAVE header size */
- if (fport->fsize <= sizeof(pjmedia_wave_hdr)) {
- return PJMEDIA_ENOTVALIDWAVE;
- }
-
- /* Open file. */
- status = pj_file_open( pool, filename, PJ_O_RDONLY, &fport->fd);
- if (status != PJ_SUCCESS)
- return status;
-
- /* Read the WAVE header. */
- size_read = sizeof(wave_hdr);
- status = pj_file_read( fport->fd, &wave_hdr, &size_read);
- if (status != PJ_SUCCESS) {
- pj_file_close(fport->fd);
- return status;
- }
- if (size_read != sizeof(wave_hdr)) {
- pj_file_close(fport->fd);
- return PJMEDIA_ENOTVALIDWAVE;
- }
-
- /* Normalize WAVE header fields value (only used in big-endian hosts) */
- normalize_wave_hdr(&wave_hdr);
-
- /* Validate WAVE file. */
- if (wave_hdr.riff_hdr.riff != PJMEDIA_RIFF_TAG ||
- wave_hdr.riff_hdr.wave != PJMEDIA_WAVE_TAG ||
- wave_hdr.fmt_hdr.fmt != PJMEDIA_FMT_TAG)
- {
- pj_file_close(fport->fd);
- TRACE_((THIS_FILE,
- "actual value|expected riff=%x|%x, wave=%x|%x fmt=%x|%x",
- wave_hdr.riff_hdr.riff, PJMEDIA_RIFF_TAG,
- wave_hdr.riff_hdr.wave, PJMEDIA_WAVE_TAG,
- wave_hdr.fmt_hdr.fmt, PJMEDIA_FMT_TAG));
- return PJMEDIA_ENOTVALIDWAVE;
- }
-
- /* 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*2) {
- pj_file_close(fport->fd);
- return PJMEDIA_EWAVEUNSUPP;
- }
-
- /* Validate length. */
- if (wave_hdr.data_hdr.len != fport->fsize-sizeof(pjmedia_wave_hdr)) {
- pj_file_close(fport->fd);
- return PJMEDIA_EWAVEUNSUPP;
- }
- if (wave_hdr.data_hdr.len < 400) {
- pj_file_close(fport->fd);
- return PJMEDIA_EWAVETOOSHORT;
- }
-
- /* It seems like we have a valid WAVE file. */
-
- /* Initialize */
- fport->base.user_data = user_data;
-
- /* Update port info. */
- fport->base.info.channel_count = wave_hdr.fmt_hdr.nchan;
- fport->base.info.sample_rate = wave_hdr.fmt_hdr.sample_rate;
- fport->base.info.bits_per_sample = wave_hdr.fmt_hdr.bits_per_sample;
- fport->base.info.samples_per_frame = fport->base.info.sample_rate *
- wave_hdr.fmt_hdr.nchan *
- 20 / 1000;
- fport->base.info.bytes_per_frame =
- fport->base.info.samples_per_frame *
- fport->base.info.bits_per_sample / 8;
-
- pj_strdup2(pool, &fport->base.info.name, filename);
-
- /* Create file buffer.
- */
- fport->bufsize = PJMEDIA_FILE_PORT_BUFSIZE;
-
-
- /* Create buffer. */
- fport->buf = pj_pool_alloc(pool, fport->bufsize);
- if (!fport->buf) {
- pj_file_close(fport->fd);
- return PJ_ENOMEM;
- }
-
- fport->readpos = fport->buf;
-
- /* Set initial position of the file. */
- fport->fpos = sizeof(struct pjmedia_wave_hdr);
-
- /* Fill up the buffer. */
- status = fill_buffer(fport);
- if (status != PJ_SUCCESS) {
- pj_file_close(fport->fd);
- return status;
- }
-
- /* Done. */
-
- *p_port = &fport->base;
-
-
- PJ_LOG(4,(THIS_FILE,
- "File port '%.*s' created: clock=%dKHz, bufsize=%uKB, "
- "filesize=%luKB",
- (int)fport->base.info.name.slen,
- fport->base.info.name.ptr,
- fport->base.info.sample_rate/1000,
- fport->bufsize / 1000,
- (unsigned long)(fport->fsize / 1000)));
-
- return PJ_SUCCESS;
-}
-
-
-/*
- * Put frame to file.
- */
-static pj_status_t file_put_frame(pjmedia_port *this_port,
- const pjmedia_frame *frame)
-{
- PJ_UNUSED_ARG(this_port);
- PJ_UNUSED_ARG(frame);
- return PJ_EINVALIDOP;
-}
-
-/*
- * Get frame from file.
- */
-static pj_status_t file_get_frame(pjmedia_port *this_port,
- pjmedia_frame *frame)
-{
- struct file_port *fport = (struct file_port*)this_port;
- unsigned frame_size;
- pj_status_t status;
-
- pj_assert(fport->base.info.signature == SIGNATURE);
-
- frame_size = fport->base.info.bytes_per_frame;
- pj_assert(frame->size == frame_size);
-
- /* Copy frame from buffer. */
- frame->type = PJMEDIA_FRAME_TYPE_AUDIO;
- frame->size = frame_size;
- frame->timestamp.u64 = 0;
-
- if (fport->readpos + frame_size <= fport->buf + fport->bufsize) {
-
- /* Read contiguous buffer. */
- pj_memcpy(frame->buf, fport->readpos, frame_size);
-
- /* Fill up the buffer if all has been read. */
- fport->readpos += frame_size;
- if (fport->readpos == fport->buf + fport->bufsize) {
- fport->readpos = fport->buf;
-
- status = fill_buffer(fport);
- if (status != PJ_SUCCESS)
- return status;
- }
- } else {
- unsigned endread;
-
- /* Split read.
- * First stage: read until end of buffer.
- */
- endread = (fport->buf+fport->bufsize) - fport->readpos;
- pj_memcpy(frame->buf, fport->readpos, endread);
-
- /* Second stage: fill up buffer, and read from the start of buffer. */
- status = fill_buffer(fport);
- if (status != PJ_SUCCESS) {
- pj_memset(((char*)frame->buf)+endread, 0, frame_size-endread);
- return status;
- }
-
- pj_memcpy(((char*)frame->buf)+endread, fport->buf, frame_size-endread);
- fport->readpos = fport->buf + (frame_size - endread);
- }
-
- return PJ_SUCCESS;
-}
-
-/*
- * Destroy port.
- */
-static pj_status_t file_on_destroy(pjmedia_port *this_port)
-{
- struct file_port *fport = (struct file_port*) this_port;
-
- pj_assert(this_port->info.signature == SIGNATURE);
-
- pj_file_close(fport->fd);
- return PJ_SUCCESS;
-}
diff --git a/pjmedia/src/pjmedia/file_writer.c b/pjmedia/src/pjmedia/file_writer.c
new file mode 100644
index 00000000..8471ba48
--- /dev/null
+++ b/pjmedia/src/pjmedia/file_writer.c
@@ -0,0 +1,306 @@
+/* $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/file_port.h>
+#include <pjmedia/errno.h>
+#include <pjmedia/wave.h>
+#include <pj/assert.h>
+#include <pj/file_access.h>
+#include <pj/file_io.h>
+#include <pj/log.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+
+
+#define THIS_FILE "file_writer.c"
+#define SIGNATURE ('F'<<24|'W'<<16|'R'<<8|'T')
+#define BYTES_PER_SAMPLE 2
+
+
+struct file_port
+{
+ pjmedia_port base;
+ pj_size_t bufsize;
+ char *buf;
+ char *writepos;
+
+ pj_oshandle_t fd;
+};
+
+static pj_status_t file_put_frame(pjmedia_port *this_port,
+ const pjmedia_frame *frame);
+static pj_status_t file_get_frame(pjmedia_port *this_port,
+ pjmedia_frame *frame);
+static pj_status_t file_on_destroy(pjmedia_port *this_port);
+
+
+/*
+ * Create file writer port.
+ */
+PJ_DEF(pj_status_t) pjmedia_file_writer_port_create( pj_pool_t *pool,
+ const char *filename,
+ unsigned sampling_rate,
+ unsigned channel_count,
+ unsigned samples_per_frame,
+ unsigned bits_per_sample,
+ unsigned flags,
+ pj_ssize_t buff_size,
+ void *user_data,
+ pjmedia_port **p_port )
+{
+ struct file_port *fport;
+ pjmedia_wave_hdr wave_hdr;
+ pj_ssize_t size;
+ pj_status_t status;
+
+ PJ_UNUSED_ARG(flags);
+ PJ_UNUSED_ARG(user_data);
+
+ /* Check arguments. */
+ PJ_ASSERT_RETURN(pool && filename && p_port, PJ_EINVAL);
+
+ /* Only supports 16bits per sample for now.
+ * See flush_buffer().
+ */
+ PJ_ASSERT_RETURN(bits_per_sample == 16, PJ_EINVAL);
+
+ /* Create file port instance. */
+ fport = pj_pool_zalloc(pool, sizeof(struct file_port));
+ PJ_ASSERT_RETURN(fport != NULL, PJ_ENOMEM);
+
+ /* Initialize port info. */
+ fport->base.info.bits_per_sample = bits_per_sample;
+ fport->base.info.bytes_per_frame = samples_per_frame * bits_per_sample *
+ channel_count / 8;
+ fport->base.info.channel_count = channel_count;
+ fport->base.info.encoding_name = pj_str("pom");
+ fport->base.info.has_info = 1;
+ pj_strdup2(pool, &fport->base.info.name, filename);
+ fport->base.info.need_info = 0;
+ fport->base.info.pt = 0xFF;
+ fport->base.info.sample_rate = sampling_rate;
+ fport->base.info.samples_per_frame = samples_per_frame;
+ fport->base.info.signature = SIGNATURE;
+ fport->base.info.type = PJMEDIA_TYPE_AUDIO;
+
+ fport->base.get_frame = &file_get_frame;
+ fport->base.put_frame = &file_put_frame;
+ fport->base.on_destroy = &file_on_destroy;
+
+
+ /* Open file in write and read mode.
+ * We need the read mode because we'll modify the WAVE header once
+ * the recording has completed.
+ */
+ status = pj_file_open(pool, filename, PJ_O_WRONLY, &fport->fd);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ /* Initialize WAVE header */
+ pj_memset(&wave_hdr, 0, sizeof(pjmedia_wave_hdr));
+ wave_hdr.riff_hdr.riff = PJMEDIA_RIFF_TAG;
+ wave_hdr.riff_hdr.file_len = 0; /* will be filled later */
+ wave_hdr.riff_hdr.wave = PJMEDIA_WAVE_TAG;
+
+ 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.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;
+
+ wave_hdr.data_hdr.data = PJMEDIA_DATA_TAG;
+ wave_hdr.data_hdr.len = 0; /* will be filled later */
+
+
+ /* Convert WAVE header from host byte order to little endian
+ * before writing the header.
+ */
+ pjmedia_wave_hdr_host_to_file(&wave_hdr);
+
+
+ /* 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;
+ }
+
+ /* Set buffer size. */
+ if (buff_size < 1) buff_size = PJMEDIA_FILE_PORT_BUFSIZE;
+ fport->bufsize = buff_size;
+
+ /* Check that buffer size is greater than bytes per frame */
+ pj_assert(fport->bufsize >= fport->base.info.bytes_per_frame);
+
+
+ /* Allocate buffer and set initial write position */
+ fport->buf = pj_pool_alloc(pool, fport->bufsize);
+ if (fport->buf == NULL) {
+ pj_file_close(fport->fd);
+ return PJ_ENOMEM;
+ }
+ fport->writepos = fport->buf;
+
+ /* Done. */
+ *p_port = &fport->base;
+
+ PJ_LOG(4,(THIS_FILE,
+ "File writer '%.*s' created: samp.rate=%d, bufsize=%uKB",
+ (int)fport->base.info.name.slen,
+ fport->base.info.name.ptr,
+ fport->base.info.sample_rate,
+ fport->bufsize / 1000));
+
+
+ return PJ_SUCCESS;
+}
+
+
+#if defined(PJ_IS_BIG_ENDIAN) && PJ_IS_BIG_ENDIAN!=0
+ static void swap_samples(pj_int16_t *samples, unsigned count)
+ {
+ unsigned i;
+ for (i=0; i<count; ++i) {
+ samples[i] = pj_swap16(samples[i]);
+ }
+ }
+#else
+# define swap_samples(samples,count)
+#endif
+
+/*
+ * Flush the contents of the buffer to the file.
+ */
+static pj_status_t flush_buffer(struct file_port *fport)
+{
+ pj_ssize_t bytes = fport->writepos - fport->buf;
+ pj_status_t status;
+
+ /* Convert samples to little endian */
+ swap_samples((pj_int16_t*)fport->buf, bytes/BYTES_PER_SAMPLE);
+
+ /* Write to file. */
+ status = pj_file_write(fport->fd, fport->buf, &bytes);
+
+ /* Reset writepos */
+ fport->writepos = fport->buf;
+
+ return status;
+}
+
+/*
+ * Put a frame into the buffer. When the buffer is full, flush the buffer
+ * to the file.
+ */
+static pj_status_t file_put_frame(pjmedia_port *this_port,
+ const pjmedia_frame *frame)
+{
+ struct file_port *fport = (struct file_port *)this_port;
+
+ /* Flush buffer if we don't have enough room for the frame. */
+ if (fport->writepos + frame->size > fport->buf + fport->bufsize) {
+ pj_status_t status;
+ status = flush_buffer(fport);
+ if (status != PJ_SUCCESS)
+ return status;
+ }
+
+ /* Check if frame is not too large. */
+ 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;
+
+ return PJ_SUCCESS;
+}
+
+/*
+ * Get frame, basicy is a no-op operation.
+ */
+static pj_status_t file_get_frame(pjmedia_port *this_port,
+ pjmedia_frame *frame)
+{
+ PJ_UNUSED_ARG(this_port);
+ PJ_UNUSED_ARG(frame);
+ return PJ_EINVALIDOP;
+}
+
+/*
+ * Close the port, modify file header with updated file length.
+ */
+static pj_status_t file_on_destroy(pjmedia_port *this_port)
+{
+ enum { FILE_LEN_POS = 4, DATA_LEN_POS = 40 };
+ struct file_port *fport = (struct file_port *)this_port;
+ pj_off_t file_size;
+ pj_ssize_t bytes;
+ pj_uint32_t wave_file_len;
+ pj_uint32_t wave_data_len;
+ pj_status_t status;
+
+ /* Flush remaining buffers. */
+ if (fport->writepos != fport->buf)
+ flush_buffer(fport);
+
+ /* Get file size. */
+ status = pj_file_getpos(fport->fd, &file_size);
+ PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
+
+ /* Calculate wave fields */
+ wave_file_len = (pj_uint32_t)(file_size - 8);
+ wave_data_len = (pj_uint32_t)(file_size - sizeof(pjmedia_wave_hdr));
+
+#if defined(PJ_IS_BIG_ENDIAN) && PJ_IS_BIG_ENDIAN!=0
+ wave_file_len = pj_swap32(wave_file_len);
+ wave_data_len = pj_swap32(wave_data_len);
+#endif
+
+ /* Seek to the file_len field. */
+ status = pj_file_setpos(fport->fd, FILE_LEN_POS, PJ_SEEK_SET);
+ PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
+
+ /* Write file_len */
+ bytes = sizeof(wave_file_len);
+ status = pj_file_write(fport->fd, &wave_file_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);
+ PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
+
+ /* Write file_len */
+ bytes = sizeof(wave_data_len);
+ status = pj_file_write(fport->fd, &wave_data_len, &bytes);
+ PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
+
+ /* Close file */
+ status = pj_file_close(fport->fd);
+ PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
+
+ /* Done. */
+ return PJ_SUCCESS;
+}
+
diff --git a/pjmedia/src/pjmedia/pasound.c b/pjmedia/src/pjmedia/pasound.c
index 44bac29b..769a1c14 100644
--- a/pjmedia/src/pjmedia/pasound.c
+++ b/pjmedia/src/pjmedia/pasound.c
@@ -131,9 +131,9 @@ static int PaPlayerCallback( const void *input,
PJ_LOG(5,(THIS_FILE, "Player thread started"));
}
- if (statusFlags & paInputUnderflow)
+ if (statusFlags & paOutputUnderflow)
++stream->underflow;
- if (statusFlags & paInputOverflow)
+ if (statusFlags & paOutputOverflow)
++stream->overflow;
stream->timestamp += frameCount;
@@ -239,6 +239,7 @@ PJ_DEF(pj_status_t) pjmedia_snd_open_rec( int index,
PaStreamParameters inputParam;
int sampleFormat;
const PaDeviceInfo *paDevInfo = NULL;
+ const PaHostApiInfo *paHostApiInfo = NULL;
unsigned paFrames;
PaError err;
@@ -291,6 +292,8 @@ PJ_DEF(pj_status_t) pjmedia_snd_open_rec( int index,
inputParam.sampleFormat = sampleFormat;
inputParam.suggestedLatency = paDevInfo->defaultLowInputLatency;
+ paHostApiInfo = Pa_GetHostApiInfo(paDevInfo->hostApi);
+
/* Frames in PortAudio is number of samples in a single channel */
paFrames = samples_per_frame / channel_count;
@@ -302,11 +305,12 @@ PJ_DEF(pj_status_t) pjmedia_snd_open_rec( int index,
return PJMEDIA_ERRNO_FROM_PORTAUDIO(err);
}
- PJ_LOG(5,(THIS_FILE, "%s opening device %s for recording, sample rate=%d, "
- "channel count=%d, "
- "%d bits per sample, %d samples per buffer",
+ PJ_LOG(5,(THIS_FILE, "%s opening device %s (%s) for recording, sample "
+ "rate=%d, ch=%d, "
+ "bits=%d, %d samples per frame",
(err==0 ? "Success" : "Error"),
- paDevInfo->name, clock_rate, channel_count,
+ paDevInfo->name, paHostApiInfo->name,
+ clock_rate, channel_count,
bits_per_sample, samples_per_frame));
*p_snd_strm = stream;
@@ -328,6 +332,7 @@ PJ_DEF(pj_status_t) pjmedia_snd_open_player( int index,
PaStreamParameters outputParam;
int sampleFormat;
const PaDeviceInfo *paDevInfo = NULL;
+ const PaHostApiInfo *paHostApiInfo = NULL;
unsigned paFrames;
PaError err;
@@ -380,6 +385,8 @@ PJ_DEF(pj_status_t) pjmedia_snd_open_player( int index,
outputParam.sampleFormat = sampleFormat;
outputParam.suggestedLatency = paDevInfo->defaultLowInputLatency;
+ paHostApiInfo = Pa_GetHostApiInfo(paDevInfo->hostApi);
+
/* Frames in PortAudio is number of samples in a single channel */
paFrames = samples_per_frame / channel_count;
@@ -391,11 +398,12 @@ PJ_DEF(pj_status_t) pjmedia_snd_open_player( int index,
return PJMEDIA_ERRNO_FROM_PORTAUDIO(err);
}
- PJ_LOG(5,(THIS_FILE, "%s opening device %s for playing, sample rate=%d, "
- "channel count=%d, "
- "%d bits per sample, %d samples per frame",
+ PJ_LOG(5,(THIS_FILE, "%s opening device %s(%s) for playing, sample rate=%d"
+ ", ch=%d, "
+ "bits=%d, %d samples per frame",
(err==0 ? "Success" : "Error"),
- paDevInfo->name, clock_rate, channel_count,
+ paDevInfo->name, paHostApiInfo->name,
+ clock_rate, channel_count,
bits_per_sample, samples_per_frame));
*p_snd_strm = stream;
@@ -425,6 +433,8 @@ PJ_DEF(pj_status_t) pjmedia_snd_open( int rec_id,
int sampleFormat;
const PaDeviceInfo *paRecDevInfo = NULL;
const PaDeviceInfo *paPlayDevInfo = NULL;
+ const PaHostApiInfo *paRecHostApiInfo = NULL;
+ const PaHostApiInfo *paPlayHostApiInfo = NULL;
unsigned paFrames;
PaError err;
@@ -497,12 +507,16 @@ PJ_DEF(pj_status_t) pjmedia_snd_open( int rec_id,
inputParam.sampleFormat = sampleFormat;
inputParam.suggestedLatency = paRecDevInfo->defaultLowInputLatency;
+ paRecHostApiInfo = Pa_GetHostApiInfo(paRecDevInfo->hostApi);
+
pj_memset(&outputParam, 0, sizeof(outputParam));
outputParam.device = play_id;
outputParam.channelCount = channel_count;
outputParam.hostApiSpecificStreamInfo = NULL;
outputParam.sampleFormat = sampleFormat;
- outputParam.suggestedLatency = paPlayDevInfo->defaultLowInputLatency;
+ outputParam.suggestedLatency = paPlayDevInfo->defaultLowOutputLatency;
+
+ paPlayHostApiInfo = Pa_GetHostApiInfo(paPlayDevInfo->hostApi);
/* Frames in PortAudio is number of samples in a single channel */
paFrames = samples_per_frame / channel_count;
@@ -515,11 +529,12 @@ PJ_DEF(pj_status_t) pjmedia_snd_open( int rec_id,
return PJMEDIA_ERRNO_FROM_PORTAUDIO(err);
}
- PJ_LOG(5,(THIS_FILE, "%s opening device %s/%s for recording and playback, "
- "sample rate=%d, channel count=%d, "
- "%d bits per sample, %d samples per buffer",
+ PJ_LOG(5,(THIS_FILE, "%s opening device %s(%s)/%s(%s) for recording and "
+ "playback, sample rate=%d, ch=%d, "
+ "bits=%d, %d samples per frame",
(err==0 ? "Success" : "Error"),
- paRecDevInfo->name, paPlayDevInfo->name,
+ paRecDevInfo->name, paRecHostApiInfo->name,
+ paPlayDevInfo->name, paPlayHostApiInfo->name,
clock_rate, channel_count,
bits_per_sample, samples_per_frame));
diff --git a/pjmedia/src/pjmedia/port.c b/pjmedia/src/pjmedia/port.c
index d283b975..50a008e9 100644
--- a/pjmedia/src/pjmedia/port.c
+++ b/pjmedia/src/pjmedia/port.c
@@ -35,6 +35,7 @@ PJ_DEF(pj_status_t) pjmedia_port_connect( pj_pool_t *pool,
PJ_ASSERT_RETURN(pool && upstream_port && downstream_port, PJ_EINVAL);
+#if 0
/* They both MUST have the same media type. */
PJ_ASSERT_RETURN(upstream_port->info.type ==
downstream_port->info.type, PJMEDIA_ENCTYPE);
@@ -57,17 +58,22 @@ PJ_DEF(pj_status_t) pjmedia_port_connect( pj_pool_t *pool,
PJ_ASSERT_RETURN(upstream_port->info.bytes_per_frame ==
downstream_port->info.bytes_per_frame,
PJMEDIA_ENCBYTES);
+#endif
/* Create mutual attachment. */
- status = upstream_port->on_downstream_connect( pool, upstream_port,
- downstream_port );
- if (status != PJ_SUCCESS)
- return status;
-
- status = downstream_port->on_upstream_connect( pool, downstream_port,
- upstream_port );
- if (status != PJ_SUCCESS)
- return status;
+ if (upstream_port->on_downstream_connect) {
+ status = upstream_port->on_downstream_connect( pool, upstream_port,
+ downstream_port );
+ if (status != PJ_SUCCESS)
+ return status;
+ }
+
+ if (downstream_port->on_upstream_connect) {
+ status = downstream_port->on_upstream_connect( pool, downstream_port,
+ upstream_port );
+ if (status != PJ_SUCCESS)
+ return status;
+ }
/* Save the attachment. */
upstream_port->downstream_port = downstream_port;
diff --git a/pjmedia/src/pjmedia/resample.c b/pjmedia/src/pjmedia/resample.c
index 9045a02c..95cf44ca 100644
--- a/pjmedia/src/pjmedia/resample.c
+++ b/pjmedia/src/pjmedia/resample.c
@@ -498,7 +498,7 @@ PJ_DEF(pj_status_t) pjmedia_resample_create( pj_pool_t *pool,
* to yield the same performance.
*/
if (rate_out < rate_in) {
- high_quality = 0;
+ //high_quality = 0;
}
#if !defined(PJMEDIA_HAS_LARGE_FILTER) || PJMEDIA_HAS_LARGE_FILTER==0
@@ -579,8 +579,7 @@ PJ_DEF(void) pjmedia_resample_run( pjmedia_resample *resample,
* 0 xoff 2*xoff size+2*xoff
*/
dst_buf = resample->buffer + resample->xoff*2;
- for (i=0; i<resample->frame_size; ++i)
- dst_buf[i] = input[i];
+ for (i=0; i<resample->frame_size; ++i) dst_buf[i] = input[i];
if (resample->factor >= 1) {
@@ -632,3 +631,10 @@ PJ_DEF(void) pjmedia_resample_run( pjmedia_resample *resample,
SrcLinear( input, output, resample->factor, resample->frame_size);
}
}
+
+PJ_DEF(unsigned) pjmedia_resample_get_input_size(pjmedia_resample *resample)
+{
+ PJ_ASSERT_RETURN(resample != NULL, 0);
+ return resample->frame_size;
+}
+
diff --git a/pjmedia/src/pjmedia/resample_port.c b/pjmedia/src/pjmedia/resample_port.c
new file mode 100644
index 00000000..d8ec7087
--- /dev/null
+++ b/pjmedia/src/pjmedia/resample_port.c
@@ -0,0 +1,185 @@
+/* $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/resample.h>
+#include <pjmedia/errno.h>
+#include <pj/assert.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+
+
+#define BYTES_PER_SAMPLE 2
+
+struct resample_port
+{
+ pjmedia_port base;
+ pjmedia_resample *resample_get;
+ pjmedia_resample *resample_put;
+ pj_int16_t *get_buf;
+ pj_int16_t *put_buf;
+ unsigned downstream_frame_size;
+ unsigned upstream_frame_size;
+};
+
+
+
+static pj_status_t resample_put_frame(pjmedia_port *this_port,
+ const pjmedia_frame *frame);
+static pj_status_t resample_get_frame(pjmedia_port *this_port,
+ pjmedia_frame *frame);
+
+
+
+PJ_DEF(pj_status_t) pjmedia_resample_port_create( pj_pool_t *pool,
+ pj_bool_t high_quality,
+ pj_bool_t large_filter,
+ unsigned downstream_rate,
+ unsigned upstream_rate,
+ unsigned channel_count,
+ unsigned samples_per_frame,
+ pjmedia_port **p_port )
+{
+ struct resample_port *rport;
+ unsigned upstream_samples_per_frame;
+ pj_status_t status;
+
+ PJ_ASSERT_RETURN(pool && p_port, PJ_EINVAL);
+
+ upstream_samples_per_frame = (unsigned)(samples_per_frame * 1.0 *
+ upstream_rate / downstream_rate);
+
+ /* Create and initialize port. */
+ rport = pj_pool_zalloc(pool, sizeof(struct resample_port));
+ PJ_ASSERT_RETURN(rport != NULL, PJ_ENOMEM);
+
+ rport->base.info.bits_per_sample = 16;
+ rport->base.info.bytes_per_frame = samples_per_frame * BYTES_PER_SAMPLE;
+ rport->base.info.channel_count = channel_count;
+ rport->base.info.encoding_name = pj_str("pcm");
+ rport->base.info.has_info = 1;
+ rport->base.info.name = pj_str("resample");
+ rport->base.info.need_info = 0;
+ rport->base.info.pt = 0xFF;
+ rport->base.info.sample_rate = upstream_rate;
+ rport->base.info.samples_per_frame = upstream_samples_per_frame;
+ rport->base.info.signature = 0;
+ rport->base.info.type = PJMEDIA_TYPE_AUDIO;
+
+ rport->downstream_frame_size = samples_per_frame;
+ rport->upstream_frame_size = upstream_samples_per_frame;
+
+ /* Create buffers.
+ * We need separate buffer for get_frame() and put_frame() since
+ * both functions may run simultaneously.
+ */
+ rport->get_buf = pj_pool_alloc(pool, samples_per_frame * BYTES_PER_SAMPLE);
+ PJ_ASSERT_RETURN(rport->get_buf, PJ_ENOMEM);
+
+ rport->put_buf = pj_pool_alloc(pool, samples_per_frame * BYTES_PER_SAMPLE);
+ PJ_ASSERT_RETURN(rport->put_buf, PJ_ENOMEM);
+
+
+ /* Create "get_frame" resample */
+ status = pjmedia_resample_create( pool, high_quality, large_filter,
+ downstream_rate, upstream_rate,
+ samples_per_frame, &rport->resample_get);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ /* Create "put_frame" resample */
+ status = pjmedia_resample_create( pool, high_quality, large_filter,
+ upstream_rate, downstream_rate,
+ upstream_samples_per_frame,
+ &rport->resample_put);
+
+ /* Set get_frame and put_frame interface */
+ rport->base.get_frame = &resample_get_frame;
+ rport->base.put_frame = &resample_put_frame;
+
+
+ /* Done */
+ *p_port = &rport->base;
+
+ return PJ_SUCCESS;
+}
+
+
+
+static pj_status_t resample_put_frame(pjmedia_port *this_port,
+ const pjmedia_frame *frame)
+{
+ struct resample_port *rport = (struct resample_port*) this_port;
+ pjmedia_frame downstream_frame;
+
+ /* Return if we don't have downstream port. */
+ if (this_port->downstream_port == NULL) {
+ return PJ_SUCCESS;
+ }
+
+ if (frame->type == PJMEDIA_FRAME_TYPE_AUDIO) {
+ pjmedia_resample_run( rport->resample_put, frame->buf, rport->put_buf);
+
+ downstream_frame.buf = rport->put_buf;
+ downstream_frame.size = rport->downstream_frame_size *
+ BYTES_PER_SAMPLE;
+ } else {
+ downstream_frame.buf = frame->buf;
+ downstream_frame.size = frame->size;
+ }
+
+ downstream_frame.type = frame->type;
+ downstream_frame.timestamp.u64 = frame->timestamp.u64;
+
+ return pjmedia_port_put_frame( this_port->downstream_port,
+ &downstream_frame );
+}
+
+
+
+static pj_status_t resample_get_frame(pjmedia_port *this_port,
+ pjmedia_frame *frame)
+{
+ struct resample_port *rport = (struct resample_port*) this_port;
+ pjmedia_frame downstream_frame;
+ pj_status_t status;
+
+ /* Return silence if we don't have downstream port */
+ if (this_port->downstream_port == NULL) {
+ pj_memset(frame->buf, frame->size, 0);
+ return PJ_SUCCESS;
+ }
+
+ downstream_frame.buf = rport->get_buf;
+ downstream_frame.size = rport->downstream_frame_size * BYTES_PER_SAMPLE;
+ downstream_frame.timestamp.u64 = frame->timestamp.u64;
+ downstream_frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
+
+ status = pjmedia_port_get_frame( this_port->downstream_port,
+ &downstream_frame);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ pjmedia_resample_run( rport->resample_get, rport->get_buf, frame->buf);
+
+ frame->size = rport->upstream_frame_size * BYTES_PER_SAMPLE;
+ frame->type = PJMEDIA_FRAME_TYPE_AUDIO;
+
+ return PJ_SUCCESS;
+}
+
+
diff --git a/pjmedia/src/pjmedia/sound_port.c b/pjmedia/src/pjmedia/sound_port.c
index aca74e16..b4e66845 100644
--- a/pjmedia/src/pjmedia/sound_port.c
+++ b/pjmedia/src/pjmedia/sound_port.c
@@ -70,6 +70,9 @@ static pj_status_t play_cb(/* in */ void *user_data,
if (frame.type != PJMEDIA_FRAME_TYPE_AUDIO)
goto no_frame;
+ /* Must supply the required samples */
+ pj_assert(frame.size == size);
+
return PJ_SUCCESS;
no_frame:
diff --git a/pjmedia/src/pjmedia/wave.c b/pjmedia/src/pjmedia/wave.c
new file mode 100644
index 00000000..29aafd5f
--- /dev/null
+++ b/pjmedia/src/pjmedia/wave.c
@@ -0,0 +1,57 @@
+/* $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/wave.h>
+
+/*
+ * Change the endianness of WAVE header fields.
+ */
+static void wave_hdr_swap_bytes( pjmedia_wave_hdr *hdr )
+{
+#if defined(PJ_IS_BIG_ENDIAN) && PJ_IS_BIG_ENDIAN!=0
+ hdr->riff_hdr.riff = pj_swap32(hdr->riff_hdr.riff);
+ hdr->riff_hdr.file_len = pj_swap32(hdr->riff_hdr.file_len);
+ hdr->riff_hdr.wave = pj_swap32(hdr->riff_hdr.wave);
+
+ hdr->fmt_hdr.fmt = pj_swap32(hdr->fmt_hdr.fmt);
+ hdr->fmt_hdr.len = pj_swap32(hdr->fmt_hdr.len);
+ hdr->fmt_hdr.fmt_tag = pj_swap16(hdr->fmt_hdr.fmt_tag);
+ hdr->fmt_hdr.nchan = pj_swap16(hdr->fmt_hdr.nchan);
+ hdr->fmt_hdr.sample_rate = pj_swap32(hdr->fmt_hdr.sample_rate);
+ hdr->fmt_hdr.bytes_per_sec = pj_swap32(hdr->fmt_hdr.bytes_per_sec);
+ hdr->fmt_hdr.block_align = pj_swap16(hdr->fmt_hdr.block_align);
+ hdr->fmt_hdr.bits_per_sample = pj_swap16(hdr->fmt_hdr.bits_per_sample);
+
+ hdr->data_hdr.data = pj_swap32(hdr->data_hdr.data);
+ hdr->data_hdr.len = pj_swap32(hdr->data_hdr.len);
+#else
+ PJ_UNUSED_ARG(hdr);
+#endif
+}
+
+
+PJ_DEF(void) pjmedia_wave_hdr_file_to_host( pjmedia_wave_hdr *hdr )
+{
+ wave_hdr_swap_bytes(hdr);
+}
+
+PJ_DEF(void) pjmedia_wave_hdr_host_to_file( pjmedia_wave_hdr *hdr )
+{
+ wave_hdr_swap_bytes(hdr);
+}
+