diff --git a/.gitignore b/.gitignore index 93316ad..298604e 100644 --- a/.gitignore +++ b/.gitignore @@ -3,7 +3,7 @@ autoscan-2.*.log configure.scan # autoreconf by-products -*.in +Makefile.in aclocal.m4 autom4te.cache/ @@ -18,6 +18,7 @@ m4/ missing # configure by-products +*.pc .deps Makefile @@ -35,8 +36,17 @@ stamp-h1 libgsmhr/refsrc +# GNU autotest +tests/package.m4 +tests/atconfig +tests/atlocal +tests/testsuite +tests/testsuite.dir/ +tests/testsuite.log +tests/*/*_test + # final executables -src/gapk +src/osmo-gapk # temporary/backup files *.*~ diff --git a/Makefile.am b/Makefile.am index d1638ec..0093f0e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,6 +1,9 @@ AUTOMAKE_OPTIONS = foreign dist-bzip2 1.6 ACLOCAL_AMFLAGS = -I m4 +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = libosmogapk.pc + AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include SUBDIRS = include @@ -9,4 +12,4 @@ if ENABLE_GSMHR SUBDIRS += libgsmhr endif -SUBDIRS += src +SUBDIRS += src tests diff --git a/configure.ac b/configure.ac index c24e8f0..180e380 100644 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,7 @@ AC_PREREQ([2.65]) AC_INIT([gapk], - m4_esyscmd([./git-version-gen .tarball-version]), [main@lists.airprobe.org]) + m4_esyscmd([./git-version-gen .tarball-version]), + [openbsc@lists.osmocom.org]) AM_INIT_AUTOMAKE([dist-bzip2 subdir-objects]) LT_INIT([disable-static]) @@ -8,16 +9,18 @@ LT_INIT([disable-static]) # kernel style compile messages m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) -AC_CONFIG_SRCDIR([src/main.c]) +AC_CONFIG_SRCDIR([src/app_osmo_gapk.c]) AC_CONFIG_HEADERS([config.h]) AC_CONFIG_MACRO_DIR([m4]) +AC_CONFIG_TESTDIR(tests) AC_CONFIG_FILES([ Makefile src/Makefile libgsmhr/Makefile include/Makefile - include/gapk/Makefile include/gsmhr/Makefile + libosmogapk.pc + tests/Makefile ]) # Options @@ -45,6 +48,16 @@ AC_COMPILE_IFELSE([AC_LANG_SOURCE([char foo;])], CFLAGS="$saved_CFLAGS" AC_SUBST(SYMBOL_VISIBILITY) +dnl check os: some linker flags not available on osx +case $host in +*-darwin*) + ;; +*) + LTLDFLAGS_OSMOGAPK='-Wl,--version-script=$(srcdir)/libosmogapk.map' + ;; +esac +AC_SUBST(LTLDFLAGS_OSMOGAPK) + # Checks for programs. AC_PROG_CC diff --git a/contrib/benchmark.sh b/contrib/benchmark.sh index 0148667..0e86876 100755 --- a/contrib/benchmark.sh +++ b/contrib/benchmark.sh @@ -1,6 +1,6 @@ #!/bin/sh -GAPK=./src/gapk +GAPK=./src/osmo-gapk PCMFILE=$1 BASE=`basename $PCMFILE` diff --git a/include/Makefile.am b/include/Makefile.am index ddb25df..c20d3ca 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -1,5 +1,18 @@ -SUBDIRS = gapk +noinst_HEADERS = \ + osmocom/gapk/logging.h \ + osmocom/gapk/utils.h \ + osmocom/gapk/bench.h \ + $(NULL) + +nobase_include_HEADERS = \ + osmocom/gapk/get_cycles.h \ + osmocom/gapk/benchmark.h \ + osmocom/gapk/procqueue.h \ + osmocom/gapk/formats.h \ + osmocom/gapk/codecs.h \ + osmocom/gapk/common.h \ + $(NULL) if ENABLE_GSMHR -SUBDIRS += gsmhr +SUBDIRS = gsmhr endif diff --git a/include/gapk/Makefile.am b/include/gapk/Makefile.am deleted file mode 100644 index 5fcc3b9..0000000 --- a/include/gapk/Makefile.am +++ /dev/null @@ -1,6 +0,0 @@ -noinst_HEADERS = benchmark.h \ - codecs.h \ - formats.h \ - get_cycles.h \ - procqueue.h \ - utils.h diff --git a/include/gapk/benchmark.h b/include/gapk/benchmark.h deleted file mode 100644 index 49c2c36..0000000 --- a/include/gapk/benchmark.h +++ /dev/null @@ -1,60 +0,0 @@ -#ifndef _BENCHMARK_H -#define _BENCHMARK_H - -/* - * This file is part of gapk (GSM Audio Pocket Knife). - * - * gapk 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 3 of the License, or - * (at your option) any later version. - * - * gapk 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 gapk. If not, see . - * - * (C) 2014 Harald Welte - */ - -#include -#include - -#define NUM_AVG 102400 - -struct benchmark_cycles { - cycles_t enc[NUM_AVG]; - unsigned int enc_used; - cycles_t dec[NUM_AVG]; - unsigned int dec_used; -}; - -extern struct benchmark_cycles codec_cycles[_CODEC_MAX]; - -static inline void benchmark_stop(enum codec_type codec, int encode, unsigned long cycles) -{ - struct benchmark_cycles *bc = &codec_cycles[codec]; - - if (encode) { - bc->enc_used = (bc->enc_used + 1) % NUM_AVG; - bc->enc[bc->enc_used] = cycles; - } else { - bc->dec_used = (bc->dec_used + 1) % NUM_AVG; - bc->dec[bc->dec_used] = cycles; - } -} - -#define BENCHMARK_START do { \ - cycles_t _cycles_start, _cycles_stop; \ - _cycles_start = get_cycles() - -#define BENCHMARK_STOP(x,y) _cycles_stop = get_cycles(); \ - benchmark_stop(x, y, _cycles_stop - _cycles_start); \ - } while (0) - -void benchmark_dump(void); - -#endif diff --git a/include/gapk/procqueue.h b/include/gapk/procqueue.h deleted file mode 100644 index d9a5546..0000000 --- a/include/gapk/procqueue.h +++ /dev/null @@ -1,72 +0,0 @@ -/* Processing Queue Management */ - -/* - * This file is part of gapk (GSM Audio Pocket Knife). - * - * gapk 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 3 of the License, or - * (at your option) any later version. - * - * gapk 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 gapk. If not, see . - */ - -#ifndef __GAPK_PROCQUEUE_H__ -#define __GAPK_PROCQUEUE_H__ - -#include -#include /* for FILE */ - -struct pq; - -struct pq_item { - /*! input frame size (in bytes). '0' in case of variable frames */ - int len_in; - /*! output frame size (in bytes). '0' in case of variable frames */ - int len_out; - /*! opaque state */ - void *state; - /*! call-back for actual format conversion function - * \param[in] state opaque state pointer - * \param[out] out caller-allocated buffer for output data - * \param[in] in input data - * \param[in] in_len length of input data \a in - * \returns number of output bytes written to \a out; negative on error */ - int (*proc)(void *state, uint8_t *out, const uint8_t *in, unsigned int in_len); - void (*exit)(void *state); -}; - -/* Management */ -struct pq * pq_create(void); -void pq_destroy(struct pq *pq); -struct pq_item * pq_add_item(struct pq *pq); -int pq_prepare(struct pq *pq); -int pq_execute(struct pq *pq); - -/* File */ -int pq_queue_file_input(struct pq *pq, FILE *src, unsigned int block_len); -int pq_queue_file_output(struct pq *pq, FILE *dst, unsigned int block_len); - -/* RTP */ -int pq_queue_rtp_input(struct pq *pq, int rtp_fd, unsigned int block_len); -int pq_queue_rtp_output(struct pq *pq, int rtp_fd, unsigned int block_len); - -/* ALSA */ -int pq_queue_alsa_input(struct pq *pq, const char *hwdev, unsigned int blk_len); -int pq_queue_alsa_output(struct pq *pq, const char *hwdev, unsigned int blk_len); - -/* Format */ -struct format_desc; -int pq_queue_fmt_convert(struct pq *pq, const struct format_desc *fmt, int to_from_n); - -/* Codec */ -struct codec_desc; -int pq_queue_codec(struct pq *pq, const struct codec_desc *codec, int encode); - -#endif /* __GAPK_PROCQUEUE_H__ */ diff --git a/include/gsmhr/gsmhr.h b/include/gsmhr/gsmhr.h index 17d42cb..b2fa0da 100644 --- a/include/gsmhr/gsmhr.h +++ b/include/gsmhr/gsmhr.h @@ -17,8 +17,7 @@ * along with gapk. If not, see . */ -#ifndef __GSM_HR_H__ -#define __GSM_HR_H__ +#pragma once #include @@ -36,5 +35,3 @@ int gsmhr_decode(struct gsmhr *state, int16_t *pcm, const int16_t *hr_ #ifdef __cplusplus } #endif - -#endif /* __GSM_HR_H__ */ diff --git a/include/osmocom/gapk/bench.h b/include/osmocom/gapk/bench.h new file mode 100644 index 0000000..404760e --- /dev/null +++ b/include/osmocom/gapk/bench.h @@ -0,0 +1,49 @@ +/* + * This file is part of gapk (GSM Audio Pocket Knife). + * + * (C) 2014 Harald Welte + * + * gapk 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 3 of the License, or + * (at your option) any later version. + * + * gapk 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 gapk. If not, see . + * + */ + +#pragma once + +#include +#include +#include + +#define BENCHMARK_START \ + do { \ + cycles_t _cycles_start, _cycles_stop; \ + struct osmo_gapk_bench_cycles *_bc; \ + _cycles_start = get_cycles() + +#define BENCHMARK_STOP(codec, encode) \ + _cycles_stop = get_cycles(); \ + _bc = osmo_gapk_bench_codec[codec]; \ + if (!_bc) break; \ + \ + if (encode) { \ + _bc->enc_used = (_bc->enc_used + 1) \ + % OSMO_GAPK_CYCLES_NUM_AVG; \ + _bc->enc[_bc->enc_used] = \ + _cycles_stop - _cycles_start; \ + } else { \ + _bc->dec_used = (_bc->dec_used + 1) \ + % OSMO_GAPK_CYCLES_NUM_AVG; \ + _bc->dec[_bc->dec_used] = \ + _cycles_stop - _cycles_start; \ + } \ + } while (0) diff --git a/include/osmocom/gapk/benchmark.h b/include/osmocom/gapk/benchmark.h new file mode 100644 index 0000000..f87899d --- /dev/null +++ b/include/osmocom/gapk/benchmark.h @@ -0,0 +1,42 @@ +/* + * This file is part of gapk (GSM Audio Pocket Knife). + * + * gapk 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 3 of the License, or + * (at your option) any later version. + * + * gapk 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 gapk. If not, see . + * + * (C) 2014 Harald Welte + */ + +#pragma once + +#include +#include + +#define OSMO_GAPK_CYCLES_NUM_AVG 102400 + +struct osmo_gapk_bench_cycles { + cycles_t enc[OSMO_GAPK_CYCLES_NUM_AVG]; + unsigned int enc_used; + cycles_t dec[OSMO_GAPK_CYCLES_NUM_AVG]; + unsigned int dec_used; +}; + +extern struct osmo_gapk_bench_cycles *osmo_gapk_bench_codec[_CODEC_MAX]; + +int osmo_gapk_bench_enable(enum osmo_gapk_codec_type codec); +void osmo_gapk_bench_free(void); + +unsigned long long +osmo_gapk_bench_get_cycles(enum osmo_gapk_codec_type codec, int enc); +unsigned int +osmo_gapk_bench_get_frames(enum osmo_gapk_codec_type codec, int enc); diff --git a/include/gapk/codecs.h b/include/osmocom/gapk/codecs.h similarity index 62% rename from include/gapk/codecs.h rename to include/osmocom/gapk/codecs.h index aa1c829..253fb14 100644 --- a/include/gapk/codecs.h +++ b/include/osmocom/gapk/codecs.h @@ -17,8 +17,7 @@ * along with gapk. If not, see . */ -#ifndef __GAPK_CODECS_H__ -#define __GAPK_CODECS_H__ +#pragma once #include @@ -29,7 +28,7 @@ #define HR_REF_ENC_LEN (20 * sizeof(uint16_t)) #define HR_REF_DEC_LEN (22 * sizeof(uint16_t)) -enum codec_type { +enum osmo_gapk_codec_type { CODEC_INVALID = 0, CODEC_PCM, /* 16 bits PCM samples */ CODEC_HR, /* GSM Half Rate codec GSM 06.20 */ @@ -39,7 +38,8 @@ enum codec_type { _CODEC_MAX, }; -#include /* need to import here because or enum interdep */ +/* Need to import here because of enum interdep */ +#include /*! call-back for actual codec conversion function * \param[in] state opaque state pointer (returned by codec->init) @@ -47,25 +47,33 @@ enum codec_type { * \param[in] src input data * \param[in] src_len length of input data \a src * \returns number of output bytes written to \a dst; negative on error */ -typedef int (*codec_conv_cb_t)(void *state, uint8_t *dst, const uint8_t *src, unsigned int src_len); +typedef int (*osmo_gapk_codec_conv_cb_t)(void *state, uint8_t *dst, + const uint8_t *src, unsigned int src_len); -struct codec_desc { - enum codec_type type; - const char * name; - const char * description; - /*! canonical frame size (in bytes); 0 in case of variable length */ - unsigned int canon_frame_len; +struct osmo_gapk_codec_desc { + enum osmo_gapk_codec_type type; + const char *description; + const char *name; - enum format_type codec_enc_format_type; /* what the encoder provides */ - enum format_type codec_dec_format_type; /* what to give the decoder */ - /*! codec initialization function pointer, returns opaque state */ - void * (*codec_init)(void); - /*! codec exit function pointer, gets passed opaque state */ - void (*codec_exit)(void *state); - codec_conv_cb_t codec_encode; - codec_conv_cb_t codec_decode; + /*! + * Canonical frame size (in bytes); + * 0 in case of variable length + */ + unsigned int canon_frame_len; + + /*! What the encoder provides */ + enum osmo_gapk_format_type codec_enc_format_type; + /*! What to give the decoder */ + enum osmo_gapk_format_type codec_dec_format_type; + + /* (De)initialization function pointers */ + void *(*codec_init)(void); + void (*codec_exit)(void *state); + + /* Encoding / decoding function pointers */ + osmo_gapk_codec_conv_cb_t codec_encode; + osmo_gapk_codec_conv_cb_t codec_decode; }; -const struct codec_desc *codec_get_from_type(enum codec_type type); - -#endif /* __GAPK_CODECS_H__ */ +const struct osmo_gapk_codec_desc * +osmo_gapk_codec_get_from_type(enum osmo_gapk_codec_type type); diff --git a/include/osmocom/gapk/common.h b/include/osmocom/gapk/common.h new file mode 100644 index 0000000..920824e --- /dev/null +++ b/include/osmocom/gapk/common.h @@ -0,0 +1,22 @@ +/* + * This file is part of gapk (GSM Audio Pocket Knife). + * + * gapk 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 3 of the License, or + * (at your option) any later version. + * + * gapk 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 gapk. If not, see . + * + */ + +#pragma once + +void osmo_gapk_set_talloc_ctx(void *ctx); +void osmo_gapk_log_init(int subsys); diff --git a/include/gapk/formats.h b/include/osmocom/gapk/formats.h similarity index 63% rename from include/gapk/formats.h rename to include/osmocom/gapk/formats.h index 81670b8..d1521d0 100644 --- a/include/gapk/formats.h +++ b/include/osmocom/gapk/formats.h @@ -17,12 +17,11 @@ * along with gapk. If not, see . */ -#ifndef __GAPK_FORMATS_H__ -#define __GAPK_FORMATS_H__ +#pragma once #include -enum format_type { +enum osmo_gapk_format_type { FMT_INVALID = 0, /* Classic .amr container */ @@ -62,33 +61,37 @@ enum format_type { _FMT_MAX, }; -#include /* need to import here because or enum interdep */ +/* Need to import here because of enum interdep */ +#include /*! call-back for actual format conversion function * \param[out] dst caller-allocated buffer for output data * \param[in] src input data * \param[in] src_len length of input data \a src * \returns number of output bytes written to \a dst; negative on error */ -typedef int (*fmt_conv_cb_t)(uint8_t *dst, const uint8_t *src, unsigned int src_len); +typedef int (*osmo_gapk_fmt_conv_cb_t)(uint8_t *dst, + const uint8_t *src, unsigned int src_len); -struct format_desc { - enum format_type type; - enum codec_type codec_type; - const char * name; - const char * description; +struct osmo_gapk_format_desc { + enum osmo_gapk_format_type type; + enum osmo_gapk_codec_type codec_type; + const char *description; + const char *name; - /*! length of frames in this format (as opposed to canonical) */ - unsigned int frame_len; - fmt_conv_cb_t conv_from_canon; - fmt_conv_cb_t conv_to_canon; + /*! Length of frames in this format (as opposed to canonical) */ + unsigned int frame_len; - /*! length of a (global) header at start of file */ - unsigned int header_len; - /*! exact match for (global) header at start of file */ - const uint8_t * header; + /* Format conversation function pointers */ + osmo_gapk_fmt_conv_cb_t conv_from_canon; + osmo_gapk_fmt_conv_cb_t conv_to_canon; + + /*! Length of a (global) header at start of file */ + unsigned int header_len; + /*! Exact match for (global) header at start of file */ + const uint8_t *header; }; -const struct format_desc *fmt_get_from_type(enum format_type type); -const struct format_desc *fmt_get_from_name(const char *name); - -#endif /* __GAPK_FORMATS_H__ */ +const struct osmo_gapk_format_desc * +osmo_gapk_fmt_get_from_type(enum osmo_gapk_format_type type); +const struct osmo_gapk_format_desc * +osmo_gapk_fmt_get_from_name(const char *name); diff --git a/include/gapk/get_cycles.h b/include/osmocom/gapk/get_cycles.h similarity index 98% rename from include/gapk/get_cycles.h rename to include/osmocom/gapk/get_cycles.h index 9eb7bc3..40ac570 100644 --- a/include/gapk/get_cycles.h +++ b/include/osmocom/gapk/get_cycles.h @@ -33,8 +33,7 @@ * $Id$ */ -#ifndef GET_CLOCK_H -#define GET_CLOCK_H +#pragma once #if 0 @@ -134,5 +133,3 @@ static inline cycles_t get_cycles() #endif extern double get_cpu_mhz(void); - -#endif /* GET_CLOCK_H */ diff --git a/include/osmocom/gapk/logging.h b/include/osmocom/gapk/logging.h new file mode 100644 index 0000000..60376f1 --- /dev/null +++ b/include/osmocom/gapk/logging.h @@ -0,0 +1,28 @@ +/* + * This file is part of gapk (GSM Audio Pocket Knife). + * + * gapk 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 3 of the License, or + * (at your option) any later version. + * + * gapk 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 gapk. If not, see . + * + */ + +#pragma once + +#include + +extern int gapk_log_init_complete; +extern int gapk_log_subsys; + +#define LOGPGAPK(level, fmt, args...) \ + if (gapk_log_init_complete) \ + LOGP(gapk_log_subsys, level, fmt, ## args) diff --git a/include/osmocom/gapk/procqueue.h b/include/osmocom/gapk/procqueue.h new file mode 100644 index 0000000..6b929d7 --- /dev/null +++ b/include/osmocom/gapk/procqueue.h @@ -0,0 +1,105 @@ +/* Processing Queue Management */ + +/* + * This file is part of gapk (GSM Audio Pocket Knife). + * + * gapk 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 3 of the License, or + * (at your option) any later version. + * + * gapk 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 gapk. If not, see . + */ + +#pragma once + +#include +#include /* for FILE */ + +#include + +enum osmo_gapk_pq_item_type { + OSMO_GAPK_ITEM_TYPE_SOURCE, + OSMO_GAPK_ITEM_TYPE_SINK, + OSMO_GAPK_ITEM_TYPE_PROC, +}; + +#define OSMO_GAPK_CAT_NAME_SOURCE "source" +#define OSMO_GAPK_CAT_NAME_SINK "sink" +#define OSMO_GAPK_CAT_NAME_PROC "proc" + +struct osmo_gapk_pq_item { + /*! input frame size (in bytes). '0' in case of variable frames */ + unsigned int len_in; + /*! output frame size (in bytes). '0' in case of variable frames */ + unsigned int len_out; + /*! opaque state */ + void *state; + /*! buffer for output data */ + uint8_t *buf; + /*! call-back for actual format conversion function + * \param[in] state opaque state pointer + * \param[out] out caller-allocated buffer for output data + * \param[in] in input data + * \param[in] in_len length of input data \a in + * \returns number of output bytes written to \a out; negative on error */ + int (*proc)(void *state, uint8_t *out, const uint8_t *in, unsigned int in_len); + int (*wait)(void *state); + void (*exit)(void *state); + + /*! \brief link to a processing queue */ + struct llist_head list; + /*! \brief type of item */ + enum osmo_gapk_pq_item_type type; + /*! \brief category name (src, format, codec, sink) */ + const char *cat_name; + /*! \brief sub-category name (file, rtp-amr, amr, alsa) */ + const char *sub_name; +}; + +#define VAR_BUF_SIZE 320 + +struct osmo_gapk_pq { + struct llist_head items; + unsigned n_items; + + /*! \brief human-readable name */ + const char *name; +}; + +/* Processing queue management */ +struct osmo_gapk_pq *osmo_gapk_pq_create(const char *name); +int osmo_gapk_pq_check(struct osmo_gapk_pq *pq, int strict); +int osmo_gapk_pq_prepare(struct osmo_gapk_pq *pq); +int osmo_gapk_pq_execute(struct osmo_gapk_pq *pq); +void osmo_gapk_pq_destroy(struct osmo_gapk_pq *pq); +char *osmo_gapk_pq_describe(struct osmo_gapk_pq *pq); + +/* Processing queue item management */ +struct osmo_gapk_pq_item *osmo_gapk_pq_add_item(struct osmo_gapk_pq *pq); + +/* File */ +int osmo_gapk_pq_queue_file_input(struct osmo_gapk_pq *pq, FILE *src, unsigned int block_len); +int osmo_gapk_pq_queue_file_output(struct osmo_gapk_pq *pq, FILE *dst, unsigned int block_len); + +/* RTP */ +int osmo_gapk_pq_queue_rtp_input(struct osmo_gapk_pq *pq, int rtp_fd, unsigned int block_len); +int osmo_gapk_pq_queue_rtp_output(struct osmo_gapk_pq *pq, int rtp_fd, unsigned int block_len); + +/* ALSA */ +int osmo_gapk_pq_queue_alsa_input(struct osmo_gapk_pq *pq, const char *hwdev, unsigned int blk_len); +int osmo_gapk_pq_queue_alsa_output(struct osmo_gapk_pq *pq, const char *hwdev, unsigned int blk_len); + +/* Format */ +struct osmo_gapk_format_desc; +int osmo_gapk_pq_queue_fmt_convert(struct osmo_gapk_pq *pq, const struct osmo_gapk_format_desc *fmt, int to_from_n); + +/* Codec */ +struct osmo_gapk_codec_desc; +int osmo_gapk_pq_queue_codec(struct osmo_gapk_pq *pq, const struct osmo_gapk_codec_desc *codec, int encode); diff --git a/include/gapk/utils.h b/include/osmocom/gapk/utils.h similarity index 96% rename from include/gapk/utils.h rename to include/osmocom/gapk/utils.h index 3b02049..0b8b75b 100644 --- a/include/gapk/utils.h +++ b/include/osmocom/gapk/utils.h @@ -17,8 +17,7 @@ * along with gapk. If not, see . */ -#ifndef __GAPK_UTILS_H__ -#define __GAPK_UTILS_H__ +#pragma once #include @@ -100,5 +99,3 @@ lsb_clr_bit(uint8_t *buf, int bn) buf[pos_byte] &= ~(1 << pos_bit); } - -#endif /* __GAPK_UTILS_H__ */ diff --git a/libosmogapk.pc.in b/libosmogapk.pc.in new file mode 100644 index 0000000..df27402 --- /dev/null +++ b/libosmogapk.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: Osmocom GSM Audio Transcoding Library +Description: C Utility Library +Requires: libosmocore, libosmocodec +Version: @VERSION@ +Libs: -L${libdir} -losmogapk +Cflags: -I${includedir}/ diff --git a/src/Makefile.am b/src/Makefile.am index e0b622c..8efd165 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,20 +1,97 @@ -AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir) -AM_CFLAGS=-Wall $(LIBOSMOCODEC_CFLAGS) $(LIBOSMOCORE_CFLAGS) \ - ${OPENCORE_AMRNB_CFLAGS} $(LIBALSA_CFLAGS) -AM_LDFLAGS=$(LIBOSMOCODEC_LIBS) $(LIBOSMOCORE_LIBS) \ - ${OPENCORE_AMRNB_LIBS} ${LIBGSM_LIBS} $(LIBALSA_LIBS) +# This is _NOT_ the library release version, it's an API version. +# Please read Chapter 6 "Library interface versions" of the libtool +# documentation before making any modification +LIBVERSION = 0:0:0 -COM_SOURCES = procqueue.c pq_file.c pq_format.c pq_codec.c pq_rtp.c pq_alsa.c \ - formats.c fmt_amr.c fmt_gsm.c fmt_hr_ref.c fmt_racal.c \ - fmt_amr_opencore.c \ - fmt_rtp_amr.c fmt_rtp_efr.c fmt_rtp_hr_etsi.c fmt_rtp_hr_ietf.c \ - fmt_rawpcm.c fmt_ti.c benchmark.c \ - codecs.c codec_pcm.c codec_hr.c codec_fr.c codec_efr.c codec_amr.c +AM_CPPFLAGS = \ + $(all_includes) \ + -I$(top_builddir) \ + -I$(top_srcdir)/include \ + $(NULL) -bin_PROGRAMS = gapk +AM_CFLAGS = \ + -Wall \ + $(LIBOSMOCORE_CFLAGS) \ + $(LIBOSMOCODEC_CFLAGS) \ + ${OPENCORE_AMRNB_CFLAGS} \ + $(LIBALSA_CFLAGS) \ + $(NULL) -gapk_SOURCES = main.c $(COM_SOURCES) +lib_LTLIBRARIES = libosmogapk.la +EXTRA_DIST = libosmogapk.map + +libosmogapk_la_LDFLAGS = \ + $(LIBOSMOCORE_LIBS) \ + $(LIBOSMOCODEC_LIBS) \ + ${OPENCORE_AMRNB_LIBS} \ + ${LIBGSM_LIBS} \ + $(LIBALSA_LIBS) \ + $(LTLDFLAGS_OSMOGAPK) \ + -version-info $(LIBVERSION) \ + -no-undefined \ + $(NULL) if ENABLE_GSMHR -gapk_LDADD = $(top_builddir)/libgsmhr/libgsmhr.la +libosmogapk_la_LIBADD = $(top_builddir)/libgsmhr/libgsmhr.la endif + +# Processing queue implementation +libosmogapk_la_SOURCES = \ + procqueue.c \ + pq_format.c \ + pq_codec.c \ + pq_file.c \ + pq_alsa.c \ + pq_rtp.c \ + $(NULL) + +# Formats implementation +libosmogapk_la_SOURCES += \ + formats.c \ + fmt_ti.c \ + fmt_amr.c \ + fmt_gsm.c \ + fmt_hr_ref.c \ + fmt_racal.c \ + fmt_rawpcm.c \ + fmt_rtp_amr.c \ + fmt_rtp_efr.c \ + fmt_rtp_hr_etsi.c \ + fmt_rtp_hr_ietf.c \ + fmt_amr_opencore.c \ + $(NULL) + +# Codecs implementation +libosmogapk_la_SOURCES += \ + codecs.c \ + codec_pcm.c \ + codec_hr.c \ + codec_fr.c \ + codec_efr.c \ + codec_amr.c \ + $(NULL) + +# Codec benchmarking +libosmogapk_la_SOURCES += \ + benchmark.c \ + $(NULL) + +# Common routines +libosmogapk_la_SOURCES += \ + common.c \ + $(NULL) + +# libosmogapk representative application +bin_PROGRAMS = osmo-gapk + +osmo_gapk_SOURCES = \ + app_osmo_gapk.c \ + $(NULL) + +osmo_gapk_LDFLAGS = \ + $(LIBOSMOCORE_LIBS) \ + $(NULL) + +osmo_gapk_LDADD = \ + $(top_builddir)/src/libosmogapk.la \ + $(NULL) diff --git a/src/main.c b/src/app_osmo_gapk.c similarity index 52% rename from src/main.c rename to src/app_osmo_gapk.c index 58e23c1..729ea25 100644 --- a/src/main.c +++ b/src/app_osmo_gapk.c @@ -26,18 +26,25 @@ #include #include #include +#include #include #include #include +#include +#include #include +#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +/* The root talloc context of application */ +TALLOC_CTX *app_root_ctx; struct gapk_options { @@ -47,7 +54,7 @@ struct gapk_options uint16_t port; } rtp_in; const char *alsa_in; - const struct format_desc *fmt_in; + const struct osmo_gapk_format_desc *fmt_in; const char *fname_out; struct { @@ -55,14 +62,17 @@ struct gapk_options uint16_t port; } rtp_out; const char *alsa_out; - const struct format_desc *fmt_out; + const struct osmo_gapk_format_desc *fmt_out; + + int benchmark; + int verbose; }; struct gapk_state { struct gapk_options opts; - - struct pq *pq; + struct osmo_gapk_pq *pq; + int exit; struct { struct { @@ -83,10 +93,30 @@ struct gapk_state } out; }; +/* Logging related routines */ +enum { + DAPP, +}; + +static struct log_info_cat gapk_log_info_cat[] = { + [DAPP] = { + .name = "DAPP", + .description = "Application", + .color = "\033[0;36m", + .enabled = 1, .loglevel = LOGL_NOTICE, + }, +}; + +static const struct log_info gapk_log_info = { + .cat = gapk_log_info_cat, + .num_cat = ARRAY_SIZE(gapk_log_info_cat), +}; + static void print_help(char *progname) { + const struct osmo_gapk_codec_desc *codec; int i; /* Header */ @@ -103,6 +133,8 @@ print_help(char *progname) #endif fprintf(stdout, " -f, --input-format=FMT\tInput format (see below)\n"); fprintf(stdout, " -g, --output-format=FMT\tOutput format (see below)\n"); + fprintf(stdout, " -b, --enable-benchmark\tEnable codec benchmarking\n"); + fprintf(stdout, " -v, --verbose\t\t\tEnable debug messages\n"); fprintf(stdout, "\n"); /* Print all codecs */ @@ -110,7 +142,7 @@ print_help(char *progname) fprintf(stdout, " name\tfmt enc dec\tdescription\n"); for (i=CODEC_INVALID+1; i<_CODEC_MAX; i++) { - const struct codec_desc *codec = codec_get_from_type(i); + codec = osmo_gapk_codec_get_from_type(i); fprintf(stdout, " %4s %c %c %c \t%s\n", codec->name, '*', @@ -126,7 +158,7 @@ print_help(char *progname) fprintf(stdout, "Supported formats:\n"); for (i=FMT_INVALID+1; i<_FMT_MAX; i++) { - const struct format_desc *fmt = fmt_get_from_type(i); + const struct osmo_gapk_format_desc *fmt = osmo_gapk_fmt_get_from_type(i); fprintf(stdout, " %-19s %s\n", fmt->name, fmt->description @@ -171,9 +203,11 @@ parse_options(struct gapk_state *state, int argc, char *argv[]) #endif {"input-format", 1, 0, 'f'}, {"output-format", 1, 0, 'g'}, + {"enable-benchmark", 0, 0, 'b'}, + {"verbose", 0, 0, 'v'}, {"help", 0, 0, 'h'}, }; - const char *short_options = "i:o:I:O:f:g:h" + const char *short_options = "i:o:I:O:f:g:bvh" #ifdef HAVE_ALSA "a:A:" #endif @@ -206,7 +240,7 @@ parse_options(struct gapk_state *state, int argc, char *argv[]) case 'I': rv = parse_host_port(optarg, &opt->rtp_in.hostname); if (rv < 0 || rv > 0xffff) { - fprintf(stderr, "[!] Invalid port: %d\n", rv); + LOGP(DAPP, LOGL_ERROR, "Invalid port: %d\n", rv); return -EINVAL; } opt->rtp_in.port = rv; @@ -223,33 +257,42 @@ parse_options(struct gapk_state *state, int argc, char *argv[]) case 'O': rv = parse_host_port(optarg, &opt->rtp_out.hostname); if (rv < 0 || rv > 0xffff) { - fprintf(stderr, "[!] Invalid port: %d\n", rv); + LOGP(DAPP, LOGL_ERROR, "Invalid port: %d\n", rv); return -EINVAL; } opt->rtp_out.port = rv; break; case 'f': - opt->fmt_in = fmt_get_from_name(optarg); + opt->fmt_in = osmo_gapk_fmt_get_from_name(optarg); if (!opt->fmt_in) { - fprintf(stderr, "[!] Unsupported format: %s\n", optarg); + LOGP(DAPP, LOGL_ERROR, "Unsupported format: %s\n", optarg); return -EINVAL; } break; case 'g': - opt->fmt_out = fmt_get_from_name(optarg); + opt->fmt_out = osmo_gapk_fmt_get_from_name(optarg); if (!opt->fmt_out) { - fprintf(stderr, "[!] Unsupported format: %s\n", optarg); + LOGP(DAPP, LOGL_ERROR, "Unsupported format: %s\n", optarg); return -EINVAL; } break; + case 'b': + opt->benchmark = 1; + break; + + case 'v': + log_parse_category_mask(osmo_stderr_target, "DAPP"); + opt->verbose = 1; + break; + case 'h': return 1; default: - fprintf(stderr, "[+] Unknown option\n"); + LOGP(DAPP, LOGL_ERROR, "Unknown option\n"); return -EINVAL; } @@ -263,98 +306,157 @@ check_options(struct gapk_state *gs) { /* Required formats */ if (!gs->opts.fmt_in || !gs->opts.fmt_out) { - fprintf(stderr, "[!] Input and output formats are required arguments !\n"); + LOGP(DAPP, LOGL_ERROR, "Input and output formats are required arguments\n"); return -EINVAL; } /* Transcoding */ if (gs->opts.fmt_in->codec_type != gs->opts.fmt_out->codec_type) { - const struct codec_desc *codec; + const struct osmo_gapk_codec_desc *codec; /* Check source codec */ - codec = codec_get_from_type(gs->opts.fmt_in->codec_type); + codec = osmo_gapk_codec_get_from_type(gs->opts.fmt_in->codec_type); if (!codec) { - fprintf(stderr, "[!] Internal error: bad codec reference\n"); + LOGP(DAPP, LOGL_ERROR, "Internal error: bad codec reference\n"); return -EINVAL; } if ((codec->type != CODEC_PCM) && !codec->codec_decode) { - fprintf(stderr, "[!] Decoding from '%s' codec is unsupported\n", codec->name); + LOGP(DAPP, LOGL_ERROR, "Decoding from '%s' codec is unsupported\n", codec->name); return -ENOTSUP; } /* Check destination codec */ - codec = codec_get_from_type(gs->opts.fmt_out->codec_type); + codec = osmo_gapk_codec_get_from_type(gs->opts.fmt_out->codec_type); if (!codec) { - fprintf(stderr, "[!] Internal error: bad codec reference\n"); + LOGP(DAPP, LOGL_ERROR, "Internal error: bad codec reference\n"); return -EINVAL; } if ((codec->type != CODEC_PCM) && !codec->codec_encode) { - fprintf(stderr, "[!] Encoding to '%s' codec is unsupported\n", codec->name); + LOGP(DAPP, LOGL_ERROR, "Encoding to '%s' codec is unsupported\n", codec->name); return -ENOTSUP; } } - /* Input combinations */ - if (gs->opts.fname_in && gs->opts.rtp_in.port) { - fprintf(stderr, "[!] You have to decide on either file or RTP input\n"); - return -EINVAL; - } + /* Check I/O combinations */ + int src_count = 0; + int sink_count = 0; - /* Output combinations */ - if (gs->opts.fname_out && gs->opts.rtp_out.port) { - fprintf(stderr, "[!] You have to decide on either file or RTP output\n"); + if (gs->opts.fname_in) + src_count++; + if (gs->opts.rtp_in.port) + src_count++; + if (gs->opts.alsa_in) + src_count++; + + if (gs->opts.fname_out) + sink_count++; + if (gs->opts.rtp_out.port) + sink_count++; + if (gs->opts.alsa_out) + sink_count++; + + if (src_count > 1 || sink_count > 1) { + LOGP(DAPP, LOGL_ERROR, "You have to decide on " + "a single input and a single output\n"); return -EINVAL; } return 0; } +static void +benchmark_dump(void) +{ + int i; + + for (i = 0; i < _CODEC_MAX; i++) { + struct osmo_gapk_bench_cycles *bc; + unsigned long long cycles; + unsigned int frames; + + /* Check if there are benchmark data */ + bc = osmo_gapk_bench_codec[i]; + if (!bc) + continue; + + if (bc->enc_used) { + cycles = osmo_gapk_bench_get_cycles(i, 1); + frames = osmo_gapk_bench_get_frames(i, 1); + + LOGP(DAPP, LOGL_NOTICE, "Codec %u (ENC): %llu cycles for %u frames" + " => %llu cycles/frame\n", i, cycles, + frames, cycles / frames); + } + + if (bc->dec_used) { + cycles = osmo_gapk_bench_get_cycles(i, 0); + frames = osmo_gapk_bench_get_frames(i, 0); + + LOGP(DAPP, LOGL_NOTICE, "Codec %u (DEC): %llu cycles for %u frames" + " => %llu cycles/frame\n", i, cycles, + frames, cycles / frames); + } + } +} + static int files_open(struct gapk_state *gs) { + LOGP(DAPP, LOGL_NOTICE, "Opening I/O streams\n"); + if (gs->opts.fname_in) { + LOGP(DAPP, LOGL_NOTICE, "Using a file as source\n"); gs->in.file.fh = fopen(gs->opts.fname_in, "rb"); if (!gs->in.file.fh) { - fprintf(stderr, "[!] Error while opening input file for reading\n"); + LOGP(DAPP, LOGL_ERROR, "Error while opening input file for reading\n"); perror("fopen"); return -errno; } } else if (gs->opts.rtp_in.port) { + LOGP(DAPP, LOGL_NOTICE, "Using RTP as source\n"); gs->in.rtp.fd = osmo_sock_init(AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, gs->opts.rtp_in.hostname, gs->opts.rtp_in.port, OSMO_SOCK_F_BIND); if (gs->in.rtp.fd < 0) { - fprintf(stderr, "[!] Error while opening input socket\n"); + LOGP(DAPP, LOGL_ERROR, "Error while opening input socket\n"); return gs->in.rtp.fd; } } else if (gs->opts.alsa_in) { - printf("alsa_in, not stdin\n"); - } else + /* Do nothing, ALSA source does the initialization itself */ + LOGP(DAPP, LOGL_NOTICE, "Using ALSA as source\n"); + } else { + LOGP(DAPP, LOGL_NOTICE, "Using stdin as source\n"); gs->in.file.fh = stdin; + } if (gs->opts.fname_out) { + LOGP(DAPP, LOGL_NOTICE, "Using a file as sink\n"); gs->out.file.fh = fopen(gs->opts.fname_out, "wb"); if (!gs->out.file.fh) { - fprintf(stderr, "[!] Error while opening output file for writing\n"); + LOGP(DAPP, LOGL_ERROR, "Error while opening output file for writing\n"); perror("fopen"); return -errno; } } else if (gs->opts.rtp_out.port) { + LOGP(DAPP, LOGL_NOTICE, "Using RTP as sink\n"); gs->out.rtp.fd = osmo_sock_init(AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, gs->opts.rtp_out.hostname, gs->opts.rtp_out.port, OSMO_SOCK_F_CONNECT); if (gs->out.rtp.fd < 0) { - fprintf(stderr, "[!] Error while opening output socket\n"); + LOGP(DAPP, LOGL_ERROR, "Error while opening output socket\n"); return gs->out.rtp.fd; } } else if (gs->opts.alsa_out) { - printf("alsa_out, not stdout\n"); - } else + /* Do nothing, ALSA sink does the initialization itself */ + LOGP(DAPP, LOGL_NOTICE, "Using ALSA as sink\n"); + } else { + LOGP(DAPP, LOGL_NOTICE, "Using stdout as sink\n"); gs->out.file.fh = stdout; + } return 0; } @@ -362,6 +464,8 @@ files_open(struct gapk_state *gs) static void files_close(struct gapk_state *gs) { + LOGP(DAPP, LOGL_NOTICE, "Closing I/O streams\n"); + if (gs->in.file.fh && gs->in.file.fh != stdin) fclose(gs->in.file.fh); if (gs->in.rtp.fd >= 0) @@ -383,7 +487,7 @@ handle_headers(struct gapk_state *gs) if (len && gs->in.file.fh) { uint8_t *buf; - buf = malloc(len); + buf = talloc_size(app_root_ctx, len); if (!buf) return -ENOMEM; @@ -391,12 +495,12 @@ handle_headers(struct gapk_state *gs) if ((rv != 1) || memcmp(buf, gs->opts.fmt_in->header, len)) { - free(buf); - fprintf(stderr, "[!] Invalid header in input file"); + LOGP(DAPP, LOGL_ERROR, "Invalid header in input file"); + talloc_free(buf); return -EINVAL; } - free(buf); + talloc_free(buf); } /* Output file header (write it) */ @@ -413,16 +517,19 @@ handle_headers(struct gapk_state *gs) static int make_processing_chain(struct gapk_state *gs) { - const struct format_desc *fmt_in, *fmt_out; - const struct codec_desc *codec_in, *codec_out; + const struct osmo_gapk_format_desc *fmt_in, *fmt_out; + const struct osmo_gapk_codec_desc *codec_in, *codec_out; int need_dec, need_enc; + int rc; + + LOGP(DAPP, LOGL_NOTICE, "Creating a processing queue\n"); fmt_in = gs->opts.fmt_in; fmt_out = gs->opts.fmt_out; - codec_in = codec_get_from_type(fmt_in->codec_type); - codec_out = codec_get_from_type(fmt_out->codec_type); + codec_in = osmo_gapk_codec_get_from_type(fmt_in->codec_type); + codec_out = osmo_gapk_codec_get_from_type(fmt_out->codec_type); need_dec = (fmt_in->codec_type != CODEC_PCM) && (fmt_in->codec_type != fmt_out->codec_type); @@ -431,15 +538,15 @@ make_processing_chain(struct gapk_state *gs) /* File read */ if (gs->in.file.fh) - pq_queue_file_input(gs->pq, gs->in.file.fh, fmt_in->frame_len); + osmo_gapk_pq_queue_file_input(gs->pq, gs->in.file.fh, fmt_in->frame_len); else if (gs->in.rtp.fd != -1) - pq_queue_rtp_input(gs->pq, gs->in.rtp.fd, fmt_in->frame_len); + osmo_gapk_pq_queue_rtp_input(gs->pq, gs->in.rtp.fd, fmt_in->frame_len); #ifdef HAVE_ALSA else if (gs->opts.alsa_in) - pq_queue_alsa_input(gs->pq, gs->opts.alsa_in, fmt_in->frame_len); + osmo_gapk_pq_queue_alsa_input(gs->pq, gs->opts.alsa_in, fmt_in->frame_len); #endif else { - fprintf(stderr, "Unknown/invalid input\n"); + LOGP(DAPP, LOGL_ERROR, "Unknown/invalid input\n"); return -1; } @@ -449,85 +556,111 @@ make_processing_chain(struct gapk_state *gs) /* Convert input to decoder input fmt */ if (fmt_in->type != codec_in->codec_dec_format_type) { - const struct format_desc *fmt_dec; + const struct osmo_gapk_format_desc *fmt_dec; - fmt_dec = fmt_get_from_type(codec_in->codec_dec_format_type); + fmt_dec = osmo_gapk_fmt_get_from_type(codec_in->codec_dec_format_type); if (!fmt_dec) { - fprintf(stderr, "Cannot determine decoder input format for codec %s\n", - codec_in->name); + LOGP(DAPP, LOGL_ERROR, "Cannot determine decoder input format " + "for codec %s\n", codec_in->name); return -EINVAL; } - pq_queue_fmt_convert(gs->pq, fmt_in, 0); - pq_queue_fmt_convert(gs->pq, fmt_dec, 1); + osmo_gapk_pq_queue_fmt_convert(gs->pq, fmt_in, 0); + osmo_gapk_pq_queue_fmt_convert(gs->pq, fmt_dec, 1); } /* Do decoding */ - pq_queue_codec(gs->pq, codec_in, 0); + osmo_gapk_pq_queue_codec(gs->pq, codec_in, 0); + + /* Allocate memory for benchmarking */ + if (gs->opts.benchmark) + osmo_gapk_bench_enable(fmt_in->codec_type); } else if (fmt_in->type != fmt_out->type) { /* Convert input to canonical fmt */ - pq_queue_fmt_convert(gs->pq, fmt_in, 0); + osmo_gapk_pq_queue_fmt_convert(gs->pq, fmt_in, 0); } /* Encoding from PCM ? */ if (need_enc) { /* Do encoding */ - pq_queue_codec(gs->pq, codec_out, 1); + osmo_gapk_pq_queue_codec(gs->pq, codec_out, 1); + + /* Allocate memory for benchmarking */ + if (gs->opts.benchmark) + osmo_gapk_bench_enable(fmt_out->codec_type); /* Convert encoder output to output fmt */ if (fmt_out->type != codec_out->codec_enc_format_type) { - const struct format_desc *fmt_enc; + const struct osmo_gapk_format_desc *fmt_enc; - fmt_enc = fmt_get_from_type(codec_out->codec_enc_format_type); + fmt_enc = osmo_gapk_fmt_get_from_type(codec_out->codec_enc_format_type); if (!fmt_enc) { - fprintf(stderr, "Cannot determine encoder output format for codec %s\n", - codec_out->name); + LOGP(DAPP, LOGL_ERROR, "Cannot determine encoder output format " + "for codec %s\n", codec_out->name); return -EINVAL; } - pq_queue_fmt_convert(gs->pq, fmt_enc, 0); - pq_queue_fmt_convert(gs->pq, fmt_out, 1); + osmo_gapk_pq_queue_fmt_convert(gs->pq, fmt_enc, 0); + osmo_gapk_pq_queue_fmt_convert(gs->pq, fmt_out, 1); } } else if (fmt_in->type != fmt_out->type) { /* Convert canonical to output fmt */ - pq_queue_fmt_convert(gs->pq, fmt_out, 1); + osmo_gapk_pq_queue_fmt_convert(gs->pq, fmt_out, 1); } /* File write */ if (gs->out.file.fh) - pq_queue_file_output(gs->pq, gs->out.file.fh, fmt_out->frame_len); + osmo_gapk_pq_queue_file_output(gs->pq, gs->out.file.fh, fmt_out->frame_len); else if (gs->out.rtp.fd != -1) - pq_queue_rtp_output(gs->pq, gs->out.rtp.fd, fmt_out->frame_len); + osmo_gapk_pq_queue_rtp_output(gs->pq, gs->out.rtp.fd, fmt_out->frame_len); #ifdef HAVE_ALSA else if (gs->opts.alsa_out) - pq_queue_alsa_output(gs->pq, gs->opts.alsa_out, fmt_out->frame_len); + osmo_gapk_pq_queue_alsa_output(gs->pq, gs->opts.alsa_out, fmt_out->frame_len); #endif else { - fprintf(stderr, "Unknown/invalid output\n"); + LOGP(DAPP, LOGL_ERROR, "Unknown/invalid output\n"); return -1; } + /* Check the processing queue in strict mode */ + rc = osmo_gapk_pq_check(gs->pq, 1); + if (rc) + return rc; + return 0; } static int run(struct gapk_state *gs) { + struct osmo_gapk_pq_item *item; int rv, frames; - rv = pq_prepare(gs->pq); + rv = osmo_gapk_pq_prepare(gs->pq); if (rv) return rv; - for (frames=0; !(rv = pq_execute(gs->pq)); frames++); + for (frames = 0; !gs->exit; frames++) { + rv = osmo_gapk_pq_execute(gs->pq); + if (rv) + break; + } - fprintf(stderr, "[+] Processed %d frames\n", frames); + LOGP(DAPP, LOGL_NOTICE, "Processed %d frames\n", frames); + + /* Wait for sink to process buffers */ + item = llist_last_entry(&gs->pq->items, struct osmo_gapk_pq_item, list); + if (item->wait && !gs->exit) { + LOGP(DAPP, LOGL_NOTICE, "Waiting for sink to finish...\n"); + while (item->wait(item->state)) + continue; + } return frames > 0 ? 0 : rv; } @@ -535,15 +668,39 @@ run(struct gapk_state *gs) static struct gapk_state _gs, *gs = &_gs; +static void app_shutdown(void) +{ + /* Close source / destination files */ + files_close(gs); + + /* Release processing queue */ + osmo_gapk_pq_destroy(gs->pq); + + /* Print benchmarking results, if enabled */ + benchmark_dump(); + + /* Free memory taken by benchmark data */ + osmo_gapk_bench_free(); + + if (gs->opts.verbose) + talloc_report_full(app_root_ctx, stderr); +} + static void signal_handler(int signal) { + fprintf(stderr, "signal %u received\n", signal); + switch (signal) { case SIGINT: - fprintf(stderr, "catching sigint, closing files\n"); - files_close(gs); - pq_destroy(gs->pq); - exit(0); + if (gs->exit++) { + app_shutdown(); + exit(0); + } break; + case SIGABRT: + case SIGUSR1: + case SIGUSR2: + talloc_report_full(app_root_ctx, stderr); default: break; } @@ -554,6 +711,15 @@ int main(int argc, char *argv[]) { int rv; + /* Init talloc memory management system */ + app_root_ctx = talloc_init("osmo-gapk root context"); + osmo_gapk_set_talloc_ctx(app_root_ctx); + + /* Init Osmocom logging framework */ + osmo_init_logging(&gapk_log_info); + /* and GAPK logging wrapper */ + osmo_gapk_log_init(DAPP); + /* Clear state */ memset(gs, 0x00, sizeof(struct gapk_state)); gs->in.rtp.fd = -1; @@ -574,47 +740,44 @@ int main(int argc, char *argv[]) return rv; /* Create processing queue */ - gs->pq = pq_create(); + gs->pq = osmo_gapk_pq_create("main"); if (!gs->pq) { rv = -ENOMEM; - fprintf(stderr, "Error creating processing queue\n"); + LOGP(DAPP, LOGL_ERROR, "Error creating processing queue\n"); goto error; } /* Open source / destination files */ rv = files_open(gs); if (rv) { - fprintf(stderr, "Error opening file(s)\n"); + LOGP(DAPP, LOGL_ERROR, "Error opening file(s)\n"); goto error; } /* Handle input/output headers */ rv = handle_headers(gs); if (rv) { - fprintf(stderr, "Error handling header(s)\n"); + LOGP(DAPP, LOGL_ERROR, "Error handling header(s)\n"); goto error; } /* Make processing chain */ rv = make_processing_chain(gs); if (rv) { - fprintf(stderr, "Error making processing chain\n"); + LOGP(DAPP, LOGL_ERROR, "Error making processing chain\n"); goto error; } signal(SIGINT, &signal_handler); + signal(SIGABRT, &signal_handler); + signal(SIGUSR1, &signal_handler); + signal(SIGUSR2, &signal_handler); /* Run the processing queue */ + LOGP(DAPP, LOGL_NOTICE, "Init complete, starting processing queue...\n"); rv = run(gs); error: - /* Close source / destination files */ - files_close(gs); - - /* Release processing queue */ - pq_destroy(gs->pq); - - benchmark_dump(); - + app_shutdown(); return rv; } diff --git a/src/benchmark.c b/src/benchmark.c index 91e2ce5..b3bb60b 100644 --- a/src/benchmark.c +++ b/src/benchmark.c @@ -17,41 +17,73 @@ * (C) 2014 Harald Welte */ -#include +#include +#include -#include +#include +#include -struct benchmark_cycles codec_cycles[_CODEC_MAX]; +/* Internal root talloc context */ +extern TALLOC_CTX *gapk_root_ctx; -void benchmark_dump(void) +struct osmo_gapk_bench_cycles * + osmo_gapk_bench_codec[_CODEC_MAX] = { NULL }; + +int osmo_gapk_bench_enable(enum osmo_gapk_codec_type codec) +{ + struct osmo_gapk_bench_cycles *bench; + + /* Allocate zero-initialized memory */ + bench = talloc_zero(gapk_root_ctx, struct osmo_gapk_bench_cycles); + if (!bench) + return -ENOMEM; + + /* Set up pointer */ + osmo_gapk_bench_codec[codec] = bench; + + return 0; +} + +unsigned long long +osmo_gapk_bench_get_cycles(enum osmo_gapk_codec_type codec, int enc) +{ + struct osmo_gapk_bench_cycles *bench; + unsigned long long cycles = 0; + int i; + + /* Check if there are benchmark data */ + bench = osmo_gapk_bench_codec[codec]; + if (!bench) + return -EAGAIN; + + if (enc) { + for (i = 0; i < bench->enc_used; i++) + cycles += bench->enc[i]; + } else { + for (i = 0; i < bench->dec_used; i++) + cycles += bench->dec[i]; + } + + return cycles; +} + +unsigned int +osmo_gapk_bench_get_frames(enum osmo_gapk_codec_type codec, int enc) +{ + struct osmo_gapk_bench_cycles *bench; + + /* Check if there are benchmark data */ + bench = osmo_gapk_bench_codec[codec]; + if (!bench) + return -EAGAIN; + + return enc ? bench->enc_used : bench->dec_used; +} + +void osmo_gapk_bench_free(void) { int i; - for (i = 0; i < _CODEC_MAX; i++) { - struct benchmark_cycles *bc = &codec_cycles[i]; - unsigned long long total; - int j; - - if (bc->enc_used) { - total = 0; - for (j = 0; j < bc->enc_used; j++) - total += bc->enc[j]; - - fprintf(stderr, - "Codec %u (ENC): %llu cycles for %u frames => " - "%llu cycles/frame\n", i, total, bc->enc_used, - total / bc->enc_used); - } - - if (bc->dec_used) { - total = 0; - for (j = 0; j < bc->dec_used; j++) - total += bc->dec[j]; - - fprintf(stderr, - "Codec %u (DEC): %llu cycles for %u frames => " - "%llu cycles/frame\n", i, total, bc->dec_used, - total / bc->dec_used); - } - } + for (i = 0; i < _CODEC_MAX; i++) + talloc_free(osmo_gapk_bench_codec[i]); } diff --git a/src/codec_amr.c b/src/codec_amr.c index ca614ed..614b744 100644 --- a/src/codec_amr.c +++ b/src/codec_amr.c @@ -18,20 +18,23 @@ * along with gapk. If not, see . */ -#include -#include +#include +#include +#include #include "config.h" #ifdef HAVE_OPENCORE_AMRNB -#include -#include +#include #include #include +/* Internal root talloc context */ +extern TALLOC_CTX *gapk_root_ctx; + struct codec_amr_state { void *encoder; void *decoder; @@ -43,7 +46,7 @@ codec_amr_init(void) { struct codec_amr_state *st; - st = calloc(1, sizeof(*st)); + st = talloc_zero(gapk_root_ctx, struct codec_amr_state); if (!st) return NULL; @@ -61,6 +64,8 @@ codec_amr_exit(void *state) Decoder_Interface_exit(st->decoder); Encoder_Interface_exit(st->encoder); + talloc_free(st); + return; } @@ -103,7 +108,7 @@ codec_amr_decode(void *state, uint8_t *pcm, const uint8_t *cod, unsigned int cod #endif /* HAVE_OPENCORE_AMRNB */ -const struct codec_desc codec_amr_desc = { +const struct osmo_gapk_codec_desc codec_amr_desc = { .type = CODEC_AMR, .name = "amr", .description = "GSM 26.071 Adaptive Multi Rate codec", diff --git a/src/codec_efr.c b/src/codec_efr.c index 339172a..9804bd9 100644 --- a/src/codec_efr.c +++ b/src/codec_efr.c @@ -17,20 +17,23 @@ * along with gapk. If not, see . */ -#include -#include +#include +#include +#include #include "config.h" #ifdef HAVE_OPENCORE_AMRNB -#include +#include #include #include #include +/* Internal root talloc context */ +extern TALLOC_CTX *gapk_root_ctx; struct codec_efr_state { void *encoder; @@ -43,7 +46,7 @@ codec_efr_init(void) { struct codec_efr_state *st; - st = calloc(1, sizeof(*st)); + st = talloc_zero(gapk_root_ctx, struct codec_efr_state); if (!st) return NULL; @@ -61,6 +64,8 @@ codec_efr_exit(void *state) Decoder_Interface_exit(st->decoder); Encoder_Interface_exit(st->encoder); + talloc_free(st); + return; } @@ -108,7 +113,7 @@ codec_efr_decode(void *state, uint8_t *pcm, const uint8_t *cod, unsigned int cod #endif /* HAVE_OPENCORE_AMRNB */ -const struct codec_desc codec_efr_desc = { +const struct osmo_gapk_codec_desc codec_efr_desc = { .type = CODEC_EFR, .name = "efr", .description = "GSM 06.60 Enhanced Full Rate codec", diff --git a/src/codec_fr.c b/src/codec_fr.c index 917f34b..eddbee6 100644 --- a/src/codec_fr.c +++ b/src/codec_fr.c @@ -19,8 +19,9 @@ #include -#include -#include +#include +#include +#include #include "config.h" @@ -83,7 +84,7 @@ codec_fr_decode(void *state, uint8_t *pcm, const uint8_t *cod, unsigned int cod_ #endif /* HAVE_LIBGSM */ -const struct codec_desc codec_fr_desc = { +const struct osmo_gapk_codec_desc codec_fr_desc = { .type = CODEC_FR, .name = "fr", .description = "GSM 06.10 Full Rate codec (classic gsm codec)", diff --git a/src/codec_hr.c b/src/codec_hr.c index b3247e0..ef66f57 100644 --- a/src/codec_hr.c +++ b/src/codec_hr.c @@ -19,8 +19,9 @@ #include -#include -#include +#include +#include +#include #include "config.h" @@ -73,7 +74,7 @@ codec_hr_decode(void *_state, uint8_t *pcm, const uint8_t *cod, unsigned int cod #endif /* HAVE_LIBGSMHR */ -const struct codec_desc codec_hr_desc = { +const struct osmo_gapk_codec_desc codec_hr_desc = { .type = CODEC_HR, .name = "hr", .description = "GSM 06.20 Half Rate codec", diff --git a/src/codec_pcm.c b/src/codec_pcm.c index 9fa8c1b..76cab34 100644 --- a/src/codec_pcm.c +++ b/src/codec_pcm.c @@ -17,9 +17,9 @@ * along with gapk. If not, see . */ -#include +#include -const struct codec_desc codec_pcm_desc = { +const struct osmo_gapk_codec_desc codec_pcm_desc = { .type = CODEC_PCM, .name = "pcm", .description = "Raw PCM signed 16 bits samples", diff --git a/src/codecs.c b/src/codecs.c index 623e80c..be5a112 100644 --- a/src/codecs.c +++ b/src/codecs.c @@ -19,18 +19,18 @@ #include /* for NULL */ -#include +#include /* Extern codec descriptors */ -extern const struct codec_desc codec_pcm_desc; -extern const struct codec_desc codec_hr_desc; -extern const struct codec_desc codec_fr_desc; -extern const struct codec_desc codec_efr_desc; -extern const struct codec_desc codec_amr_desc; +extern const struct osmo_gapk_codec_desc codec_pcm_desc; +extern const struct osmo_gapk_codec_desc codec_hr_desc; +extern const struct osmo_gapk_codec_desc codec_fr_desc; +extern const struct osmo_gapk_codec_desc codec_efr_desc; +extern const struct osmo_gapk_codec_desc codec_amr_desc; -const struct codec_desc * -codec_get_from_type(enum codec_type type) +const struct osmo_gapk_codec_desc * +osmo_gapk_codec_get_from_type(enum osmo_gapk_codec_type type) { switch (type) { case CODEC_PCM: return &codec_pcm_desc; diff --git a/src/common.c b/src/common.c new file mode 100644 index 0000000..c7c5c32 --- /dev/null +++ b/src/common.c @@ -0,0 +1,38 @@ +/* + * This file is part of gapk (GSM Audio Pocket Knife). + * + * (C) 2017 by Vadim Yanitskiy + * + * gapk 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 3 of the License, or + * (at your option) any later version. + * + * gapk 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 gapk. If not, see . + */ + +#include + +/* Internal root talloc context */ +TALLOC_CTX *gapk_root_ctx = NULL; + +void osmo_gapk_set_talloc_ctx(void *ctx) +{ + gapk_root_ctx = ctx; +} + +/* Internal GAPK logging */ +int gapk_log_init_complete = 0; +int gapk_log_subsys; + +void osmo_gapk_log_init(int subsys) +{ + gapk_log_subsys = subsys; + gapk_log_init_complete = 1; +} diff --git a/src/fmt_amr.c b/src/fmt_amr.c index e28024c..3694f8a 100644 --- a/src/fmt_amr.c +++ b/src/fmt_amr.c @@ -22,9 +22,9 @@ #include -#include -#include -#include +#include +#include +#include #define EFR_LEN 32 @@ -70,7 +70,7 @@ amr_efr_to_canon(uint8_t *dst, const uint8_t *src, unsigned int src_len) return EFR_CANON_LEN; } -const struct format_desc fmt_amr_efr = { +const struct osmo_gapk_format_desc fmt_amr_efr = { .type = FMT_AMR_EFR, .codec_type = CODEC_EFR, .name = "amr-efr", diff --git a/src/fmt_amr_opencore.c b/src/fmt_amr_opencore.c index 3fa547b..640595e 100644 --- a/src/fmt_amr_opencore.c +++ b/src/fmt_amr_opencore.c @@ -21,9 +21,9 @@ #include #include -#include -#include -#include +#include +#include +#include static int amr_opencore_from_canon(uint8_t *dst, const uint8_t *src, unsigned int src_len) @@ -39,7 +39,7 @@ amr_opencore_to_canon(uint8_t *dst, const uint8_t *src, unsigned int src_len) return src_len; } -const struct format_desc fmt_amr_opencore = { +const struct osmo_gapk_format_desc fmt_amr_opencore = { .type = FMT_AMR_OPENCORE, .codec_type = CODEC_AMR, .name = "amr-opencore", diff --git a/src/fmt_gsm.c b/src/fmt_gsm.c index 27701f0..e97ac9b 100644 --- a/src/fmt_gsm.c +++ b/src/fmt_gsm.c @@ -18,8 +18,8 @@ */ #include -#include -#include +#include +#include #define GSM_LEN 33 #define GSM_MAGIC 0xd @@ -54,7 +54,7 @@ gsm_to_canon(uint8_t *dst, const uint8_t *src, unsigned int src_len) return FR_CANON_LEN; } -const struct format_desc fmt_gsm = { +const struct osmo_gapk_format_desc fmt_gsm = { .type = FMT_GSM, .codec_type = CODEC_FR, .name = "gsm", diff --git a/src/fmt_hr_ref.c b/src/fmt_hr_ref.c index d64918b..0394b76 100644 --- a/src/fmt_hr_ref.c +++ b/src/fmt_hr_ref.c @@ -22,9 +22,9 @@ #include -#include -#include -#include +#include +#include +#include static const int params_unvoiced[] = { 5, /* R0 */ @@ -191,7 +191,7 @@ hr_ref_enc_to_canon(uint8_t *dst, const uint8_t *src, unsigned int src_len) } -const struct format_desc fmt_hr_ref_dec = { +const struct osmo_gapk_format_desc fmt_hr_ref_dec = { .type = FMT_HR_REF_DEC, .codec_type = CODEC_HR, .name = "hr-ref-dec", @@ -202,7 +202,7 @@ const struct format_desc fmt_hr_ref_dec = { .conv_to_canon = hr_ref_dec_to_canon, }; -const struct format_desc fmt_hr_ref_enc = { +const struct osmo_gapk_format_desc fmt_hr_ref_enc = { .type = FMT_HR_REF_ENC, .codec_type = CODEC_HR, .name = "hr-ref-enc", diff --git a/src/fmt_racal.c b/src/fmt_racal.c index 1dbc61d..7bb53dd 100644 --- a/src/fmt_racal.c +++ b/src/fmt_racal.c @@ -22,9 +22,9 @@ #include -#include -#include -#include +#include +#include +#include #define RACAL_HR_LEN 14 #define RACAL_FR_LEN 33 @@ -76,7 +76,7 @@ racal_hr_to_canon(uint8_t *dst, const uint8_t *src, unsigned int src_len) return RACAL_HR_LEN; } -const struct format_desc fmt_racal_hr = { +const struct osmo_gapk_format_desc fmt_racal_hr = { .type = FMT_RACAL_HR, .codec_type = CODEC_HR, .name = "racal-hr", @@ -124,7 +124,7 @@ racal_fr_to_canon(uint8_t *dst, const uint8_t *src, unsigned int src_len) return FR_CANON_LEN; } -const struct format_desc fmt_racal_fr = { +const struct osmo_gapk_format_desc fmt_racal_fr = { .type = FMT_RACAL_FR, .codec_type = CODEC_FR, .name = "racal-fr", @@ -166,7 +166,7 @@ racal_efr_to_canon(uint8_t *dst, const uint8_t *src, unsigned int src_len) return EFR_CANON_LEN; } -const struct format_desc fmt_racal_efr = { +const struct osmo_gapk_format_desc fmt_racal_efr = { .type = FMT_RACAL_EFR, .codec_type = CODEC_EFR, .name = "racal-efr", diff --git a/src/fmt_rawpcm.c b/src/fmt_rawpcm.c index 207708c..7b45060 100644 --- a/src/fmt_rawpcm.c +++ b/src/fmt_rawpcm.c @@ -19,8 +19,8 @@ #include -#include -#include +#include +#include static int rawpcm_s16le_from_canon(uint8_t *dst, const uint8_t *src, unsigned int src_len) @@ -53,7 +53,7 @@ rawpcm_s16le_to_canon(uint8_t *dst, const uint8_t *src, unsigned int src_len) return PCM_CANON_LEN; } -const struct format_desc fmt_rawpcm_s16le = { +const struct osmo_gapk_format_desc fmt_rawpcm_s16le = { .type = FMT_RAWPCM_S16LE, .codec_type = CODEC_PCM, .name = "rawpcm-s16le", diff --git a/src/fmt_rtp_amr.c b/src/fmt_rtp_amr.c index 1d5357d..a6a3c9f 100644 --- a/src/fmt_rtp_amr.c +++ b/src/fmt_rtp_amr.c @@ -23,9 +23,9 @@ #include -#include -#include -#include +#include +#include +#include /* conversion function: RTP payload -> canonical format */ static int @@ -48,7 +48,7 @@ rtp_amr_to_canon(uint8_t *dst, const uint8_t *src, unsigned int src_len) return src_len-1; } -const struct format_desc fmt_rtp_amr = { +const struct osmo_gapk_format_desc fmt_rtp_amr = { .type = FMT_RTP_AMR, .codec_type = CODEC_AMR, .name = "rtp-amr", diff --git a/src/fmt_rtp_efr.c b/src/fmt_rtp_efr.c index 0132e32..accfd4f 100644 --- a/src/fmt_rtp_efr.c +++ b/src/fmt_rtp_efr.c @@ -23,9 +23,9 @@ #include -#include -#include -#include +#include +#include +#include #define EFR_LEN 31 #define EFR_MAGIC 0xc @@ -61,7 +61,7 @@ rtp_efr_to_canon(uint8_t *dst, const uint8_t *src, unsigned int src_len) return EFR_CANON_LEN; } -const struct format_desc fmt_rtp_efr = { +const struct osmo_gapk_format_desc fmt_rtp_efr = { .type = FMT_RTP_EFR, .codec_type = CODEC_EFR, .name = "rtp-efr", diff --git a/src/fmt_rtp_hr_etsi.c b/src/fmt_rtp_hr_etsi.c index fe6728a..9ed9b22 100644 --- a/src/fmt_rtp_hr_etsi.c +++ b/src/fmt_rtp_hr_etsi.c @@ -22,9 +22,9 @@ #include #include -#include -#include -#include +#include +#include +#include /* conversion function: RTP payload -> canonical format */ static int @@ -48,7 +48,7 @@ rtp_hr_etsi_to_canon(uint8_t *dst, const uint8_t *src, unsigned int src_len) return HR_CANON_LEN; } -const struct format_desc fmt_rtp_hr_etsi = { +const struct osmo_gapk_format_desc fmt_rtp_hr_etsi = { .type = FMT_RTP_HR_ETSI, .codec_type = CODEC_HR, .name = "rtp-hr-etsi", diff --git a/src/fmt_rtp_hr_ietf.c b/src/fmt_rtp_hr_ietf.c index 3e8e6a3..4ee548f 100644 --- a/src/fmt_rtp_hr_ietf.c +++ b/src/fmt_rtp_hr_ietf.c @@ -22,9 +22,9 @@ #include #include -#include -#include -#include +#include +#include +#include #define HR_LEN (HR_CANON_LEN+1) @@ -68,7 +68,7 @@ rtp_hr_ietf_to_canon(uint8_t *dst, const uint8_t *src, unsigned int src_len) return HR_CANON_LEN; } -const struct format_desc fmt_rtp_hr_ietf = { +const struct osmo_gapk_format_desc fmt_rtp_hr_ietf = { .type = FMT_RTP_HR_IETF, .codec_type = CODEC_HR, .name = "rtp-hr-ietf", diff --git a/src/fmt_ti.c b/src/fmt_ti.c index 582bff4..d7a1ec7 100644 --- a/src/fmt_ti.c +++ b/src/fmt_ti.c @@ -30,9 +30,9 @@ #include -#include -#include -#include +#include +#include +#include #define TI_LEN 33 @@ -84,7 +84,7 @@ ti_hr_to_canon(uint8_t *dst, const uint8_t *src, unsigned int src_len) return HR_CANON_LEN; } -const struct format_desc fmt_ti_hr = { +const struct osmo_gapk_format_desc fmt_ti_hr = { .type = FMT_TI_HR, .codec_type = CODEC_HR, .name = "ti-hr", @@ -132,7 +132,7 @@ ti_fr_to_canon(uint8_t *dst, const uint8_t *src, unsigned int src_len) return FR_CANON_LEN; } -const struct format_desc fmt_ti_fr = { +const struct osmo_gapk_format_desc fmt_ti_fr = { .type = FMT_TI_FR, .codec_type = CODEC_FR, .name = "ti-fr", @@ -224,7 +224,7 @@ ti_efr_to_canon(uint8_t *dst, const uint8_t *src, unsigned int src_len) return EFR_CANON_LEN; } -const struct format_desc fmt_ti_efr = { +const struct osmo_gapk_format_desc fmt_ti_efr = { .type = FMT_TI_EFR, .codec_type = CODEC_EFR, .name = "ti-efr", diff --git a/src/formats.c b/src/formats.c index 60182fe..835af6b 100644 --- a/src/formats.c +++ b/src/formats.c @@ -20,27 +20,27 @@ #include /* for NULL */ #include -#include +#include /* Extern format descriptors */ -extern const struct format_desc fmt_amr_efr; -extern const struct format_desc fmt_gsm; -extern const struct format_desc fmt_hr_ref_dec; -extern const struct format_desc fmt_hr_ref_enc; -extern const struct format_desc fmt_racal_hr; -extern const struct format_desc fmt_racal_fr; -extern const struct format_desc fmt_racal_efr; -extern const struct format_desc fmt_rawpcm_s16le; -extern const struct format_desc fmt_ti_hr; -extern const struct format_desc fmt_ti_fr; -extern const struct format_desc fmt_ti_efr; -extern const struct format_desc fmt_amr_opencore; -extern const struct format_desc fmt_rtp_amr; -extern const struct format_desc fmt_rtp_efr; -extern const struct format_desc fmt_rtp_hr_etsi; -extern const struct format_desc fmt_rtp_hr_ietf; +extern const struct osmo_gapk_format_desc fmt_amr_efr; +extern const struct osmo_gapk_format_desc fmt_gsm; +extern const struct osmo_gapk_format_desc fmt_hr_ref_dec; +extern const struct osmo_gapk_format_desc fmt_hr_ref_enc; +extern const struct osmo_gapk_format_desc fmt_racal_hr; +extern const struct osmo_gapk_format_desc fmt_racal_fr; +extern const struct osmo_gapk_format_desc fmt_racal_efr; +extern const struct osmo_gapk_format_desc fmt_rawpcm_s16le; +extern const struct osmo_gapk_format_desc fmt_ti_hr; +extern const struct osmo_gapk_format_desc fmt_ti_fr; +extern const struct osmo_gapk_format_desc fmt_ti_efr; +extern const struct osmo_gapk_format_desc fmt_amr_opencore; +extern const struct osmo_gapk_format_desc fmt_rtp_amr; +extern const struct osmo_gapk_format_desc fmt_rtp_efr; +extern const struct osmo_gapk_format_desc fmt_rtp_hr_etsi; +extern const struct osmo_gapk_format_desc fmt_rtp_hr_ietf; -static const struct format_desc *supported_formats[_FMT_MAX] = { +static const struct osmo_gapk_format_desc *supported_formats[_FMT_MAX] = { [FMT_INVALID] = NULL, [FMT_AMR_EFR] = &fmt_amr_efr, [FMT_GSM] = &fmt_gsm, @@ -61,20 +61,20 @@ static const struct format_desc *supported_formats[_FMT_MAX] = { }; -const struct format_desc * -fmt_get_from_type(enum format_type type) +const struct osmo_gapk_format_desc * +osmo_gapk_fmt_get_from_type(enum osmo_gapk_format_type type) { if (type <= FMT_INVALID || type >= _FMT_MAX) return NULL; return supported_formats[type]; } -const struct format_desc * -fmt_get_from_name(const char *name) +const struct osmo_gapk_format_desc * +osmo_gapk_fmt_get_from_name(const char *name) { int i; for (i=FMT_INVALID+1; i<_FMT_MAX; i++) { - const struct format_desc *fmt = supported_formats[i]; + const struct osmo_gapk_format_desc *fmt = supported_formats[i]; if (!fmt) continue; if (!strcmp(fmt->name, name)) diff --git a/src/libosmogapk.map b/src/libosmogapk.map new file mode 100644 index 0000000..6cd817e --- /dev/null +++ b/src/libosmogapk.map @@ -0,0 +1,4 @@ +LIBOSMOGAPK_1.0 { +global: osmo_gapk_*; +local: *; +}; diff --git a/src/pq_alsa.c b/src/pq_alsa.c index cad76ca..3550221 100644 --- a/src/pq_alsa.c +++ b/src/pq_alsa.c @@ -20,12 +20,12 @@ #include #include -#include -#include +#include -#include -#include -#include +#include +#include +#include +#include #include "config.h" @@ -69,22 +69,30 @@ pq_cb_alsa_output(void *_state, uint8_t *out, const uint8_t *in, unsigned int in return rv == num_samples ? 0 : -1; } +static int +pq_cb_alsa_wait(void *_state) +{ + struct pq_state_alsa *state = _state; + return snd_pcm_avail_update(state->pcm_handle) > 0; +} + static void pq_cb_alsa_exit(void *_state) { struct pq_state_alsa *state = _state; snd_pcm_close(state->pcm_handle); + talloc_free(state); } static int -pq_queue_alsa_op(struct pq *pq, const char *alsa_dev, unsigned int blk_len, int in_out_n) +pq_queue_alsa_op(struct osmo_gapk_pq *pq, const char *alsa_dev, unsigned int blk_len, int in_out_n) { - struct pq_item *item; + struct osmo_gapk_pq_item *item; struct pq_state_alsa *state; snd_pcm_hw_params_t *hw_params; int rc = -1; - state = calloc(1, sizeof(struct pq_state_alsa)); + state = talloc_zero(pq, struct pq_state_alsa); if (!state) { rc = -ENOMEM; goto out_print; @@ -127,27 +135,37 @@ pq_queue_alsa_op(struct pq *pq, const char *alsa_dev, unsigned int blk_len, int snd_pcm_hw_params_free(hw_params); - item = pq_add_item(pq); + item = osmo_gapk_pq_add_item(pq); if (!item) { rc = -ENOMEM; goto out_close; } + item->type = in_out_n ? + OSMO_GAPK_ITEM_TYPE_SOURCE : OSMO_GAPK_ITEM_TYPE_SINK; + item->cat_name = in_out_n ? + OSMO_GAPK_CAT_NAME_SOURCE : OSMO_GAPK_CAT_NAME_SINK; + item->sub_name = "alsa"; + item->len_in = in_out_n ? 0 : blk_len; item->len_out = in_out_n ? blk_len : 0; item->state = state; item->proc = in_out_n ? pq_cb_alsa_input : pq_cb_alsa_output; + item->wait = pq_cb_alsa_wait; item->exit = pq_cb_alsa_exit; + /* Change state's talloc context from pq to item */ + talloc_steal(item, state); + return 0; out_free_par: snd_pcm_hw_params_free(hw_params); out_close: snd_pcm_close(state->pcm_handle); - free(state); + talloc_free(state); out_print: - fprintf(stderr, "[!] Couldn't init ALSA device '%s': %s\n", + LOGPGAPK(LOGL_ERROR, "Couldn't init ALSA device '%s': %s\n", alsa_dev, snd_strerror(rc)); return rc; } @@ -160,9 +178,10 @@ out_print: * \param[in] blk_len block length to be read from device * \returns 0 on sucess; negative on error */ int -pq_queue_alsa_input(struct pq *pq, const char *hwdev, unsigned int blk_len) +osmo_gapk_pq_queue_alsa_input(struct osmo_gapk_pq *pq, const char *hwdev, unsigned int blk_len) { - fprintf(stderr, "[+] PQ: Adding ALSA input (dev='%s', blk_len=%u)\n", hwdev, blk_len); + LOGPGAPK(LOGL_DEBUG, "PQ '%s': Adding ALSA input " + "(dev='%s', blk_len=%u)\n", pq->name, hwdev, blk_len); return pq_queue_alsa_op(pq, hwdev, blk_len, 1); } @@ -173,9 +192,10 @@ pq_queue_alsa_input(struct pq *pq, const char *hwdev, unsigned int blk_len) * \param[in] blk_len block length to be written to device * \returns 0 on sucess; negative on error */ int -pq_queue_alsa_output(struct pq *pq, const char *hwdev, unsigned int blk_len) +osmo_gapk_pq_queue_alsa_output(struct osmo_gapk_pq *pq, const char *hwdev, unsigned int blk_len) { - fprintf(stderr, "[+] PQ: Adding ALSA output (dev='%s', blk_len=%u)\n", hwdev, blk_len); + LOGPGAPK(LOGL_DEBUG, "PQ '%s': Adding ALSA output " + "(dev='%s', blk_len=%u)\n", pq->name, hwdev, blk_len); return pq_queue_alsa_op(pq, hwdev, blk_len, 0); } diff --git a/src/pq_codec.c b/src/pq_codec.c index f1884fb..87afd82 100644 --- a/src/pq_codec.c +++ b/src/pq_codec.c @@ -20,9 +20,10 @@ #include #include -#include -#include -#include +#include +#include +#include +#include /*! Add a codecl to the processing queue @@ -31,14 +32,14 @@ * \param[in] encode (1) or decode (0) * \returns 0 on success; negative on error */ int -pq_queue_codec(struct pq *pq, const struct codec_desc *codec, int enc_dec_n) +osmo_gapk_pq_queue_codec(struct osmo_gapk_pq *pq, const struct osmo_gapk_codec_desc *codec, int enc_dec_n) { - const struct codec_desc *codec_pcm = codec_get_from_type(CODEC_PCM); - const struct format_desc *fmt; - struct pq_item *item; + const struct osmo_gapk_codec_desc *codec_pcm; + const struct osmo_gapk_format_desc *fmt; + struct osmo_gapk_pq_item *item; /* allocate a new item to the processing queue */ - item = pq_add_item(pq); + item = osmo_gapk_pq_add_item(pq); if (!item) return -ENOMEM; @@ -50,7 +51,8 @@ pq_queue_codec(struct pq *pq, const struct codec_desc *codec, int enc_dec_n) } if (enc_dec_n) { - fmt = fmt_get_from_type(codec->codec_enc_format_type); + codec_pcm = osmo_gapk_codec_get_from_type(CODEC_PCM); + fmt = osmo_gapk_fmt_get_from_type(codec->codec_enc_format_type); if (!fmt) return -EINVAL; @@ -58,7 +60,8 @@ pq_queue_codec(struct pq *pq, const struct codec_desc *codec, int enc_dec_n) item->len_out = fmt->frame_len; item->proc = codec->codec_encode; } else { - fmt = fmt_get_from_type(codec->codec_dec_format_type); + codec_pcm = osmo_gapk_codec_get_from_type(CODEC_PCM); + fmt = osmo_gapk_fmt_get_from_type(codec->codec_dec_format_type); if (!fmt) return -EINVAL; @@ -67,10 +70,17 @@ pq_queue_codec(struct pq *pq, const struct codec_desc *codec, int enc_dec_n) item->proc = codec->codec_decode; } + item->type = OSMO_GAPK_ITEM_TYPE_PROC; item->exit = codec->codec_exit; + item->wait = NULL; - fprintf(stderr, "[+] PQ: Adding Codec %s, %s format %s\n", codec->name, - enc_dec_n ? "encoding to" : "decoding from", fmt->name); + /* Meta information */ + item->cat_name = "codec"; + item->sub_name = codec->name; + + LOGPGAPK(LOGL_DEBUG, "PQ '%s': Adding codec %s, %s format %s\n", + pq->name, codec->name, enc_dec_n ? + "encoding to" : "decoding from", fmt->name); if (!item->proc) return -ENOTSUP; diff --git a/src/pq_file.c b/src/pq_file.c index 96f7b3f..22dc9cd 100644 --- a/src/pq_file.c +++ b/src/pq_file.c @@ -19,12 +19,12 @@ #include #include -#include -#include +#include -#include -#include -#include +#include +#include +#include +#include struct pq_state_file { @@ -56,34 +56,44 @@ pq_cb_file_output(void *_state, uint8_t *out, const uint8_t *in, unsigned int in static void pq_cb_file_exit(void *_state) { - free(_state); + talloc_free(_state); } static int -pq_queue_file_op(struct pq *pq, FILE *fh, unsigned int blk_len, int in_out_n) +pq_queue_file_op(struct osmo_gapk_pq *pq, FILE *fh, unsigned int blk_len, int in_out_n) { - struct pq_item *item; + struct osmo_gapk_pq_item *item; struct pq_state_file *state; - state = calloc(1, sizeof(struct pq_state_file)); + state = talloc_zero(pq, struct pq_state_file); if (!state) return -ENOMEM; state->fh = fh; state->blk_len = blk_len; - item = pq_add_item(pq); + item = osmo_gapk_pq_add_item(pq); if (!item) { - free(state); + talloc_free(state); return -ENOMEM; } + item->type = in_out_n ? + OSMO_GAPK_ITEM_TYPE_SOURCE : OSMO_GAPK_ITEM_TYPE_SINK; + item->cat_name = in_out_n ? + OSMO_GAPK_CAT_NAME_SOURCE : OSMO_GAPK_CAT_NAME_SINK; + item->sub_name = "file"; + item->len_in = in_out_n ? 0 : blk_len; item->len_out = in_out_n ? blk_len : 0; item->state = state; item->proc = in_out_n ? pq_cb_file_input : pq_cb_file_output; + item->wait = NULL; item->exit = pq_cb_file_exit; + /* Change state's talloc context from pq to item */ + talloc_steal(item, state); + return 0; } @@ -95,9 +105,10 @@ pq_queue_file_op(struct pq *pq, FILE *fh, unsigned int blk_len, int in_out_n) * \param[in] blk_len block length to be read from file * \returns 0 on sucess; negative on error */ int -pq_queue_file_input(struct pq *pq, FILE *src, unsigned int blk_len) +osmo_gapk_pq_queue_file_input(struct osmo_gapk_pq *pq, FILE *src, unsigned int blk_len) { - fprintf(stderr, "[+] PQ: Adding file input (blk_len=%u)\n", blk_len); + LOGPGAPK(LOGL_DEBUG, "PQ '%s': Adding file input (blk_len=%u)\n", + pq->name, blk_len); return pq_queue_file_op(pq, src, blk_len, 1); } @@ -108,8 +119,9 @@ pq_queue_file_input(struct pq *pq, FILE *src, unsigned int blk_len) * \param[in] blk_len block length to be written to file * \returns 0 on sucess; negative on error */ int -pq_queue_file_output(struct pq *pq, FILE *dst, unsigned int blk_len) +osmo_gapk_pq_queue_file_output(struct osmo_gapk_pq *pq, FILE *dst, unsigned int blk_len) { - fprintf(stderr, "[+] PQ: Adding file output (blk_len=%u)\n", blk_len); + LOGPGAPK(LOGL_DEBUG, "PQ '%s': Adding file output (blk_len=%u)\n", + pq->name, blk_len); return pq_queue_file_op(pq, dst, blk_len, 0); } diff --git a/src/pq_format.c b/src/pq_format.c index d6bc8af..2c4a2fd 100644 --- a/src/pq_format.c +++ b/src/pq_format.c @@ -20,15 +20,16 @@ #include #include -#include -#include -#include +#include +#include +#include +#include static int pq_cb_fmt_convert(void *_state, uint8_t *out, const uint8_t *in, unsigned int in_len) { - fmt_conv_cb_t f = _state; + osmo_gapk_fmt_conv_cb_t f = _state; return f(out, in, in_len); } @@ -37,36 +38,44 @@ pq_cb_fmt_convert(void *_state, uint8_t *out, const uint8_t *in, unsigned int in * \param[in] fmt Format description for conversion * \param[in] to_from_n convert to (0) or from (1) specified format */ int -pq_queue_fmt_convert(struct pq *pq, const struct format_desc *fmt, int to_from_n) +osmo_gapk_pq_queue_fmt_convert(struct osmo_gapk_pq *pq, const struct osmo_gapk_format_desc *fmt, int to_from_n) { - struct pq_item *item; - const struct codec_desc *codec = codec_get_from_type(fmt->codec_type); + const struct osmo_gapk_codec_desc *codec; + struct osmo_gapk_pq_item *item; + codec = osmo_gapk_codec_get_from_type(fmt->codec_type); if (!codec) { - fprintf(stderr, "[!] Cannot determine codec from format %s\n", fmt->name); + LOGPGAPK(LOGL_ERROR, "Cannot determine codec from " + "format %s\n", fmt->name); return -EINVAL; } - item = pq_add_item(pq); + item = osmo_gapk_pq_add_item(pq); if (!item) return -ENOMEM; if (to_from_n) { - fprintf(stderr, "[+] PQ: Adding conversion from canon to %s (for codec %s)\n", - fmt->name, codec->name); + LOGPGAPK(LOGL_DEBUG, "PQ '%s': Adding conversion from canon " + "to %s (for codec %s)\n", pq->name, fmt->name, codec->name); item->len_in = codec->canon_frame_len; item->len_out = fmt->frame_len; item->state = fmt->conv_from_canon; } else { - fprintf(stderr, "[+] PQ: Adding conversion from %s to canon (for codec %s)\n", - fmt->name, codec->name); + LOGPGAPK(LOGL_DEBUG, "PQ '%s': Adding conversion from %s " + "to canon (for codec %s)\n", pq->name, fmt->name, codec->name); item->len_in = fmt->frame_len; item->len_out = codec->canon_frame_len; item->state = fmt->conv_to_canon; } + item->type = OSMO_GAPK_ITEM_TYPE_PROC; item->proc = pq_cb_fmt_convert; + item->wait = NULL; + + /* Meta information */ + item->cat_name = "format"; + item->sub_name = fmt->name; return 0; } diff --git a/src/pq_rtp.c b/src/pq_rtp.c index f5603de..a50013a 100644 --- a/src/pq_rtp.c +++ b/src/pq_rtp.c @@ -20,16 +20,16 @@ #include #include -#include -#include #include #include +#include #include -#include -#include -#include +#include +#include +#include +#include #ifndef __BYTE_ORDER # ifdef __APPLE__ @@ -86,7 +86,8 @@ struct pq_state_rtp { uint32_t ssrc; }; -#define rtp_err(x, args...) fprintf(stderr, "[!] %s():" x, __func__, ## args) +#define rtp_err(err_msg, args...) \ + LOGPGAPK(LOGL_ERROR, "%s():" err_msg, __func__, ## args) static int pq_cb_rtp_input(void *_state, uint8_t *out, const uint8_t *in, unsigned int in_len) @@ -188,16 +189,16 @@ pq_cb_rtp_output(void *_state, uint8_t *out, const uint8_t *in, unsigned int in_ static void pq_cb_rtp_exit(void *_state) { - free(_state); + talloc_free(_state); } static int -pq_queue_rtp_op(struct pq *pq, int udp_fd, unsigned int blk_len, int in_out_n) +pq_queue_rtp_op(struct osmo_gapk_pq *pq, int udp_fd, unsigned int blk_len, int in_out_n) { - struct pq_item *item; + struct osmo_gapk_pq_item *item; struct pq_state_rtp *state; - state = calloc(1, sizeof(struct pq_state_rtp)); + state = talloc_zero(pq, struct pq_state_rtp); if (!state) return -ENOMEM; @@ -217,18 +218,28 @@ pq_queue_rtp_op(struct pq *pq, int udp_fd, unsigned int blk_len, int in_out_n) state->payload_type = RTP_PT_GSM_FULL; } - item = pq_add_item(pq); + item = osmo_gapk_pq_add_item(pq); if (!item) { - free(state); + talloc_free(state); return -ENOMEM; } + item->type = in_out_n ? + OSMO_GAPK_ITEM_TYPE_SOURCE : OSMO_GAPK_ITEM_TYPE_SINK; + item->cat_name = in_out_n ? + OSMO_GAPK_CAT_NAME_SOURCE : OSMO_GAPK_CAT_NAME_SINK; + item->sub_name = "rtp"; + item->len_in = in_out_n ? 0 : blk_len; item->len_out = in_out_n ? blk_len : 0; item->state = state; item->proc = in_out_n ? pq_cb_rtp_input : pq_cb_rtp_output; + item->wait = NULL; item->exit = pq_cb_rtp_exit; + /* Change state's talloc context from pq to item */ + talloc_steal(item, state); + return 0; } @@ -239,9 +250,10 @@ pq_queue_rtp_op(struct pq *pq, int udp_fd, unsigned int blk_len, int in_out_n) * \param[in] udp_fd UDP file descriptor for the RTP input * \param[in] blk_len Block Length to read from RTP */ int -pq_queue_rtp_input(struct pq *pq, int udp_fd, unsigned int blk_len) +osmo_gapk_pq_queue_rtp_input(struct osmo_gapk_pq *pq, int udp_fd, unsigned int blk_len) { - fprintf(stderr, "[+] PQ: Adding RTP input (blk_len=%u)\n", blk_len); + LOGPGAPK(LOGL_DEBUG, "PQ '%s': Adding RTP input (blk_len=%u)\n", + pq->name, blk_len); return pq_queue_rtp_op(pq, udp_fd, blk_len, 1); } @@ -251,8 +263,9 @@ pq_queue_rtp_input(struct pq *pq, int udp_fd, unsigned int blk_len) * \param[in] udp_fd UDP file descriptor for the RTP output * \param[in] blk_len Block Length to read from RTP */ int -pq_queue_rtp_output(struct pq *pq, int udp_fd, unsigned int blk_len) +osmo_gapk_pq_queue_rtp_output(struct osmo_gapk_pq *pq, int udp_fd, unsigned int blk_len) { - fprintf(stderr, "[+] PQ: Adding RTP output (blk_len=%u)\n", blk_len); + LOGPGAPK(LOGL_DEBUG, "PQ '%s': Adding RTP output (blk_len=%u)\n", + pq->name, blk_len); return pq_queue_rtp_op(pq, udp_fd, blk_len, 0); } diff --git a/src/procqueue.c b/src/procqueue.c index 01ad581..78dee4d 100644 --- a/src/procqueue.c +++ b/src/procqueue.c @@ -20,108 +20,164 @@ #include #include #include +#include -#include +#include -#define VAR_BUF_SIZE 320 -#define MAX_PQ_ITEMS 8 - -struct pq { - int n_items; - struct pq_item* items[MAX_PQ_ITEMS]; - void * buffers[MAX_PQ_ITEMS+1]; -}; +#include +#include +/* Internal root talloc context */ +extern TALLOC_CTX *gapk_root_ctx; /* crate a new (empty) processing queue */ -struct pq * -pq_create(void) +struct osmo_gapk_pq * +osmo_gapk_pq_create(const char *name) { - return (struct pq *) calloc(1, sizeof(struct pq)); + struct osmo_gapk_pq *pq; + + /* Allocate memory for a new processing queue */ + pq = talloc_zero(gapk_root_ctx, struct osmo_gapk_pq); + if (!pq) + return NULL; + + if (name != NULL) { + /* Rename talloc context */ + talloc_set_name(pq, "struct osmo_gapk_pq '%s'", name); + /* Set queue name */ + pq->name = name; + } + + /* Init its list of items */ + INIT_LLIST_HEAD(&pq->items); + + return pq; } /*! destroy a processing queue, calls exit() callback of each item * \param[in] pq Processing Queue to be destroyed */ void -pq_destroy(struct pq *pq) +osmo_gapk_pq_destroy(struct osmo_gapk_pq *pq) { - int i; + struct osmo_gapk_pq_item *item, *item_next; if (!pq) return; - for (i=0; in_items; i++) { - if (!pq->items[i]) - continue; - if (pq->items[i]->exit) - pq->items[i]->exit(pq->items[i]->state); - free(pq->items[i]); + /* Iterate over all items in queue */ + llist_for_each_entry_safe(item, item_next, &pq->items, list) { + /* Free output buffer memory */ + talloc_free(item->buf); + + /* Call exit handler if preset */ + if (item->exit) + item->exit(item->state); + + /* Delete an item from list */ + llist_del(&item->list); + talloc_free(item); } - for (i=0; in_items-1; i++) - free(pq->buffers[i]); /* free is NULL safe */ - - free(pq); + talloc_free(pq); } /*! allocate + add an item to a processing queue; return new item * \param[in] pq Processing Queue to which item is added * \returns new PQ item; NULL on error */ -struct pq_item * -pq_add_item(struct pq *pq) +struct osmo_gapk_pq_item * +osmo_gapk_pq_add_item(struct osmo_gapk_pq *pq) { - struct pq_item *item; + struct osmo_gapk_pq_item *item; - if (pq->n_items == MAX_PQ_ITEMS) { - fprintf(stderr, "[!] Processing Queue cannot handle more than %u items\n", - MAX_PQ_ITEMS); - return NULL; - } - - item = calloc(1, sizeof(struct pq_item)); + /* Allocate memory for a new item */ + item = talloc_zero(pq, struct osmo_gapk_pq_item); if (!item) return NULL; - pq->items[pq->n_items++] = item; + /* Add one to the end of a queue */ + llist_add_tail(&item->list, &pq->items); + + /* Increase the items count */ + pq->n_items++; return item; } -/*! prepare a processing queue; allocates buffers; checks lengths +/*! check a processing queue; make sure I/O data lengths are equal + * \param[in] pq Make sure both source and sink are preset + * \param[in] strict Processing Queue to be checked + * \returns 0 on succcess; negative on error */ +int +osmo_gapk_pq_check(struct osmo_gapk_pq *pq, int strict) +{ + struct osmo_gapk_pq_item *item_prev = NULL; + struct osmo_gapk_pq_item *item; + + /* Make sure I/O data lengths are equal */ + llist_for_each_entry(item, &pq->items, list) { + if (item_prev && item->len_in) { + if (item->len_in != item_prev->len_out) { + LOGPGAPK(LOGL_ERROR, "PQ '%s': item '%s/%s' requires " + "input size %u, but previous '%s/%s' has %u\n", + pq->name, item->cat_name, item->sub_name, + item->len_in, item_prev->cat_name, + item_prev->sub_name, item_prev->len_out); + return -EINVAL; + } + } + + /* Save pointer to the previous item */ + item_prev = item; + } + + if (strict) { + /* Make sure the first item is a source */ + item = llist_first_entry(&pq->items, + struct osmo_gapk_pq_item, list); + if (item->type != OSMO_GAPK_ITEM_TYPE_SOURCE) + goto src_sink_err; + + /* Make sure the last item is a sink */ + item = llist_last_entry(&pq->items, + struct osmo_gapk_pq_item, list); + if (item->type != OSMO_GAPK_ITEM_TYPE_SINK) + goto src_sink_err; + } + + return 0; + +src_sink_err: + LOGPGAPK(LOGL_ERROR, "PQ '%s': the first item should be a source, " + "and the last one should be a sink\n", pq->name); + return -EINVAL; +} + +/*! prepare a processing queue; allocates buffers * \param[in] pq Processing Queue to be prepared * \returns 0 on succcess; negative on error */ int -pq_prepare(struct pq *pq) +osmo_gapk_pq_prepare(struct osmo_gapk_pq *pq) { - int i; - unsigned int len_prev; + struct osmo_gapk_pq_item *item; - len_prev = 0; - - for (i=0; in_items; i++) { - struct pq_item *item = pq->items[i]; - - if (item->len_in && item->len_in != len_prev) { - fprintf(stderr, "[!] PQ item requires input size %u, but previous output is %u\n", - item->len_in, len_prev); - return -EINVAL; - } - - if (i < (pq->n_items-1)) { + /* Iterate over all items in queue */ + llist_for_each_entry(item, &pq->items, list) { + /* The sink item doesn't require an output buffer */ + if (item->list.next != &pq->items) { unsigned int buf_size = item->len_out; - /* variable-length codec output, use maximum - * known buffer size */ + + /** + * Use maximum known buffer size + * for variable-length codec output + */ if (!buf_size) buf_size = VAR_BUF_SIZE; - pq->buffers[i] = malloc(buf_size); - if (!pq->buffers[i]) - return -ENOMEM; - } else{ - if (item->len_out) - return -EINVAL; - } - len_prev = item->len_out; + /* Allocate memory for an output buffer */ + item->buf = talloc_named_const(item, buf_size, ".buffer"); + if (!item->buf) + return -ENOMEM; + } } return 0; @@ -131,30 +187,54 @@ pq_prepare(struct pq *pq) * \param[in] pq Processing Queue to be executed * \returns 0 on success; negative on error (if any item returns negative) */ int -pq_execute(struct pq *pq) +osmo_gapk_pq_execute(struct osmo_gapk_pq *pq) { - int i; - void *buf_prev, *buf; - unsigned int len_prev; + struct osmo_gapk_pq_item *item; + unsigned int len_prev = 0; + uint8_t *buf_prev = NULL; + int rv; - buf_prev = NULL; - len_prev = 0; - - for (i=0; in_items; i++) { - int rv; - struct pq_item *item = pq->items[i]; - - buf = i < (pq->n_items-1) ? pq->buffers[i] : NULL; - - rv = item->proc(item->state, buf, buf_prev, len_prev); + /* Iterate over all items in queue */ + llist_for_each_entry(item, &pq->items, list) { + /* Call item's processing handler */ + rv = item->proc(item->state, item->buf, buf_prev, len_prev); if (rv < 0) { - fprintf(stderr, "[!] pq_execute(): abort, item returned %d\n", rv); + LOGPGAPK(LOGL_ERROR, "PQ '%s': execution aborted: " + "item '%s/%s' returned %d\n", pq->name, + item->cat_name, item->sub_name, rv); return rv; } - buf_prev = buf; + buf_prev = item->buf; len_prev = rv; } return 0; } + +char * +osmo_gapk_pq_describe(struct osmo_gapk_pq *pq) +{ + struct osmo_gapk_pq_item *item; + char *result = NULL; + int i = 0; + + /* Nothing to describe */ + if (!pq->n_items) + return NULL; + + /* Iterate over all items in queue */ + llist_for_each_entry(item, &pq->items, list) { + result = talloc_asprintf_append(result, "%s/%s%s", + item->cat_name, item->sub_name, + ++i < pq->n_items ? " -> " : ""); + } + + /* Change talloc context name */ + talloc_set_name_const(result, ".description"); + + /* Change parent talloc context to pq */ + talloc_steal(pq, result); + + return result; +} diff --git a/tests/Makefile.am b/tests/Makefile.am new file mode 100644 index 0000000..cda5c2d --- /dev/null +++ b/tests/Makefile.am @@ -0,0 +1,93 @@ +AM_CPPFLAGS = \ + $(all_includes) \ + -I$(top_builddir) \ + -I$(top_srcdir)/include \ + $(NULL) + +AM_CFLAGS = \ + -Wall \ + $(LIBOSMOCORE_CFLAGS) \ + $(LIBOSMOCODEC_CFLAGS) \ + $(NULL) + +check_PROGRAMS = \ + procqueue/pq_test \ + io/pq_file_test \ + io/pq_rtp_test \ + $(NULL) + +procqueue_pq_test_SOURCES = procqueue/pq_test.c +procqueue_pq_test_LDADD = \ + $(top_builddir)/src/libosmogapk.la \ + $(LIBOSMOCORE_LIBS) \ + $(LIBOSMOCODEC_LIBS) \ + $(TALLOC_LIBS) \ + $(NULL) + +io_pq_file_test_SOURCES = io/pq_file_test.c +io_pq_file_test_LDADD = \ + $(top_builddir)/src/libosmogapk.la \ + $(LIBOSMOCORE_LIBS) \ + $(LIBOSMOCODEC_LIBS) \ + $(TALLOC_LIBS) \ + $(NULL) + +io_pq_rtp_test_SOURCES = io/pq_rtp_test.c +io_pq_rtp_test_LDADD = \ + $(top_builddir)/src/libosmogapk.la \ + $(LIBOSMOCORE_LIBS) \ + $(LIBOSMOCODEC_LIBS) \ + $(TALLOC_LIBS) \ + $(NULL) + +# The `:;' works around a Bash 3.2 bug when the output is not writeable. +$(srcdir)/package.m4: $(top_srcdir)/configure.ac + :;{ \ + echo '# Signature of the current package.' && \ + echo 'm4_define([AT_PACKAGE_NAME],' && \ + echo ' [$(PACKAGE_NAME)])' && \ + echo 'm4_define([AT_PACKAGE_TARNAME],' && \ + echo ' [$(PACKAGE_TARNAME)])' && \ + echo 'm4_define([AT_PACKAGE_VERSION],' && \ + echo ' [$(PACKAGE_VERSION)])' && \ + echo 'm4_define([AT_PACKAGE_STRING],' && \ + echo ' [$(PACKAGE_STRING)])' && \ + echo 'm4_define([AT_PACKAGE_BUGREPORT],' && \ + echo ' [$(PACKAGE_BUGREPORT)])'; \ + echo 'm4_define([AT_PACKAGE_URL],' && \ + echo ' [$(PACKAGE_URL)])'; \ + } >'$(srcdir)/package.m4' + +EXTRA_DIST = \ + testsuite.at \ + $(TESTSUITE) \ + $(srcdir)/package.m4 \ + $(NULL) + +EXTRA_DIST += \ + procqueue/pq_test.ok \ + io/pq_file_test.ok \ + io/pq_rtp_test.ok \ + io/io_sample.txt \ + ref-files/* \ + $(NULL) + +DISTCLEANFILES = atconfig +TESTSUITE = $(srcdir)/testsuite + +check-local: atconfig $(TESTSUITE) + $(SHELL) '$(TESTSUITE)' $(TESTSUITEFLAGS) + +installcheck-local: atconfig $(TESTSUITE) + $(SHELL) '$(TESTSUITE)' AUTOTEST_PATH='$(bindir)' \ + $(TESTSUITEFLAGS) + +clean-local: + test ! -f '$(TESTSUITE)' || \ + $(SHELL) '$(TESTSUITE)' --clean + +AUTOM4TE = $(SHELL) $(top_srcdir)/missing --run autom4te +AUTOTEST = $(AUTOM4TE) --language=autotest +$(TESTSUITE): $(srcdir)/testsuite.at $(srcdir)/package.m4 + $(AUTOTEST) -I '$(srcdir)' -o $@.tmp $@.at + mv $@.tmp $@ diff --git a/test/common.sh b/tests/common.sh similarity index 57% rename from test/common.sh rename to tests/common.sh index 591be3a..5fded94 100644 --- a/test/common.sh +++ b/tests/common.sh @@ -1,13 +1,13 @@ # directory containing the reference files for comparing against REFDIR=./ref-files -if [ -f ../src/gapk ]; then - GAPK=../src/gapk -elif [ -f `which gapk` ]; then - GAPK=`which gapk` +if [ -f ../src/osmo-gapk ]; then + GAPK=../src/osmo-gapk +elif [ -f `which osmo-gapk` ]; then + GAPK=`which osmo-gapk` else exit 1 fi -echo Using gapk found at $GAPK +echo Using osmo-gapk found at $GAPK FORMATS="amr-efr gsm racal-hr racal-fr racal-efr ti-hr ti-fr ti-efr rtp-efr rtp-hr-etsi rtp-hr-ietf" diff --git a/tests/io/io_sample.txt b/tests/io/io_sample.txt new file mode 100644 index 0000000..707e019 --- /dev/null +++ b/tests/io/io_sample.txt @@ -0,0 +1 @@ +8217C8FB7675A95008F9089D883gapk diff --git a/tests/io/pq_file_test.c b/tests/io/pq_file_test.c new file mode 100644 index 0000000..6691db3 --- /dev/null +++ b/tests/io/pq_file_test.c @@ -0,0 +1,149 @@ +/* + * This file is part of GAPK (GSM Audio Pocket Knife). + * + * (C) 2017 by Vadim Yanitskiy + * + * GAPK 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 3 of the License, or + * (at your option) any later version. + * + * GAPK 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 GAPK. If not, see . + */ + +#include +#include +#include +#include + +#include +#include + +/** + * This test is intended to check the file source / sink + * operability. To do that, the following processing chain + * is being composed: + * + * source/file -> proc/dummy -> sink/file (stdout) + * + * The source item opens the sample file named 'io_sample.txt' + * for reading. The next processing item simply converts all + * uppercase latters to the lowercase. The last one writes + * the result to stdout. + * + * This processing cycle is being repeated several times + * with different block length values. + */ + +static void talloc_ctx_walk_cb(const void *chunk, int depth, + int max_depth, int is_ref, void *data) +{ + const char *chunk_name = talloc_get_name(chunk); + int spaces_cnt; + + /* Hierarchical spacing */ + for (spaces_cnt = 0; spaces_cnt < depth; spaces_cnt++) + printf(" "); + + /* Chunk info */ + printf("chunk %s: depth=%d\n", chunk_name, depth); +} + +static int pq_file_proc(void *state, uint8_t *out, const uint8_t *in, + unsigned int in_len) +{ + int i; + + /* Change upper case to lower */ + for (i = 0; i < in_len; i++) + out[i] = (in[i] >= 65 && in[i] <= 90) ? + in[i] + 32 : in[i]; + + return in_len; +} + +static int pq_file_check(FILE *src_file, unsigned int blk_len) +{ + struct osmo_gapk_pq_item *proc_item; + struct osmo_gapk_pq *pq; + int rc; + + printf("Processing sample file with blk_len=%u:\n", blk_len); + + /* Allocate a processing queue */ + pq = osmo_gapk_pq_create("pq_file_check"); + assert(pq != NULL); + + /* Add a file sink */ + rc = osmo_gapk_pq_queue_file_input(pq, src_file, blk_len); + assert(rc == 0); + + /* Add a processing item */ + proc_item = osmo_gapk_pq_add_item(pq); + assert(proc_item != NULL); + + /* Set up I/O parameters */ + proc_item->type = OSMO_GAPK_ITEM_TYPE_PROC; + proc_item->len_in = blk_len; + proc_item->len_out = blk_len; + + /* Set the processing callback */ + proc_item->proc = &pq_file_proc; + + /* Add a sink item */ + rc = osmo_gapk_pq_queue_file_output(pq, stdout, blk_len); + assert(rc == 0); + + /* Check a queue in strict mode */ + rc = osmo_gapk_pq_check(pq, 1); + assert(rc == 0); + + /* Prepare a queue */ + rc = osmo_gapk_pq_prepare(pq); + assert(rc == 0); + + while (1) { + rc = osmo_gapk_pq_execute(pq); + if (rc) + break; + } + + /* Destroy processing queue */ + osmo_gapk_pq_destroy(pq); + + return 0; +} + +int main(int argc, char **argv) +{ + int i; + + /* Enable tracking the use of NULL memory contexts */ + talloc_enable_null_tracking(); + + /* Open sample file */ + FILE *sample_file = fopen(argv[1], "rb"); + assert(sample_file != NULL); + + /* Process sample file with different blk_len values */ + for (i = 2; i <= 32; i *= 2) { + pq_file_check(sample_file, i); + rewind(sample_file); + } + + printf("\n"); + + /* Close sample file */ + fclose(sample_file); + + /* Print talloc memory hierarchy */ + talloc_report_depth_cb(NULL, 0, 10, &talloc_ctx_walk_cb, NULL); + + return 0; +} diff --git a/tests/io/pq_file_test.ok b/tests/io/pq_file_test.ok new file mode 100644 index 0000000..8df71c3 --- /dev/null +++ b/tests/io/pq_file_test.ok @@ -0,0 +1,12 @@ +Processing sample file with blk_len=2: +8217c8fb7675a95008f9089d883gapk +Processing sample file with blk_len=4: +8217c8fb7675a95008f9089d883gapk +Processing sample file with blk_len=8: +8217c8fb7675a95008f9089d883gapk +Processing sample file with blk_len=16: +8217c8fb7675a95008f9089d883gapk +Processing sample file with blk_len=32: +8217c8fb7675a95008f9089d883gapk + +chunk null_context: depth=0 diff --git a/tests/io/pq_rtp_test.c b/tests/io/pq_rtp_test.c new file mode 100644 index 0000000..f4070b7 --- /dev/null +++ b/tests/io/pq_rtp_test.c @@ -0,0 +1,349 @@ +/* + * This file is part of GAPK (GSM Audio Pocket Knife). + * + * (C) 2017 by Vadim Yanitskiy + * + * GAPK 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 3 of the License, or + * (at your option) any later version. + * + * GAPK 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 GAPK. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include + +/** + * This test is intended to check the RTP source / sink operability. + * To do this, two processing queues are being allocated: + * + * "generator": source/random -> sink/rtp + * "checker": source/rtp -> sink/checker + * + * The first one generates some amount of random bytes (payload), + * and stores them inside a buffer that is shared between both + * queues. + * + * After generation, a payload is being sent from the first + * queue via an RTP sink, and then being received by the second + * via an RTP source. + * + * As both queues do use a shared buffer, the last item of the + * second queue (named 'sink/checker') is able to compare a + * received payload with expected. + */ + +static void talloc_ctx_walk_cb(const void *chunk, int depth, + int max_depth, int is_ref, void *data) +{ + const char *chunk_name = talloc_get_name(chunk); + int spaces_cnt; + + /* Hierarchical spacing */ + for (spaces_cnt = 0; spaces_cnt < depth; spaces_cnt++) + printf(" "); + + /* Chunk info */ + printf("chunk %s: depth=%d\n", chunk_name, depth); +} + +#define RTP_TEST_BUF_LEN 128 + +struct rtp_test_state { + unsigned int payload_len; + unsigned int rtp_port; + int rtp_src_fd; + int rtp_dst_fd; + + uint8_t data[RTP_TEST_BUF_LEN]; + uint8_t *ptr; +}; + +static int src_rand_proc(void *data, uint8_t *out, const uint8_t *in, + unsigned int in_len) +{ + struct rtp_test_state *state = (struct rtp_test_state *) data; + unsigned int i; + + /* Generate a random payload */ + for (i = 0; i < state->payload_len; i++) { + uint8_t byte = rand() % 0xff; + *(state->ptr + i) = byte; + out[i] = byte; + } + + return state->payload_len; +} + +static void src_rand_exit(void *data) +{ + struct rtp_test_state *state = (struct rtp_test_state *) data; + + if (state->rtp_src_fd >= 0) { + close(state->rtp_src_fd); + state->rtp_src_fd = -1; + } +} + +static int sink_chk_proc(void *data, uint8_t *out, const uint8_t *in, + unsigned int in_len) +{ + struct rtp_test_state *state = (struct rtp_test_state *) data; + unsigned int i; + + /* Make sure we have all bytes transferred */ + if (in_len != state->payload_len) { + printf("Data length mismatch!\n"); + return -EINVAL; + } + + for (i = 0; i < in_len; i++) { + if (in[i] != *(state->ptr + i)) { + printf("Data mismatch!\n"); + return -EINVAL; + } + } + + return in_len; +} + +static void sink_chk_exit(void *data) +{ + struct rtp_test_state *state = (struct rtp_test_state *) data; + + if (state->rtp_dst_fd >= 0) { + close(state->rtp_dst_fd); + state->rtp_dst_fd = -1; + } +} + +/* Allocates: source/random -> sink/rtp */ +static int init_gen_queue(struct osmo_gapk_pq *pq, + struct rtp_test_state *state, unsigned int payload_len) +{ + int rc; + + /* Allocate memory for the 'source/random' */ + struct osmo_gapk_pq_item *src_rand = osmo_gapk_pq_add_item(pq); + if (!src_rand) + return -ENOMEM; + + /* Fill in meta information */ + src_rand->type = OSMO_GAPK_ITEM_TYPE_SOURCE; + src_rand->cat_name = OSMO_GAPK_CAT_NAME_SOURCE; + src_rand->sub_name = "random"; + + /* Set I/O buffer lengths */ + state->payload_len = payload_len; + src_rand->len_out = payload_len; + + /* Set proc / exit callbacks and state */ + src_rand->proc = &src_rand_proc; + src_rand->exit = &src_rand_exit; + src_rand->state = state; + + + /* Init connection socket */ + state->rtp_dst_fd = osmo_sock_init(AF_UNSPEC, SOCK_DGRAM, + IPPROTO_UDP, "127.0.0.1", state->rtp_port, OSMO_SOCK_F_CONNECT); + if (state->rtp_dst_fd < 0) { + printf("Could not init connection socket\n"); + return -EINVAL; + } + + /* Init an RTP sink */ + rc = osmo_gapk_pq_queue_rtp_output(pq, state->rtp_dst_fd, payload_len); + if (rc) { + printf("Could not init an RTP sink\n"); + return rc; + } + + /* Check and prepare */ + rc = osmo_gapk_pq_check(pq, 1); + if (rc) { + printf("Queue check failed\n"); + return rc; + } + + rc = osmo_gapk_pq_prepare(pq); + if (rc) { + printf("Queue preparation failed\n"); + return rc; + } + + return 0; +} + +/* Allocates: source/rtp -> sink/checker */ +static int init_chk_queue(struct osmo_gapk_pq *pq, + struct rtp_test_state *state, unsigned int payload_len) +{ + int rc; + + /* Init listening socket */ + state->rtp_src_fd = osmo_sock_init(AF_UNSPEC, SOCK_DGRAM, + IPPROTO_UDP, "127.0.0.1", 0, OSMO_SOCK_F_BIND); + if (state->rtp_src_fd < 0) { + printf("Could not init listening socket\n"); + return -EINVAL; + } + + /* Init an RTP source on any available port */ + rc = osmo_gapk_pq_queue_rtp_input(pq, state->rtp_src_fd, payload_len); + if (rc) { + printf("Could not init an RTP sink\n"); + return rc; + } + + /* Determine on which port are we listening */ + struct sockaddr_in adr_inet; + socklen_t len_inet; + + len_inet = sizeof(adr_inet); + rc = getsockname(state->rtp_src_fd, + (struct sockaddr *) &adr_inet, &len_inet); + if (rc) + return -EINVAL; + + /* Save assigned port to shared state */ + state->rtp_port = (unsigned int) ntohs(adr_inet.sin_port); + + + /* Allocate memory for the 'sink/checker' */ + struct osmo_gapk_pq_item *sink_chk = osmo_gapk_pq_add_item(pq); + if (!sink_chk) + return -ENOMEM; + + /* Fill in meta information */ + sink_chk->type = OSMO_GAPK_ITEM_TYPE_SINK; + sink_chk->cat_name = OSMO_GAPK_CAT_NAME_SINK; + sink_chk->sub_name = "checker"; + + /* Set I/O buffer lengths */ + sink_chk->len_in = payload_len; + + /* Set proc / exit callbacks and state */ + sink_chk->proc = &sink_chk_proc; + sink_chk->exit = &sink_chk_exit; + sink_chk->state = state; + + /* Check and prepare */ + rc = osmo_gapk_pq_check(pq, 1); + if (rc) { + printf("Queue check failed\n"); + return rc; + } + + rc = osmo_gapk_pq_prepare(pq); + if (rc) { + printf("Queue preparation failed\n"); + return rc; + } + + return 0; +} + +static int rtp_test(struct rtp_test_state *state, unsigned int payload_len) +{ + struct osmo_gapk_pq *q_gen, *q_chk; + unsigned int i, chunks; + int rc; + + /* Allocate two queues */ + q_gen = osmo_gapk_pq_create("generator"); + q_chk = osmo_gapk_pq_create("checker"); + + /* Make sure both queues are allocated */ + if (!q_gen || !q_chk) { + rc = -ENOMEM; + goto exit; + } + + /* Init both queues: generator and checker */ + rc = init_chk_queue(q_chk, state, payload_len); + if (rc) + goto exit; + + rc = init_gen_queue(q_gen, state, payload_len); + if (rc) + goto exit; + + /* Calculate how much chunks do we have */ + chunks = RTP_TEST_BUF_LEN / payload_len; + + /* Execute both queues */ + for (i = 0; i < chunks; i++) { + /* Move data pointer */ + state->ptr = state->data + i * payload_len; + + /* Generate and send a payload */ + rc = osmo_gapk_pq_execute(q_gen); + if (rc) { + printf("Queue '%s' execution aborted on chunk %u/%u\n", + q_gen->name, i + 1, chunks); + goto exit; + } + + /* TODO: prevent test hang if nothing was being sent */ + + /* Receive and check a payload */ + rc = osmo_gapk_pq_execute(q_chk); + if (rc) { + printf("Queue '%s' execution aborted on chunk %u/%u\n", + q_gen->name, i + 1, chunks); + goto exit; + } + } + + printf("Payload len=%u check ok\n", payload_len); + +exit: + /* Deallocate both queues and data */ + osmo_gapk_pq_destroy(q_gen); + osmo_gapk_pq_destroy(q_chk); + + return rc; +} + +int main(int argc, char **argv) +{ + struct rtp_test_state state; + unsigned int len; + + /* Enable tracking the use of NULL memory contexts */ + talloc_enable_null_tracking(); + + /* Init pseudo-random generator */ + srand(time(NULL)); + + /* Perform testing with different payload size values */ + for (len = 1; len <= RTP_TEST_BUF_LEN; len *= 2) + assert(rtp_test(&state, len) == 0); + printf("\n"); + + /* Memory leak detection test */ + talloc_report_depth_cb(NULL, 0, 10, &talloc_ctx_walk_cb, NULL); + + return 0; +} diff --git a/tests/io/pq_rtp_test.ok b/tests/io/pq_rtp_test.ok new file mode 100644 index 0000000..bae485f --- /dev/null +++ b/tests/io/pq_rtp_test.ok @@ -0,0 +1,10 @@ +Payload len=1 check ok +Payload len=2 check ok +Payload len=4 check ok +Payload len=8 check ok +Payload len=16 check ok +Payload len=32 check ok +Payload len=64 check ok +Payload len=128 check ok + +chunk null_context: depth=0 diff --git a/test/play_all_formats.sh b/tests/play_all_formats.sh similarity index 100% rename from test/play_all_formats.sh rename to tests/play_all_formats.sh diff --git a/tests/procqueue/pq_test.c b/tests/procqueue/pq_test.c new file mode 100644 index 0000000..198108d --- /dev/null +++ b/tests/procqueue/pq_test.c @@ -0,0 +1,373 @@ +/* + * This file is part of GAPK (GSM Audio Pocket Knife). + * + * (C) 2017 by Vadim Yanitskiy + * + * GAPK 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 3 of the License, or + * (at your option) any later version. + * + * GAPK 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 GAPK. If not, see . + */ + +#include +#include +#include + +#include +#include + +/** + * This test is intended to validate the processing queue + * management API. Moreover, the talloc debugging API is + * used to ensure that there are no memory leaks. + * + * First, four processing queues are being allocated. One + * of them is empty, while others have different count of + * items. Then the human-readable description is being + * generated for all of them. And finally, the processing + * and exit callback are being tested. + * + * During the test execution, the talloc NULL-context + * tracking feature is enabled, allowing to observe every + * memory allocation within the libosmogapk, and to detect + * memory leaks. + */ + +static void talloc_ctx_walk_cb(const void *chunk, int depth, + int max_depth, int is_ref, void *data) +{ + const char *chunk_name = talloc_get_name(chunk); + int spaces_cnt; + + /* Hierarchical spacing */ + for (spaces_cnt = 0; spaces_cnt < depth; spaces_cnt++) + printf(" "); + + /* Chunk info */ + printf("chunk %s: depth=%d\n", chunk_name, depth); +} + +static int q3_i0_proc(void *state, uint8_t *out, const uint8_t *in, + unsigned int in_len) +{ + int i; + + printf("Incoming data: "); + + for (i = 0; i < 10; i++) { + printf("%d ", i); + out[i] = i; + } + + printf("\n"); + + return 0; +} + +static int q3_i1_proc(void *state, uint8_t *out, const uint8_t *in, + unsigned int in_len) +{ + int i; + + for (i = 0; i < 10; i++) + out[i] = in[i] % 2; + + return 0; +} + +static int q3_i2_proc(void *state, uint8_t *out, const uint8_t *in, + unsigned int in_len) +{ + int i; + + printf("Outgoing data: "); + + for (i = 0; i < 10; i++) + printf("%d ", in[i]); + + printf("\n"); + + return 0; +} + +static void q3_exit(void *state) +{ + struct osmo_gapk_pq_item *item; + + /* Make sure the item's state is passed correctly */ + assert(state != NULL); + + item = (struct osmo_gapk_pq_item *) state; + printf("Calling exit callback for '%s/%s'\n", + item->cat_name, item->sub_name); +} + +int main(int argc, char **argv) +{ + /* Enable tracking the use of NULL memory contexts */ + talloc_enable_null_tracking(); + + /** + * 1. Processing queue allocation test + */ + struct osmo_gapk_pq *q0, *q1, *q2, *q3; + + printf("Processing queue allocation test:\n"); + + /* Allocate four queues */ + q0 = osmo_gapk_pq_create(NULL); + q1 = osmo_gapk_pq_create("q1"); + q2 = osmo_gapk_pq_create("q2"); + q3 = osmo_gapk_pq_create("q3"); + + /* Make sure all queues are allocated */ + assert(q0 != NULL); + assert(q1 != NULL); + assert(q2 != NULL); + assert(q3 != NULL); + + /* Print talloc memory hierarchy */ + talloc_report_depth_cb(NULL, 0, 10, &talloc_ctx_walk_cb, NULL); + printf("\n"); + + + /** + * 2. Item allocation test + */ + struct osmo_gapk_pq_item *q3_i0, *q3_i1, *q3_i2; + struct osmo_gapk_pq_item *q2_i0, *q2_i1; + struct osmo_gapk_pq_item *q1_i0; + + printf("Item allocation test:\n"); + + /* Make sure there are no items */ + assert(q0->n_items == 0); + assert(q1->n_items == 0); + assert(q2->n_items == 0); + assert(q3->n_items == 0); + + /* Allocate items */ + q3_i0 = osmo_gapk_pq_add_item(q3); + q3_i1 = osmo_gapk_pq_add_item(q3); + q3_i2 = osmo_gapk_pq_add_item(q3); + q2_i0 = osmo_gapk_pq_add_item(q2); + q2_i1 = osmo_gapk_pq_add_item(q2); + q1_i0 = osmo_gapk_pq_add_item(q1); + + /* Make sure all items are allocated */ + assert(q3_i0 != NULL); + assert(q3_i1 != NULL); + assert(q3_i2 != NULL); + assert(q2_i0 != NULL); + assert(q2_i1 != NULL); + assert(q1_i0 != NULL); + + /* Check item count */ + assert(q0->n_items == 0); + assert(q1->n_items == 1); + assert(q2->n_items == 2); + assert(q3->n_items == 3); + + /* Print talloc memory hierarchy */ + talloc_report_depth_cb(NULL, 0, 10, &talloc_ctx_walk_cb, NULL); + printf("\n"); + + + /** + * 3. Items persistence test + */ + struct osmo_gapk_pq_item *item; + + /* Make sure that q0 is empty, others are not */ + assert(llist_empty(&q0->items) == 1); + assert(llist_empty(&q1->items) == 0); + assert(llist_empty(&q2->items) == 0); + assert(llist_empty(&q3->items) == 0); + + /* A single item must be the first and the last in a queue */ + item = llist_first_entry(&q1->items, struct osmo_gapk_pq_item, list); + assert(item == q1_i0); + item = llist_last_entry(&q1->items, struct osmo_gapk_pq_item, list); + assert(item == q1_i0); + + /* Two items: one is the first, second is the last */ + item = llist_first_entry(&q2->items, struct osmo_gapk_pq_item, list); + assert(item == q2_i0); + item = llist_last_entry(&q2->items, struct osmo_gapk_pq_item, list); + assert(item == q2_i1); + + /* Three items: one is the first, third is the last */ + item = llist_first_entry(&q3->items, struct osmo_gapk_pq_item, list); + assert(item == q3_i0); + assert(item != q3_i1); + item = llist_last_entry(&q3->items, struct osmo_gapk_pq_item, list); + assert(item == q3_i2); + assert(item != q3_i1); + + + /** + * 4. Queue I/O data lengths check + */ + q3_i0->len_in = 10; + q3_i0->len_out = 20; + + q3_i1->len_in = 20; + q3_i1->len_out = 30; + + q3_i2->len_in = 30; + q3_i2->len_out = 10; + + /* Normal case */ + assert(osmo_gapk_pq_check(q3, 0) == 0); + + /* Abnormal case (I/O length mismatch) */ + q3_i0->len_out = 10; + assert(osmo_gapk_pq_check(q3, 0) != 0); + q3_i0->len_out = 20; + + /* Check queue in strict mode (requires src -> ... -> sink) */ + q3_i0->type = OSMO_GAPK_ITEM_TYPE_SOURCE; + q3_i1->type = OSMO_GAPK_ITEM_TYPE_PROC; + q3_i2->type = OSMO_GAPK_ITEM_TYPE_SINK; + + /* Normal case (src -> proc -> sink) */ + assert(osmo_gapk_pq_check(q3, 1) == 0); + + /* Abnormal case (proc -> proc -> sink) */ + q3_i0->type = OSMO_GAPK_ITEM_TYPE_PROC; + assert(osmo_gapk_pq_check(q3, 1) != 0); + q3_i0->type = OSMO_GAPK_ITEM_TYPE_SOURCE; + + + /** + * 5. Buffer allocation test + */ + printf("Queue preparation test:\n"); + + /* Prepare the queue */ + assert(osmo_gapk_pq_prepare(q3) == 0); + + /* Make sure buffers were allocated */ + assert(q3_i0->buf != NULL); + assert(q3_i1->buf != NULL); + + /* Currently, we don't allocate buffers for sinks */ + assert(q3_i2->buf == NULL); + + /* Compare required vs allocated buffer sizes */ + assert(talloc_total_size(q3_i0->buf) == q3_i0->len_out); + assert(talloc_total_size(q3_i1->buf) == q3_i1->len_out); + + /* Print talloc memory hierarchy */ + talloc_report_depth_cb(NULL, 0, 10, &talloc_ctx_walk_cb, NULL); + printf("\n"); + + + /** + * 6. Queue description test + */ + char *queue_desc; + + printf("Queue description test:\n"); + + /* An empty queue doesn't have description */ + queue_desc = osmo_gapk_pq_describe(q0); + assert(queue_desc == NULL); + + /* Fill in some data */ + q3_i0->cat_name = OSMO_GAPK_CAT_NAME_SOURCE; + q3_i0->sub_name = "file"; + + q3_i1->cat_name = OSMO_GAPK_CAT_NAME_PROC; + q3_i1->sub_name = "dummy"; + + q3_i2->cat_name = OSMO_GAPK_CAT_NAME_SINK; + q3_i2->sub_name = "alsa"; + + q2_i0->cat_name = OSMO_GAPK_CAT_NAME_SOURCE; + q2_i0->sub_name = "dummy"; + q2_i1->cat_name = OSMO_GAPK_CAT_NAME_SINK; + q2_i1->sub_name = "dummy"; + + q1_i0->cat_name = "dummy"; + q1_i0->sub_name = "dummy"; + + queue_desc = osmo_gapk_pq_describe(q3); + assert(queue_desc != NULL); + printf("Queue q3 description: %s\n", queue_desc); + talloc_free(queue_desc); + + queue_desc = osmo_gapk_pq_describe(q2); + assert(queue_desc != NULL); + printf("Queue q2 description: %s\n", queue_desc); + talloc_free(queue_desc); + + queue_desc = osmo_gapk_pq_describe(q1); + assert(queue_desc != NULL); + printf("Queue q1 description: %s\n\n", queue_desc); + talloc_free(queue_desc); + + + /** + * 7. Queue execution test + */ + printf("Processing queue execution test:\n"); + + /* Make sure there are no callbacks by default */ + assert(q3_i0->proc == NULL); + assert(q3_i0->wait == NULL); + assert(q3_i0->exit == NULL); + + assert(q3_i1->proc == NULL); + assert(q3_i1->wait == NULL); + assert(q3_i1->exit == NULL); + + assert(q3_i2->proc == NULL); + assert(q3_i2->wait == NULL); + assert(q3_i2->exit == NULL); + + q3_i0->proc = &q3_i0_proc; + q3_i1->proc = &q3_i1_proc; + q3_i2->proc = &q3_i2_proc; + + assert(osmo_gapk_pq_execute(q3) == 0); + printf("\n"); + + + /** + * 8. Exit callback & deallocation tests + */ + printf("Processing queue exit callback test:\n"); + + q3_i0->state = q3_i0; + q3_i1->state = q3_i1; + q3_i2->state = q3_i2; + + q3_i0->exit = &q3_exit; + q3_i1->exit = &q3_exit; + q3_i2->exit = &q3_exit; + + osmo_gapk_pq_destroy(q0); + osmo_gapk_pq_destroy(q1); + osmo_gapk_pq_destroy(q2); + osmo_gapk_pq_destroy(q3); + + printf("\n"); + + + /** + * 9. Memory leak detection test + */ + printf("Processing queue deallocation test:\n"); + talloc_report_depth_cb(NULL, 0, 10, &talloc_ctx_walk_cb, NULL); + + return 0; +} diff --git a/tests/procqueue/pq_test.ok b/tests/procqueue/pq_test.ok new file mode 100644 index 0000000..abdb666 --- /dev/null +++ b/tests/procqueue/pq_test.ok @@ -0,0 +1,60 @@ +Processing queue allocation test: +chunk null_context: depth=0 + chunk struct osmo_gapk_pq 'q3': depth=1 + chunk .name: depth=2 + chunk struct osmo_gapk_pq 'q2': depth=1 + chunk .name: depth=2 + chunk struct osmo_gapk_pq 'q1': depth=1 + chunk .name: depth=2 + chunk struct osmo_gapk_pq: depth=1 + +Item allocation test: +chunk null_context: depth=0 + chunk struct osmo_gapk_pq 'q3': depth=1 + chunk struct osmo_gapk_pq_item: depth=2 + chunk struct osmo_gapk_pq_item: depth=2 + chunk struct osmo_gapk_pq_item: depth=2 + chunk .name: depth=2 + chunk struct osmo_gapk_pq 'q2': depth=1 + chunk struct osmo_gapk_pq_item: depth=2 + chunk struct osmo_gapk_pq_item: depth=2 + chunk .name: depth=2 + chunk struct osmo_gapk_pq 'q1': depth=1 + chunk struct osmo_gapk_pq_item: depth=2 + chunk .name: depth=2 + chunk struct osmo_gapk_pq: depth=1 + +Queue preparation test: +chunk null_context: depth=0 + chunk struct osmo_gapk_pq 'q3': depth=1 + chunk struct osmo_gapk_pq_item: depth=2 + chunk struct osmo_gapk_pq_item: depth=2 + chunk .buffer: depth=3 + chunk struct osmo_gapk_pq_item: depth=2 + chunk .buffer: depth=3 + chunk .name: depth=2 + chunk struct osmo_gapk_pq 'q2': depth=1 + chunk struct osmo_gapk_pq_item: depth=2 + chunk struct osmo_gapk_pq_item: depth=2 + chunk .name: depth=2 + chunk struct osmo_gapk_pq 'q1': depth=1 + chunk struct osmo_gapk_pq_item: depth=2 + chunk .name: depth=2 + chunk struct osmo_gapk_pq: depth=1 + +Queue description test: +Queue q3 description: source/file -> proc/dummy -> sink/alsa +Queue q2 description: source/dummy -> sink/dummy +Queue q1 description: dummy/dummy + +Processing queue execution test: +Incoming data: 0 1 2 3 4 5 6 7 8 9 +Outgoing data: 0 1 0 1 0 1 0 1 0 1 + +Processing queue exit callback test: +Calling exit callback for 'source/file' +Calling exit callback for 'proc/dummy' +Calling exit callback for 'sink/alsa' + +Processing queue deallocation test: +chunk null_context: depth=0 diff --git a/test/ref-files/hhgttg_part1_5.s16 b/tests/ref-files/hhgttg_part1_5.s16 similarity index 100% rename from test/ref-files/hhgttg_part1_5.s16 rename to tests/ref-files/hhgttg_part1_5.s16 diff --git a/test/ref-files/hhgttg_part1_5.s16.amr-efr b/tests/ref-files/hhgttg_part1_5.s16.amr-efr similarity index 100% rename from test/ref-files/hhgttg_part1_5.s16.amr-efr rename to tests/ref-files/hhgttg_part1_5.s16.amr-efr diff --git a/test/ref-files/hhgttg_part1_5.s16.amr-efr.s16 b/tests/ref-files/hhgttg_part1_5.s16.amr-efr.s16 similarity index 100% rename from test/ref-files/hhgttg_part1_5.s16.amr-efr.s16 rename to tests/ref-files/hhgttg_part1_5.s16.amr-efr.s16 diff --git a/test/ref-files/hhgttg_part1_5.s16.gsm b/tests/ref-files/hhgttg_part1_5.s16.gsm similarity index 100% rename from test/ref-files/hhgttg_part1_5.s16.gsm rename to tests/ref-files/hhgttg_part1_5.s16.gsm diff --git a/test/ref-files/hhgttg_part1_5.s16.gsm.s16 b/tests/ref-files/hhgttg_part1_5.s16.gsm.s16 similarity index 100% rename from test/ref-files/hhgttg_part1_5.s16.gsm.s16 rename to tests/ref-files/hhgttg_part1_5.s16.gsm.s16 diff --git a/test/ref-files/hhgttg_part1_5.s16.racal-efr b/tests/ref-files/hhgttg_part1_5.s16.racal-efr similarity index 100% rename from test/ref-files/hhgttg_part1_5.s16.racal-efr rename to tests/ref-files/hhgttg_part1_5.s16.racal-efr diff --git a/test/ref-files/hhgttg_part1_5.s16.racal-efr.s16 b/tests/ref-files/hhgttg_part1_5.s16.racal-efr.s16 similarity index 100% rename from test/ref-files/hhgttg_part1_5.s16.racal-efr.s16 rename to tests/ref-files/hhgttg_part1_5.s16.racal-efr.s16 diff --git a/test/ref-files/hhgttg_part1_5.s16.racal-fr b/tests/ref-files/hhgttg_part1_5.s16.racal-fr similarity index 100% rename from test/ref-files/hhgttg_part1_5.s16.racal-fr rename to tests/ref-files/hhgttg_part1_5.s16.racal-fr diff --git a/test/ref-files/hhgttg_part1_5.s16.racal-fr.s16 b/tests/ref-files/hhgttg_part1_5.s16.racal-fr.s16 similarity index 100% rename from test/ref-files/hhgttg_part1_5.s16.racal-fr.s16 rename to tests/ref-files/hhgttg_part1_5.s16.racal-fr.s16 diff --git a/test/ref-files/hhgttg_part1_5.s16.racal-hr b/tests/ref-files/hhgttg_part1_5.s16.racal-hr similarity index 100% rename from test/ref-files/hhgttg_part1_5.s16.racal-hr rename to tests/ref-files/hhgttg_part1_5.s16.racal-hr diff --git a/test/ref-files/hhgttg_part1_5.s16.racal-hr.s16 b/tests/ref-files/hhgttg_part1_5.s16.racal-hr.s16 similarity index 100% rename from test/ref-files/hhgttg_part1_5.s16.racal-hr.s16 rename to tests/ref-files/hhgttg_part1_5.s16.racal-hr.s16 diff --git a/test/ref-files/hhgttg_part1_5.s16.rtp-efr b/tests/ref-files/hhgttg_part1_5.s16.rtp-efr similarity index 100% rename from test/ref-files/hhgttg_part1_5.s16.rtp-efr rename to tests/ref-files/hhgttg_part1_5.s16.rtp-efr diff --git a/test/ref-files/hhgttg_part1_5.s16.rtp-efr.s16 b/tests/ref-files/hhgttg_part1_5.s16.rtp-efr.s16 similarity index 100% rename from test/ref-files/hhgttg_part1_5.s16.rtp-efr.s16 rename to tests/ref-files/hhgttg_part1_5.s16.rtp-efr.s16 diff --git a/test/ref-files/hhgttg_part1_5.s16.rtp-hr-etsi b/tests/ref-files/hhgttg_part1_5.s16.rtp-hr-etsi similarity index 100% rename from test/ref-files/hhgttg_part1_5.s16.rtp-hr-etsi rename to tests/ref-files/hhgttg_part1_5.s16.rtp-hr-etsi diff --git a/test/ref-files/hhgttg_part1_5.s16.rtp-hr-etsi.s16 b/tests/ref-files/hhgttg_part1_5.s16.rtp-hr-etsi.s16 similarity index 100% rename from test/ref-files/hhgttg_part1_5.s16.rtp-hr-etsi.s16 rename to tests/ref-files/hhgttg_part1_5.s16.rtp-hr-etsi.s16 diff --git a/test/ref-files/hhgttg_part1_5.s16.rtp-hr-ietf b/tests/ref-files/hhgttg_part1_5.s16.rtp-hr-ietf similarity index 100% rename from test/ref-files/hhgttg_part1_5.s16.rtp-hr-ietf rename to tests/ref-files/hhgttg_part1_5.s16.rtp-hr-ietf diff --git a/test/ref-files/hhgttg_part1_5.s16.rtp-hr-ietf.s16 b/tests/ref-files/hhgttg_part1_5.s16.rtp-hr-ietf.s16 similarity index 100% rename from test/ref-files/hhgttg_part1_5.s16.rtp-hr-ietf.s16 rename to tests/ref-files/hhgttg_part1_5.s16.rtp-hr-ietf.s16 diff --git a/test/ref-files/hhgttg_part1_5.s16.ti-efr b/tests/ref-files/hhgttg_part1_5.s16.ti-efr similarity index 100% rename from test/ref-files/hhgttg_part1_5.s16.ti-efr rename to tests/ref-files/hhgttg_part1_5.s16.ti-efr diff --git a/test/ref-files/hhgttg_part1_5.s16.ti-efr.s16 b/tests/ref-files/hhgttg_part1_5.s16.ti-efr.s16 similarity index 100% rename from test/ref-files/hhgttg_part1_5.s16.ti-efr.s16 rename to tests/ref-files/hhgttg_part1_5.s16.ti-efr.s16 diff --git a/test/ref-files/hhgttg_part1_5.s16.ti-fr b/tests/ref-files/hhgttg_part1_5.s16.ti-fr similarity index 100% rename from test/ref-files/hhgttg_part1_5.s16.ti-fr rename to tests/ref-files/hhgttg_part1_5.s16.ti-fr diff --git a/test/ref-files/hhgttg_part1_5.s16.ti-fr.s16 b/tests/ref-files/hhgttg_part1_5.s16.ti-fr.s16 similarity index 100% rename from test/ref-files/hhgttg_part1_5.s16.ti-fr.s16 rename to tests/ref-files/hhgttg_part1_5.s16.ti-fr.s16 diff --git a/test/ref-files/hhgttg_part1_5.s16.ti-hr b/tests/ref-files/hhgttg_part1_5.s16.ti-hr similarity index 100% rename from test/ref-files/hhgttg_part1_5.s16.ti-hr rename to tests/ref-files/hhgttg_part1_5.s16.ti-hr diff --git a/test/ref-files/hhgttg_part1_5.s16.ti-hr.s16 b/tests/ref-files/hhgttg_part1_5.s16.ti-hr.s16 similarity index 100% rename from test/ref-files/hhgttg_part1_5.s16.ti-hr.s16 rename to tests/ref-files/hhgttg_part1_5.s16.ti-hr.s16 diff --git a/test/test_all_formats.sh b/tests/test_all_formats.sh similarity index 100% rename from test/test_all_formats.sh rename to tests/test_all_formats.sh diff --git a/tests/testsuite.at b/tests/testsuite.at new file mode 100644 index 0000000..7367bc5 --- /dev/null +++ b/tests/testsuite.at @@ -0,0 +1,244 @@ +AT_INIT +AT_BANNER([Regression tests.]) + +AT_SETUP([procqueue]) +AT_KEYWORDS([procqueue]) +cat $abs_srcdir/procqueue/pq_test.ok > expout +AT_CHECK([$abs_top_builddir/tests/procqueue/pq_test], [0], [expout]) +AT_CLEANUP + +AT_SETUP([io/pq_file]) +AT_KEYWORDS([pq_file]) +cat $abs_srcdir/io/pq_file_test.ok > expout +AT_CHECK([ + $abs_top_builddir/tests/io/pq_file_test \ + $abs_top_builddir/tests/io/io_sample.txt], + [0], [expout]) +AT_CLEANUP + +AT_SETUP([io/pq_rtp]) +AT_KEYWORDS([pq_rtp]) +cat $abs_srcdir/io/pq_rtp_test.ok > expout +AT_CHECK([ + $abs_top_builddir/tests/io/pq_rtp_test], [0], [expout]) +AT_CLEANUP + +AT_SETUP([conv/enc/amr_efr]) +AT_KEYWORDS([amr_efr]) +cat $abs_srcdir/ref-files/hhgttg_part1_5.s16.amr-efr > expout +AT_CHECK([ + $abs_top_builddir/src/osmo-gapk \ + -i $abs_srcdir/ref-files/hhgttg_part1_5.s16 \ + -f rawpcm-s16le -g amr-efr +], [0], [expout], [ignore]) +AT_CLEANUP + +AT_SETUP([conv/enc/gsm]) +AT_KEYWORDS([gsm]) +cat $abs_srcdir/ref-files/hhgttg_part1_5.s16.gsm > expout +AT_CHECK([ + $abs_top_builddir/src/osmo-gapk \ + -i $abs_srcdir/ref-files/hhgttg_part1_5.s16 \ + -f rawpcm-s16le -g gsm +], [0], [expout], [ignore]) +AT_CLEANUP + +AT_SETUP([conv/enc/racal_hr]) +AT_KEYWORDS([racal_hr]) +cat $abs_srcdir/ref-files/hhgttg_part1_5.s16.racal-hr > expout +AT_CHECK([ + $abs_top_builddir/src/osmo-gapk \ + -i $abs_srcdir/ref-files/hhgttg_part1_5.s16 \ + -f rawpcm-s16le -g racal-hr +], [0], [expout], [ignore]) +AT_CLEANUP + +AT_SETUP([conv/enc/racal_fr]) +AT_KEYWORDS([racal_fr]) +cat $abs_srcdir/ref-files/hhgttg_part1_5.s16.racal-fr > expout +AT_CHECK([ + $abs_top_builddir/src/osmo-gapk \ + -i $abs_srcdir/ref-files/hhgttg_part1_5.s16 \ + -f rawpcm-s16le -g racal-fr +], [0], [expout], [ignore]) +AT_CLEANUP + +AT_SETUP([conv/enc/racal_efr]) +AT_KEYWORDS([racal_efr]) +cat $abs_srcdir/ref-files/hhgttg_part1_5.s16.racal-efr > expout +AT_CHECK([ + $abs_top_builddir/src/osmo-gapk \ + -i $abs_srcdir/ref-files/hhgttg_part1_5.s16 \ + -f rawpcm-s16le -g racal-efr +], [0], [expout], [ignore]) +AT_CLEANUP + +AT_SETUP([conv/enc/ti_hr]) +AT_KEYWORDS([ti_hr]) +cat $abs_srcdir/ref-files/hhgttg_part1_5.s16.ti-hr > expout +AT_CHECK([ + $abs_top_builddir/src/osmo-gapk \ + -i $abs_srcdir/ref-files/hhgttg_part1_5.s16 \ + -f rawpcm-s16le -g ti-hr +], [0], [expout], [ignore]) +AT_CLEANUP + +AT_SETUP([conv/enc/ti_fr]) +AT_KEYWORDS([ti_fr]) +cat $abs_srcdir/ref-files/hhgttg_part1_5.s16.ti-fr > expout +AT_CHECK([ + $abs_top_builddir/src/osmo-gapk \ + -i $abs_srcdir/ref-files/hhgttg_part1_5.s16 \ + -f rawpcm-s16le -g ti-fr +], [0], [expout], [ignore]) +AT_CLEANUP + +AT_SETUP([conv/enc/ti_efr]) +AT_KEYWORDS([ti_efr]) +cat $abs_srcdir/ref-files/hhgttg_part1_5.s16.ti-efr > expout +AT_CHECK([ + $abs_top_builddir/src/osmo-gapk \ + -i $abs_srcdir/ref-files/hhgttg_part1_5.s16 \ + -f rawpcm-s16le -g ti-efr +], [0], [expout], [ignore]) +AT_CLEANUP + +AT_SETUP([conv/enc/rtp_efr]) +AT_KEYWORDS([rtp_efr]) +cat $abs_srcdir/ref-files/hhgttg_part1_5.s16.rtp-efr > expout +AT_CHECK([ + $abs_top_builddir/src/osmo-gapk \ + -i $abs_srcdir/ref-files/hhgttg_part1_5.s16 \ + -f rawpcm-s16le -g rtp-efr +], [0], [expout], [ignore]) +AT_CLEANUP + +AT_SETUP([conv/enc/rtp_hr_etsi]) +AT_KEYWORDS([rtp_hr_etsi]) +cat $abs_srcdir/ref-files/hhgttg_part1_5.s16.rtp-hr-etsi > expout +AT_CHECK([ + $abs_top_builddir/src/osmo-gapk \ + -i $abs_srcdir/ref-files/hhgttg_part1_5.s16 \ + -f rawpcm-s16le -g rtp-hr-etsi +], [0], [expout], [ignore]) +AT_CLEANUP + +AT_SETUP([conv/enc/rtp_hr_ietf]) +AT_KEYWORDS([rtp_hr_ietf]) +cat $abs_srcdir/ref-files/hhgttg_part1_5.s16.rtp-hr-ietf > expout +AT_CHECK([ + $abs_top_builddir/src/osmo-gapk \ + -i $abs_srcdir/ref-files/hhgttg_part1_5.s16 \ + -f rawpcm-s16le -g rtp-hr-ietf +], [0], [expout], [ignore]) +AT_CLEANUP + +AT_SETUP([conv/dec/amr_efr]) +AT_KEYWORDS([amr_efr]) +cat $abs_srcdir/ref-files/hhgttg_part1_5.s16.amr-efr.s16 > expout +AT_CHECK([ + $abs_top_builddir/src/osmo-gapk \ + -i $abs_srcdir/ref-files/hhgttg_part1_5.s16.amr-efr \ + -f amr-efr -g rawpcm-s16le +], [0], [expout], [ignore]) +AT_CLEANUP + +AT_SETUP([conv/dec/gsm]) +AT_KEYWORDS([gsm]) +cat $abs_srcdir/ref-files/hhgttg_part1_5.s16.gsm.s16 > expout +AT_CHECK([ + $abs_top_builddir/src/osmo-gapk \ + -i $abs_srcdir/ref-files/hhgttg_part1_5.s16.gsm \ + -f gsm -g rawpcm-s16le +], [0], [expout], [ignore]) +AT_CLEANUP + +AT_SETUP([conv/dec/racal_hr]) +AT_KEYWORDS([racal_hr]) +cat $abs_srcdir/ref-files/hhgttg_part1_5.s16.racal-hr.s16 > expout +AT_CHECK([ + $abs_top_builddir/src/osmo-gapk \ + -i $abs_srcdir/ref-files/hhgttg_part1_5.s16.racal-hr \ + -f racal-hr -g rawpcm-s16le +], [0], [expout], [ignore]) +AT_CLEANUP + +AT_SETUP([conv/dec/racal_fr]) +AT_KEYWORDS([racal_fr]) +cat $abs_srcdir/ref-files/hhgttg_part1_5.s16.racal-fr.s16 > expout +AT_CHECK([ + $abs_top_builddir/src/osmo-gapk \ + -i $abs_srcdir/ref-files/hhgttg_part1_5.s16.racal-fr \ + -f racal-fr -g rawpcm-s16le +], [0], [expout], [ignore]) +AT_CLEANUP + +AT_SETUP([conv/dec/racal_efr]) +AT_KEYWORDS([racal_efr]) +cat $abs_srcdir/ref-files/hhgttg_part1_5.s16.racal-efr.s16 > expout +AT_CHECK([ + $abs_top_builddir/src/osmo-gapk \ + -i $abs_srcdir/ref-files/hhgttg_part1_5.s16.racal-efr \ + -f racal-efr -g rawpcm-s16le +], [0], [expout], [ignore]) +AT_CLEANUP + +AT_SETUP([conv/dec/ti_hr]) +AT_KEYWORDS([ti_hr]) +cat $abs_srcdir/ref-files/hhgttg_part1_5.s16.ti-hr.s16 > expout +AT_CHECK([ + $abs_top_builddir/src/osmo-gapk \ + -i $abs_srcdir/ref-files/hhgttg_part1_5.s16.ti-hr \ + -f ti-hr -g rawpcm-s16le +], [0], [expout], [ignore]) +AT_CLEANUP + +AT_SETUP([conv/dec/ti_fr]) +AT_KEYWORDS([ti_fr]) +cat $abs_srcdir/ref-files/hhgttg_part1_5.s16.ti-fr.s16 > expout +AT_CHECK([ + $abs_top_builddir/src/osmo-gapk \ + -i $abs_srcdir/ref-files/hhgttg_part1_5.s16.ti-fr \ + -f ti-fr -g rawpcm-s16le +], [0], [expout], [ignore]) +AT_CLEANUP + +AT_SETUP([conv/dec/ti_efr]) +AT_KEYWORDS([ti_efr]) +cat $abs_srcdir/ref-files/hhgttg_part1_5.s16.ti-efr.s16 > expout +AT_CHECK([ + $abs_top_builddir/src/osmo-gapk \ + -i $abs_srcdir/ref-files/hhgttg_part1_5.s16.ti-efr \ + -f ti-efr -g rawpcm-s16le +], [0], [expout], [ignore]) +AT_CLEANUP + +AT_SETUP([conv/dec/rtp_efr]) +AT_KEYWORDS([rtp_efr]) +cat $abs_srcdir/ref-files/hhgttg_part1_5.s16.rtp-efr.s16 > expout +AT_CHECK([ + $abs_top_builddir/src/osmo-gapk \ + -i $abs_srcdir/ref-files/hhgttg_part1_5.s16.rtp-efr \ + -f rtp-efr -g rawpcm-s16le +], [0], [expout], [ignore]) +AT_CLEANUP + +AT_SETUP([conv/dec/rtp_hr_etsi]) +AT_KEYWORDS([rtp_hr_etsi]) +cat $abs_srcdir/ref-files/hhgttg_part1_5.s16.rtp-hr-etsi.s16 > expout +AT_CHECK([ + $abs_top_builddir/src/osmo-gapk \ + -i $abs_srcdir/ref-files/hhgttg_part1_5.s16.rtp-hr-etsi \ + -f rtp-hr-etsi -g rawpcm-s16le +], [0], [expout], [ignore]) +AT_CLEANUP + +AT_SETUP([conv/dec/rtp_hr_ietf]) +AT_KEYWORDS([rtp_hr_ietf]) +cat $abs_srcdir/ref-files/hhgttg_part1_5.s16.rtp-hr-ietf.s16 > expout +AT_CHECK([ + $abs_top_builddir/src/osmo-gapk \ + -i $abs_srcdir/ref-files/hhgttg_part1_5.s16.rtp-hr-ietf \ + -f rtp-hr-ietf -g rawpcm-s16le +], [0], [expout], [ignore]) +AT_CLEANUP diff --git a/test/update_ref_files.sh b/tests/update_ref_files.sh similarity index 100% rename from test/update_ref_files.sh rename to tests/update_ref_files.sh