Added WAVE writer and resample port, and also found out why audio quality is poor with DirectSound
git-svn-id: https://svn.pjsip.org/repos/pjproject/trunk@358 74dad513-b988-da41-8d7b-12977e46ad98
This commit is contained in:
parent
a6642d37ef
commit
f808329783
|
@ -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)
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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__ */
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
||||
|
||||
/************************************************************
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -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__ */
|
||||
|
|
|
@ -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__ */
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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" },
|
||||
|
|
|
@ -30,13 +30,10 @@
|
|||
#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)
|
||||
#else
|
||||
|
@ -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,42 +144,12 @@ 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.
|
||||
*/
|
||||
|
@ -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;
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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));
|
||||
|
||||
|
|
|
@ -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. */
|
||||
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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
@ -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:
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue