From 6d68baecdefbc8b90749dc7cff8def2a5a88af30 Mon Sep 17 00:00:00 2001 From: Benny Prijono Date: Thu, 23 Feb 2006 02:09:10 +0000 Subject: Added support for playing WAV file git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@222 74dad513-b988-da41-8d7b-12977e46ad98 --- pjmedia/build/pjmedia.dsp | 12 ++ pjmedia/include/pjmedia.h | 2 + pjmedia/include/pjmedia/conference.h | 23 +++ pjmedia/include/pjmedia/errno.h | 19 +++ pjmedia/include/pjmedia/file_port.h | 47 +++++++ pjmedia/include/pjmedia/vad.h | 2 +- pjmedia/include/pjmedia/wave.h | 80 +++++++++++ pjmedia/src/pjmedia/conference.c | 22 +++ pjmedia/src/pjmedia/errno.c | 5 + pjmedia/src/pjmedia/file_port.c | 261 +++++++++++++++++++++++++++++++++++ 10 files changed, 472 insertions(+), 1 deletion(-) create mode 100644 pjmedia/include/pjmedia/file_port.h create mode 100644 pjmedia/include/pjmedia/wave.h create mode 100644 pjmedia/src/pjmedia/file_port.c (limited to 'pjmedia') diff --git a/pjmedia/build/pjmedia.dsp b/pjmedia/build/pjmedia.dsp index 97f4b5ca..963a85b8 100644 --- a/pjmedia/build/pjmedia.dsp +++ b/pjmedia/build/pjmedia.dsp @@ -108,6 +108,10 @@ SOURCE=..\src\pjmedia\errno.c # End Source File # Begin Source File +SOURCE=..\src\pjmedia\file_port.c +# End Source File +# Begin Source File + SOURCE=..\src\pjmedia\g711.c # End Source File # Begin Source File @@ -193,6 +197,10 @@ SOURCE=..\include\pjmedia\errno.h # End Source File # Begin Source File +SOURCE=..\include\pjmedia\file_port.h +# End Source File +# Begin Source File + SOURCE=..\include\pjmedia\jbuf.h # End Source File # Begin Source File @@ -243,6 +251,10 @@ SOURCE=..\include\pjmedia\types.h SOURCE=..\include\pjmedia\vad.h # End Source File +# Begin Source File + +SOURCE=..\include\pjmedia\wave.h +# End Source File # End Group # Begin Group "PortAudio" diff --git a/pjmedia/include/pjmedia.h b/pjmedia/include/pjmedia.h index 66ba4e0c..b59ca2ab 100644 --- a/pjmedia/include/pjmedia.h +++ b/pjmedia/include/pjmedia.h @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -37,6 +38,7 @@ #include #include #include +#include #endif /* __PJMEDIA_H__ */ diff --git a/pjmedia/include/pjmedia/conference.h b/pjmedia/include/pjmedia/conference.h index ffa44371..775e2ff7 100644 --- a/pjmedia/include/pjmedia/conference.h +++ b/pjmedia/include/pjmedia/conference.h @@ -40,6 +40,7 @@ typedef struct pjmedia_conf pjmedia_conf; */ typedef struct pjmedia_conf_port_info { + unsigned slot; pj_str_t name; pjmedia_port_op tx_setting; pjmedia_port_op rx_setting; @@ -150,12 +151,34 @@ PJ_DECL(pj_status_t) pjmedia_conf_remove_port( pjmedia_conf *conf, /** * Get port info. + * + * @param conf The conference bridge. + * @param slot Port index. + * @param info Pointer to receive the info. + * + * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_conf_get_port_info( pjmedia_conf *conf, unsigned slot, pjmedia_conf_port_info *info); +/** + * Get occupied ports info. + * + * @param conf The conference bridge. + * @param size On input, contains maximum number of infos + * to be retrieved. On output, contains the actual + * number of infos that have been copied. + * @param info Array of info. + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) pjmedia_conf_get_ports_info(pjmedia_conf *conf, + unsigned *size, + pjmedia_conf_port_info info[]); + + PJ_END_DECL diff --git a/pjmedia/include/pjmedia/errno.h b/pjmedia/include/pjmedia/errno.h index accf3fd1..354383b2 100644 --- a/pjmedia/include/pjmedia/errno.h +++ b/pjmedia/include/pjmedia/errno.h @@ -406,6 +406,25 @@ PJ_BEGIN_DECL #define PJMEDIA_ENCBYTES (PJMEDIA_ERRNO_START+165) /* 220165 */ +/************************************************************ + * FILE ERRORS + ***********************************************************/ +/** + * @hideinitializer + * Not a valid WAVE file. + */ +#define PJMEDIA_ENOTVALIDWAVE (PJMEDIA_ERRNO_START+180) /* 220180 */ +/** + * @hideinitializer + * Unsupported WAVE file. + */ +#define PJMEDIA_EWAVEUNSUPP (PJMEDIA_ERRNO_START+181) /* 220181 */ +/** + * @hideinitializer + * Wave file too short. + */ +#define PJMEDIA_EWAVETOOSHORT (PJMEDIA_ERRNO_START+182) /* 220182 */ + PJ_END_DECL diff --git a/pjmedia/include/pjmedia/file_port.h b/pjmedia/include/pjmedia/file_port.h new file mode 100644 index 00000000..75482c70 --- /dev/null +++ b/pjmedia/include/pjmedia/file_port.h @@ -0,0 +1,47 @@ +/* $Id$ */ +/* + * Copyright (C) 2003-2006 Benny Prijono + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __PJMEDIA_FILE_PORT_H__ +#define __PJMEDIA_FILE_PORT_H__ + +/** + * @file file_port.h + * @brief File player and recorder. + */ +#include + + +PJ_BEGIN_DECL + + +/** + * Create file player port. + */ +PJ_DECL(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 ); + + + +PJ_END_DECL + + +#endif /* __PJMEDIA_FILE_PORT_H__ */ diff --git a/pjmedia/include/pjmedia/vad.h b/pjmedia/include/pjmedia/vad.h index a7da42d2..fc7f3b2f 100644 --- a/pjmedia/include/pjmedia/vad.h +++ b/pjmedia/include/pjmedia/vad.h @@ -22,7 +22,7 @@ /** * @file vad.h - * @brief Voice Activity Detector. + * @brief Simple, adaptive silence detector. */ #include diff --git a/pjmedia/include/pjmedia/wave.h b/pjmedia/include/pjmedia/wave.h new file mode 100644 index 00000000..b42206fa --- /dev/null +++ b/pjmedia/include/pjmedia/wave.h @@ -0,0 +1,80 @@ +/* $Id$ */ +/* + * Copyright (C) 2003-2006 Benny Prijono + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __PJMEDIA_WAVE_H__ +#define __PJMEDIA_WAVE_H__ + + +/** + * @file wave.h + * @brief WAVE file manipulation. + */ +#include + + +PJ_BEGIN_DECL + +#if PJ_IS_BIG_ENDIAN +# define PJMEDIA_RIFF_TAG ('R'<<24|'I'<<16|'F'<<8|'F') +# define PJMEDIA_WAVE_TAG ('W'<<24|'A'<<16|'V'<<8|'E') +# define PJMEDIA_FMT_TAG ('f'<<24|'m'<<16|'t'<<8|' ') +#else +# 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') +#endif + + +/** + * This file describes the simpler/canonical version of a WAVE file. + * It does not support the full RIFF format specification. + */ +struct pjmedia_wave_hdr +{ + struct { + pj_uint32_t riff; + pj_uint32_t file_len; + pj_uint32_t wave; + } riff_hdr; + + struct { + pj_uint32_t fmt; + pj_uint32_t len; + pj_uint16_t fmt_tag; + pj_uint16_t nchan; + pj_uint32_t sample_rate; + pj_uint32_t bytes_per_sec; + pj_uint16_t block_align; + pj_uint16_t bits_per_sample; + } fmt_hdr; + + struct { + pj_uint32_t data; + pj_uint32_t len; + } data_hdr; +}; + +/** + * @see pjmedia_wave_hdr + */ +typedef struct pjmedia_wave_hdr pjmedia_wave_hdr; + + +PJ_END_DECL + +#endif /* __PJMEDIA_WAVE_H__ */ diff --git a/pjmedia/src/pjmedia/conference.c b/pjmedia/src/pjmedia/conference.c index 538dfa5e..8d2d28dc 100644 --- a/pjmedia/src/pjmedia/conference.c +++ b/pjmedia/src/pjmedia/conference.c @@ -599,6 +599,7 @@ PJ_DEF(pj_status_t) pjmedia_conf_get_port_info( pjmedia_conf *conf, conf_port = conf->ports[slot]; + info->slot = slot; info->name = conf_port->name; info->tx_setting = conf_port->tx_setting; info->rx_setting = conf_port->rx_setting; @@ -608,6 +609,27 @@ PJ_DEF(pj_status_t) pjmedia_conf_get_port_info( pjmedia_conf *conf, } +PJ_DEF(pj_status_t) pjmedia_conf_get_ports_info(pjmedia_conf *conf, + unsigned *size, + pjmedia_conf_port_info info[]) +{ + unsigned i, count=0; + + PJ_ASSERT_RETURN(conf && size && info, PJ_EINVAL); + + for (i=0; imax_ports && count<*size; ++i) { + if (!conf->ports[i]) + continue; + + pjmedia_conf_get_port_info(conf, i, &info[count]); + ++count; + } + + *size = count; + return PJ_SUCCESS; +} + + /* Convert signed 16bit pcm sample to unsigned 16bit sample */ static pj_uint16_t pcm2unsigned(pj_int32_t pcm) { diff --git a/pjmedia/src/pjmedia/errno.c b/pjmedia/src/pjmedia/errno.c index 3a91deff..37d5e261 100644 --- a/pjmedia/src/pjmedia/errno.c +++ b/pjmedia/src/pjmedia/errno.c @@ -112,6 +112,11 @@ static const struct { PJMEDIA_ENCTYPE, "Media ports have incompatible media type" }, { PJMEDIA_ENCBITS, "Media ports have incompatible bits per sample" }, { PJMEDIA_ENCBYTES, "Media ports have incompatible bytes per frame" }, + + /* Media file errors: */ + { PJMEDIA_ENOTVALIDWAVE, "Not a valid WAVE file" }, + { PJMEDIA_EWAVEUNSUPP, "Unsupported WAVE file format" }, + { PJMEDIA_EWAVETOOSHORT, "WAVE file too short" }, }; diff --git a/pjmedia/src/pjmedia/file_port.c b/pjmedia/src/pjmedia/file_port.c new file mode 100644 index 00000000..1d7e9872 --- /dev/null +++ b/pjmedia/src/pjmedia/file_port.c @@ -0,0 +1,261 @@ +/* $Id$ */ +/* + * Copyright (C) 2003-2006 Benny Prijono + * + * 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 +#include +#include +#include +#include +#include +#include +#include + + +#define SIGNATURE ('F'<<24|'I'<<16|'L'<<8|'E') +#define BUF_SIZE (320*10) + +struct file_port +{ + pjmedia_port base; + pj_size_t bufsize; + char *buf; + char *readpos; +}; + + +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 pj_status_t read_buffer(struct file_port *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.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; + + port->base.put_frame = &file_put_frame; + port->base.get_frame = &file_get_frame; + port->base.on_destroy = &file_on_destroy; + + return port; +} + +/* + * 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 ) +{ + pj_off_t file_size; + pj_oshandle_t fd = NULL; + pjmedia_wave_hdr wave_hdr; + pj_ssize_t size_read; + struct file_port *file_port; + 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; + } + + /* Get the file size. */ + file_size = pj_file_size(filename); + + /* Size must be more than WAVE header size */ + if (file_size <= sizeof(pjmedia_wave_hdr)) { + return PJMEDIA_ENOTVALIDWAVE; + } + + /* Open file. */ + status = pj_file_open( pool, filename, PJ_O_RDONLY, &fd); + if (status != PJ_SUCCESS) + return status; + + /* Read the WAVE header. */ + size_read = sizeof(wave_hdr); + status = pj_file_read( fd, &wave_hdr, &size_read); + if (status != PJ_SUCCESS) { + pj_file_close(fd); + return status; + } + if (size_read != sizeof(wave_hdr)) { + pj_file_close(fd); + return PJMEDIA_ENOTVALIDWAVE; + } + + /* 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(fd); + return PJMEDIA_ENOTVALIDWAVE; + } + + if (wave_hdr.fmt_hdr.fmt_tag != 1 || + wave_hdr.fmt_hdr.nchan != 1 || + wave_hdr.fmt_hdr.sample_rate != 8000 || + wave_hdr.fmt_hdr.bytes_per_sec != 16000 || + wave_hdr.fmt_hdr.block_align != 2 || + wave_hdr.fmt_hdr.bits_per_sample != 16) + { + pj_file_close(fd); + return PJMEDIA_EWAVEUNSUPP; + } + + /* Validate length. */ + if (wave_hdr.data_hdr.len != file_size-sizeof(pjmedia_wave_hdr)) { + pj_file_close(fd); + return PJMEDIA_EWAVEUNSUPP; + } + if (wave_hdr.data_hdr.len < 400) { + pj_file_close(fd); + return PJMEDIA_EWAVETOOSHORT; + } + + /* It seems like we have a valid WAVE file. */ + + /* Create file_port instance. */ + file_port = create_file_port(pool); + if (!file_port) { + pj_file_close(fd); + return PJ_ENOMEM; + } + + /* Initialize */ + file_port->base.user_data = user_data; + + /* For this version, we only support reading the whole + * contents of the file. + */ + file_port->bufsize = wave_hdr.data_hdr.len - 8; + + /* Create buffer. */ + file_port->buf = pj_pool_alloc(pool, file_port->bufsize); + if (!file_port->buf) { + pj_file_close(fd); + return PJ_ENOMEM; + } + + file_port->readpos = file_port->buf; + + /* Read the the file. */ + size_read = file_port->bufsize; + status = pj_file_read(fd, file_port->buf, &size_read); + if (status != PJ_SUCCESS) { + pj_file_close(fd); + return status; + } + + if (size_read != (pj_ssize_t)file_port->bufsize) { + pj_file_close(fd); + return PJMEDIA_ENOTVALIDWAVE; + } + + + /* Done. */ + pj_file_close(fd); + + *p_port = &file_port->base; + + 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 *port = (struct file_port*)this_port; + + pj_assert(port->base.info.signature == SIGNATURE); + + /* Copy frame from buffer. */ + frame->type = PJMEDIA_FRAME_TYPE_AUDIO; + frame->size = 320; + frame->timestamp.u64 = 0; + + if (port->readpos + 320 <= port->buf + port->bufsize) { + pj_memcpy(frame->buf, port->readpos, 320); + port->readpos += 320; + if (port->readpos == port->buf + port->bufsize) + port->readpos = port->buf; + } else { + unsigned endread; + + endread = (port->buf+port->bufsize) - port->readpos; + pj_memcpy(frame->buf, port->readpos, endread); + pj_memcpy(((char*)frame->buf)+endread, port->buf, 320-endread); + port->readpos = port->buf + (320-endread); + } + + return PJ_SUCCESS; +} + +/* + * + */ +static pj_status_t file_on_destroy(pjmedia_port *this_port) +{ + PJ_UNUSED_ARG(this_port); + + pj_assert(this_port->info.signature == SIGNATURE); + + return PJ_SUCCESS; +} -- cgit v1.2.3