summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVadim Yanitskiy <axilirator@gmail.com>2018-01-17 20:46:06 +0600
committerVadim Yanitskiy <axilirator@gmail.com>2018-01-17 20:54:03 +0600
commit7a79fc11692779a1f9377b416b6208f1245e7612 (patch)
tree96998b59cbc1f4dceabd54c7a6980d7e63b3fa89
parent30493c78b3579b562b4d71224857d11fbf1b56b6 (diff)
parent6f34c8f3017629ea4865543e901b61ac893e0356 (diff)
Merge branch 'fixeria/lib' into master
The previous GAPK implementation was represented by a single executable. So, all audio transcoding operations were available only via calling the 'gapk' binary. This approach didn't allow external applications to benefit from using GAPK API directly. The following set of changes separates the common code into a shared library called 'libosmogapk', linking the 'gapk' binary against it: - 95e6664 Introduce a shared 'libosmogapk' library - 30209ce Install GAPK headers to '${includedir}/osmocom/gapk/' - a8d4657 Add an 'osmo_gapk' prefix to the exposed symbols - 40d59f1 Add a pkg-config manifest for libosmogapk - 4f0a47d Add the symbol export map for libosmogapk All memory management operations are now based on talloc library: - 3c20dac libosmogapk: use talloc for memory management - 5cabe1e osmo-gapk: use talloc for memory management Integrated Osmocom logging framework: - c35ba8a libosmogapk: use Osmocom logging framework - 4b7cd2c osmo-gapk: drop useless printf calls - 0fe18af osmo-gapk: use Osmocom logging framework - 11943bf osmo-gapk: adjust application verbosity Integrated GNU Autotest environment and basic test coverage: - f069eb3 Init automake test environment - 1fe6a9b tests: add procqueue test - 3e9e57f tests: add pq_file test - 9d2b15d tests: add pq_rtp test - f59f3f1 tests: add format / codec transcoding tests For more details, see commits history. Change-Id: I3c6d4a9d326ee49153e4ad83823d094831c112da
-rw-r--r--.gitignore14
-rw-r--r--Makefile.am5
-rw-r--r--configure.ac19
-rwxr-xr-xcontrib/benchmark.sh2
-rw-r--r--include/Makefile.am17
-rw-r--r--include/gapk/Makefile.am6
-rw-r--r--include/gapk/benchmark.h60
-rw-r--r--include/gapk/procqueue.h72
-rw-r--r--include/gsmhr/gsmhr.h5
-rw-r--r--include/osmocom/gapk/bench.h49
-rw-r--r--include/osmocom/gapk/benchmark.h42
-rw-r--r--include/osmocom/gapk/codecs.h (renamed from include/gapk/codecs.h)52
-rw-r--r--include/osmocom/gapk/common.h22
-rw-r--r--include/osmocom/gapk/formats.h (renamed from include/gapk/formats.h)53
-rw-r--r--include/osmocom/gapk/get_cycles.h (renamed from include/gapk/get_cycles.h)5
-rw-r--r--include/osmocom/gapk/logging.h28
-rw-r--r--include/osmocom/gapk/procqueue.h105
-rw-r--r--include/osmocom/gapk/utils.h (renamed from include/gapk/utils.h)5
-rw-r--r--libosmogapk.pc.in11
-rw-r--r--src/Makefile.am105
-rw-r--r--src/app_osmo_gapk.c (renamed from src/main.c)353
-rw-r--r--src/benchmark.c92
-rw-r--r--src/codec_amr.c17
-rw-r--r--src/codec_efr.c15
-rw-r--r--src/codec_fr.c7
-rw-r--r--src/codec_hr.c7
-rw-r--r--src/codec_pcm.c4
-rw-r--r--src/codecs.c16
-rw-r--r--src/common.c38
-rw-r--r--src/fmt_amr.c8
-rw-r--r--src/fmt_amr_opencore.c8
-rw-r--r--src/fmt_gsm.c6
-rw-r--r--src/fmt_hr_ref.c10
-rw-r--r--src/fmt_racal.c12
-rw-r--r--src/fmt_rawpcm.c6
-rw-r--r--src/fmt_rtp_amr.c8
-rw-r--r--src/fmt_rtp_efr.c8
-rw-r--r--src/fmt_rtp_hr_etsi.c8
-rw-r--r--src/fmt_rtp_hr_ietf.c8
-rw-r--r--src/fmt_ti.c12
-rw-r--r--src/formats.c46
-rw-r--r--src/libosmogapk.map4
-rw-r--r--src/pq_alsa.c50
-rw-r--r--src/pq_codec.c34
-rw-r--r--src/pq_file.c42
-rw-r--r--src/pq_format.c35
-rw-r--r--src/pq_rtp.c45
-rw-r--r--src/procqueue.c230
-rw-r--r--tests/Makefile.am93
-rw-r--r--tests/common.sh (renamed from test/common.sh)10
-rw-r--r--tests/io/io_sample.txt1
-rw-r--r--tests/io/pq_file_test.c149
-rw-r--r--tests/io/pq_file_test.ok12
-rw-r--r--tests/io/pq_rtp_test.c349
-rw-r--r--tests/io/pq_rtp_test.ok10
-rwxr-xr-xtests/play_all_formats.sh (renamed from test/play_all_formats.sh)0
-rw-r--r--tests/procqueue/pq_test.c373
-rw-r--r--tests/procqueue/pq_test.ok60
-rw-r--r--tests/ref-files/hhgttg_part1_5.s16 (renamed from test/ref-files/hhgttg_part1_5.s16)bin80000 -> 80000 bytes
-rw-r--r--tests/ref-files/hhgttg_part1_5.s16.amr-efr (renamed from test/ref-files/hhgttg_part1_5.s16.amr-efr)bin8006 -> 8006 bytes
-rw-r--r--tests/ref-files/hhgttg_part1_5.s16.amr-efr.s16 (renamed from test/ref-files/hhgttg_part1_5.s16.amr-efr.s16)bin80000 -> 80000 bytes
-rw-r--r--tests/ref-files/hhgttg_part1_5.s16.gsm (renamed from test/ref-files/hhgttg_part1_5.s16.gsm)bin8250 -> 8250 bytes
-rw-r--r--tests/ref-files/hhgttg_part1_5.s16.gsm.s16 (renamed from test/ref-files/hhgttg_part1_5.s16.gsm.s16)bin80000 -> 80000 bytes
-rw-r--r--tests/ref-files/hhgttg_part1_5.s16.racal-efr (renamed from test/ref-files/hhgttg_part1_5.s16.racal-efr)bin7750 -> 7750 bytes
-rw-r--r--tests/ref-files/hhgttg_part1_5.s16.racal-efr.s16 (renamed from test/ref-files/hhgttg_part1_5.s16.racal-efr.s16)bin80000 -> 80000 bytes
-rw-r--r--tests/ref-files/hhgttg_part1_5.s16.racal-fr (renamed from test/ref-files/hhgttg_part1_5.s16.racal-fr)bin8250 -> 8250 bytes
-rw-r--r--tests/ref-files/hhgttg_part1_5.s16.racal-fr.s16 (renamed from test/ref-files/hhgttg_part1_5.s16.racal-fr.s16)bin80000 -> 80000 bytes
-rw-r--r--tests/ref-files/hhgttg_part1_5.s16.racal-hr (renamed from test/ref-files/hhgttg_part1_5.s16.racal-hr)bin3500 -> 3500 bytes
-rw-r--r--tests/ref-files/hhgttg_part1_5.s16.racal-hr.s16 (renamed from test/ref-files/hhgttg_part1_5.s16.racal-hr.s16)bin80000 -> 80000 bytes
-rw-r--r--tests/ref-files/hhgttg_part1_5.s16.rtp-efr (renamed from test/ref-files/hhgttg_part1_5.s16.rtp-efr)bin7750 -> 7750 bytes
-rw-r--r--tests/ref-files/hhgttg_part1_5.s16.rtp-efr.s16 (renamed from test/ref-files/hhgttg_part1_5.s16.rtp-efr.s16)bin80000 -> 80000 bytes
-rw-r--r--tests/ref-files/hhgttg_part1_5.s16.rtp-hr-etsi (renamed from test/ref-files/hhgttg_part1_5.s16.rtp-hr-etsi)bin3500 -> 3500 bytes
-rw-r--r--tests/ref-files/hhgttg_part1_5.s16.rtp-hr-etsi.s16 (renamed from test/ref-files/hhgttg_part1_5.s16.rtp-hr-etsi.s16)bin80000 -> 80000 bytes
-rw-r--r--tests/ref-files/hhgttg_part1_5.s16.rtp-hr-ietf (renamed from test/ref-files/hhgttg_part1_5.s16.rtp-hr-ietf)bin3750 -> 3750 bytes
-rw-r--r--tests/ref-files/hhgttg_part1_5.s16.rtp-hr-ietf.s16 (renamed from test/ref-files/hhgttg_part1_5.s16.rtp-hr-ietf.s16)bin80000 -> 80000 bytes
-rw-r--r--tests/ref-files/hhgttg_part1_5.s16.ti-efr (renamed from test/ref-files/hhgttg_part1_5.s16.ti-efr)bin8250 -> 8250 bytes
-rw-r--r--tests/ref-files/hhgttg_part1_5.s16.ti-efr.s16 (renamed from test/ref-files/hhgttg_part1_5.s16.ti-efr.s16)bin80000 -> 80000 bytes
-rw-r--r--tests/ref-files/hhgttg_part1_5.s16.ti-fr (renamed from test/ref-files/hhgttg_part1_5.s16.ti-fr)bin8250 -> 8250 bytes
-rw-r--r--tests/ref-files/hhgttg_part1_5.s16.ti-fr.s16 (renamed from test/ref-files/hhgttg_part1_5.s16.ti-fr.s16)bin80000 -> 80000 bytes
-rw-r--r--tests/ref-files/hhgttg_part1_5.s16.ti-hr (renamed from test/ref-files/hhgttg_part1_5.s16.ti-hr)bin8250 -> 8250 bytes
-rw-r--r--tests/ref-files/hhgttg_part1_5.s16.ti-hr.s16 (renamed from test/ref-files/hhgttg_part1_5.s16.ti-hr.s16)bin80000 -> 80000 bytes
-rwxr-xr-xtests/test_all_formats.sh (renamed from test/test_all_formats.sh)0
-rw-r--r--tests/testsuite.at244
-rwxr-xr-xtests/update_ref_files.sh (renamed from test/update_ref_files.sh)0
84 files changed, 2514 insertions, 593 deletions
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 <http://www.gnu.org/licenses/>.
- *
- * (C) 2014 Harald Welte <laforge@gnumonks.org>
- */
-
-#include <gapk/get_cycles.h>
-#include <gapk/codecs.h>
-
-#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 <http://www.gnu.org/licenses/>.
- */
-
-#ifndef __GAPK_PROCQUEUE_H__
-#define __GAPK_PROCQUEUE_H__
-
-#include <stdint.h>
-#include <stdio.h> /* 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 <http://www.gnu.org/licenses/>.
*/
-#ifndef __GSM_HR_H__
-#define __GSM_HR_H__
+#pragma once
#include <stdint.h>
@@ -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 <laforge@gnumonks.org>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#pragma once
+
+#include <osmocom/gapk/get_cycles.h>
+#include <osmocom/gapk/benchmark.h>
+#include <osmocom/gapk/codecs.h>
+
+#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 <http://www.gnu.org/licenses/>.
+ *
+ * (C) 2014 Harald Welte <laforge@gnumonks.org>
+ */
+
+#pragma once
+
+#include <osmocom/gapk/get_cycles.h>
+#include <osmocom/gapk/codecs.h>
+
+#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
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 <http://www.gnu.org/licenses/>.
*/
-#ifndef __GAPK_CODECS_H__
-#define __GAPK_CODECS_H__
+#pragma once
#include <stdint.h>
@@ -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 <gapk/formats.h> /* need to import here because or enum interdep */
+/* Need to import here because of enum interdep */
+#include <osmocom/gapk/formats.h>
/*! 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;
-const struct codec_desc *codec_get_from_type(enum codec_type 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;
+};
-#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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#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
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 <http://www.gnu.org/licenses/>.
*/
-#ifndef __GAPK_FORMATS_H__
-#define __GAPK_FORMATS_H__
+#pragma once
#include <stdint.h>
-enum format_type {
+enum osmo_gapk_format_type {
FMT_INVALID = 0,
/* Classic .amr container */
@@ -62,33 +61,37 @@ enum format_type {
_FMT_MAX,
};
-#include <gapk/codecs.h> /* need to import here because or enum interdep */
+/* Need to import here because of enum interdep */
+#include <osmocom/gapk/codecs.h>
/*! 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);
-
-struct format_desc {
- enum format_type type;
- enum codec_type codec_type;
- const char * name;
- const char * description;
-
- /*! 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 a (global) header at start of file */
- unsigned int header_len;
- /*! exact match for (global) header at start of file */
- const uint8_t * header;
+typedef int (*osmo_gapk_fmt_conv_cb_t)(uint8_t *dst,
+ const uint8_t *src, unsigned int src_len);
+
+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;
+
+ /* 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
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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#pragma once
+
+#include <osmocom/core/logging.h>
+
+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 <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <stdio.h> /* for FILE */
+
+#include <osmocom/core/linuxlist.h>
+
+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
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 <http://www.gnu.org/licenses/>.
*/
-#ifndef __GAPK_UTILS_H__
-#define __GAPK_UTILS_H__
+#pragma once
#include <stdint.h>
@@ -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
index 58e23c1..729ea25 100644
--- a/src/main.c
+++ b/src/app_osmo_gapk.c
@@ -26,18 +26,25 @@
#include <string.h>
#include <unistd.h>
#include <getopt.h>
+#include <talloc.h>
#include <sys/signal.h>
#include <sys/socket.h>
#include <netinet/in.h>
+#include <osmocom/core/application.h>
+#include <osmocom/core/logging.h>
#include <osmocom/core/socket.h>
+#include <osmocom/core/utils.h>
-#include <gapk/codecs.h>
-#include <gapk/formats.h>
-#include <gapk/procqueue.h>
-#include <gapk/benchmark.h>
+#include <osmocom/gapk/common.h>
+#include <osmocom/gapk/codecs.h>
+#include <osmocom/gapk/formats.h>
+#include <osmocom/gapk/procqueue.h>
+#include <osmocom/gapk/benchmark.h>
+/* 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;
- }
-
- /* 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");
+ /* Check I/O combinations */
+ int src_count = 0;
+ int sink_count = 0;
+
+ 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;
+ }
+
+ LOGP(DAPP, LOGL_NOTICE, "Processed %d frames\n", frames);
- fprintf(stderr, "[+] 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 <laforge@gnumonks.org>
*/
-#include <stdio.h>
+#include <talloc.h>
+#include <errno.h>
-#include <gapk/benchmark.h>
+#include <osmocom/gapk/benchmark.h>
+#include <osmocom/gapk/codecs.h>
-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;
- 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);
- }
+ /* 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++)
+ 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 <http://www.gnu.org/licenses/>.
*/
-#include <gapk/codecs.h>
-#include <gapk/benchmark.h>
+#include <osmocom/gapk/codecs.h>
+#include <osmocom/gapk/benchmark.h>
+#include <osmocom/gapk/bench.h>
#include "config.h"
#ifdef HAVE_OPENCORE_AMRNB
-#include <stdlib.h>
-#include <stdio.h>
+#include <talloc.h>
#include <opencore-amrnb/interf_dec.h>
#include <opencore-amrnb/interf_enc.h>
+/* 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 <http://www.gnu.org/licenses/>.
*/
-#include <gapk/codecs.h>
-#include <gapk/benchmark.h>
+#include <osmocom/gapk/codecs.h>
+#include <osmocom/gapk/benchmark.h>
+#include <osmocom/gapk/bench.h>
#include "config.h"
#ifdef HAVE_OPENCORE_AMRNB
-#include <stdlib.h>
+#include <talloc.h>
#include <assert.h>
#include <opencore-amrnb/interf_dec.h>
#include <opencore-amrnb/interf_enc.h>
+/* 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 <assert.h>
-#include <gapk/codecs.h>
-#include <gapk/benchmark.h>
+#include <osmocom/gapk/codecs.h>
+#include <osmocom/gapk/benchmark.h>
+#include <osmocom/gapk/bench.h>
#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 <assert.h>
-#include <gapk/codecs.h>
-#include <gapk/benchmark.h>
+#include <osmocom/gapk/codecs.h>
+#include <osmocom/gapk/benchmark.h>
+#include <osmocom/gapk/bench.h>
#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 <http://www.gnu.org/licenses/>.
*/
-#include <gapk/codecs.h>
+#include <osmocom/gapk/codecs.h>
-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 <stdio.h> /* for NULL */
-#include <gapk/codecs.h>
+#include <osmocom/gapk/codecs.h>
/* 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 <axilirator@gmail.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <talloc.h>
+
+/* 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 <osmocom/codec/codec.h>
-#include <gapk/codecs.h>
-#include <gapk/formats.h>
-#include <gapk/utils.h>
+#include <osmocom/gapk/codecs.h>
+#include <osmocom/gapk/formats.h>
+#include <osmocom/gapk/utils.h>
#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 <stdint.h>
#include <string.h>
-#include <gapk/codecs.h>
-#include <gapk/formats.h>
-#include <gapk/utils.h>
+#include <osmocom/gapk/codecs.h>
+#include <osmocom/gapk/formats.h>
+#include <osmocom/gapk/utils.h>
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 <assert.h>
-#include <gapk/codecs.h>
-#include <gapk/formats.h>
+#include <osmocom/gapk/codecs.h>
+#include <osmocom/gapk/formats.h>
#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 <assert.h>
-#include <gapk/codecs.h>
-#include <gapk/formats.h>
-#include <gapk/utils.h>
+#include <osmocom/gapk/codecs.h>
+#include <osmocom/gapk/formats.h>
+#include <osmocom/gapk/utils.h>
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 <osmocom/codec/codec.h>
-#include <gapk/codecs.h>
-#include <gapk/formats.h>
-#include <gapk/utils.h>
+#include <osmocom/gapk/codecs.h>
+#include <osmocom/gapk/formats.h>
+#include <osmocom/gapk/utils.h>
#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 <assert.h>
-#include <gapk/codecs.h>
-#include <gapk/formats.h>
+#include <osmocom/gapk/codecs.h>
+#include <osmocom/gapk/formats.h>
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 <osmocom/codec/codec.h>
-#include <gapk/codecs.h>
-#include <gapk/formats.h>
-#include <gapk/utils.h>
+#include <osmocom/gapk/codecs.h>
+#include <osmocom/gapk/formats.h>
+#include <osmocom/gapk/utils.h>
/* 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 <osmocom/codec/codec.h>
-#include <gapk/codecs.h>
-#include <gapk/formats.h>
-#include <gapk/utils.h>
+#include <osmocom/gapk/codecs.h>
+#include <osmocom/gapk/formats.h>
+#include <osmocom/gapk/utils.h>
#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 <assert.h>
#include <string.h>
-#include <gapk/codecs.h>
-#include <gapk/formats.h>
-#include <gapk/utils.h>
+#include <osmocom/gapk/codecs.h>
+#include <osmocom/gapk/formats.h>
+#include <osmocom/gapk/utils.h>
/* 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 <assert.h>
#include <string.h>
-#include <gapk/codecs.h>
-#include <gapk/formats.h>
-#include <gapk/utils.h>
+#include <osmocom/gapk/codecs.h>
+#include <osmocom/gapk/formats.h>
+#include <osmocom/gapk/utils.h>
#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 <osmocom/codec/codec.h>
-#include <gapk/codecs.h>
-#include <gapk/formats.h>
-#include <gapk/utils.h>
+#include <osmocom/gapk/codecs.h>
+#include <osmocom/gapk/formats.h>
+#include <osmocom/gapk/utils.h>
#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 <stdio.h> /* for NULL */
#include <string.h>
-#include <gapk/formats.h>
+#include <osmocom/gapk/formats.h>
/* 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 <errno.h>
#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
+#include <talloc.h>
-#include <gapk/codecs.h>
-#include <gapk/formats.h>
-#include <gapk/procqueue.h>
+#include <osmocom/gapk/logging.h>
+#include <osmocom/gapk/codecs.h>
+#include <osmocom/gapk/formats.h>
+#include <osmocom/gapk/procqueue.h>
#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 <errno.h>
#include <stdint.h>
-#include <gapk/codecs.h>
-#include <gapk/formats.h>
-#include <gapk/procqueue.h>
+#include <osmocom/gapk/logging.h>
+#include <osmocom/gapk/codecs.h>
+#include <osmocom/gapk/formats.h>
+#include <osmocom/gapk/procqueue.h>
/*! 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 <errno.h>
#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
+#include <talloc.h>
-#include <gapk/codecs.h>
-#include <gapk/formats.h>
-#include <gapk/procqueue.h>
+#include <osmocom/gapk/logging.h>
+#include <osmocom/gapk/codecs.h>
+#include <osmocom/gapk/formats.h>
+#include <osmocom/gapk/procqueue.h>
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 <errno.h>
#include <stdint.h>
-#include <gapk/codecs.h>
-#include <gapk/formats.h>
-#include <gapk/procqueue.h>
+#include <osmocom/gapk/logging.h>
+#include <osmocom/gapk/codecs.h>
+#include <osmocom/gapk/formats.h>
+#include <osmocom/gapk/procqueue.h>
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 <errno.h>
#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
#include <unistd.h>
#include <string.h>
+#include <talloc.h>
#include <arpa/inet.h>
-#include <gapk/codecs.h>
-#include <gapk/formats.h>
-#include <gapk/procqueue.h>
+#include <osmocom/gapk/logging.h>
+#include <osmocom/gapk/codecs.h>
+#include <osmocom/gapk/formats.h>
+#include <osmocom/gapk/procqueue.h>
#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 <errno.h>
#include <stdint.h>
#include <stdlib.h>
+#include <talloc.h>
-#include <gapk/procqueue.h>
+#include <osmocom/core/linuxlist.h>
-#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 <osmocom/gapk/procqueue.h>
+#include <osmocom/gapk/logging.h>
+/* 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; i<pq->n_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);
- for (i=0; i<pq->n_items-1; i++)
- free(pq->buffers[i]); /* free is NULL safe */
+ /* Call exit handler if preset */
+ if (item->exit)
+ item->exit(item->state);
- free(pq);
+ /* Delete an item from list */
+ llist_del(&item->list);
+ talloc_free(item);
+ }
+
+ 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;
-
- if (pq->n_items == MAX_PQ_ITEMS) {
- fprintf(stderr, "[!] Processing Queue cannot handle more than %u items\n",
- MAX_PQ_ITEMS);
- return NULL;
- }
+ struct osmo_gapk_pq_item *item;
- 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
- * \param[in] pq Processing Queue to be prepared
+/*! 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
-pq_prepare(struct pq *pq)
+osmo_gapk_pq_check(struct osmo_gapk_pq *pq, int strict)
{
- int i;
- unsigned int len_prev;
+ 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;
+ }
+ }
- len_prev = 0;
+ /* Save pointer to the previous item */
+ item_prev = item;
+ }
- for (i=0; i<pq->n_items; i++) {
- struct pq_item *item = pq->items[i];
+ 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;
+ }
- 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;
- }
+ 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;
+}
- if (i < (pq->n_items-1)) {
+/*! prepare a processing queue; allocates buffers
+ * \param[in] pq Processing Queue to be prepared
+ * \returns 0 on succcess; negative on error */
+int
+osmo_gapk_pq_prepare(struct osmo_gapk_pq *pq)
+{
+ struct osmo_gapk_pq_item *item;
+
+ /* 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])
+
+ /* Allocate memory for an output buffer */
+ item->buf = talloc_named_const(item, buf_size, ".buffer");
+ if (!item->buf)
return -ENOMEM;
- } else{
- if (item->len_out)
- return -EINVAL;
}
-
- len_prev = item->len_out;
}
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;
-
- buf_prev = NULL;
- len_prev = 0;
-
- for (i=0; i<pq->n_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);
+ struct osmo_gapk_pq_item *item;
+ unsigned int len_prev = 0;
+ uint8_t *buf_prev = NULL;
+ int rv;
+
+ /* 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
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 <axilirator@gmail.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <talloc.h>
+#include <assert.h>
+
+#include <osmocom/gapk/procqueue.h>
+#include <osmocom/gapk/common.h>
+
+/**
+ * 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 <axilirator@gmail.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <time.h>
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <talloc.h>
+#include <assert.h>
+#include <unistd.h>
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <osmocom/core/socket.h>
+
+#include <osmocom/gapk/procqueue.h>
+#include <osmocom/gapk/common.h>
+
+/**
+ * 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
index 5cebffb..5cebffb 100755
--- a/test/play_all_formats.sh
+++ b/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 <axilirator@gmail.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+#include <talloc.h>
+#include <assert.h>
+
+#include <osmocom/gapk/procqueue.h>
+#include <osmocom/gapk/common.h>
+
+/**
+ * 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
index 80a594d..80a594d 100644
--- a/test/ref-files/hhgttg_part1_5.s16
+++ b/tests/ref-files/hhgttg_part1_5.s16
Binary files differ
diff --git a/test/ref-files/hhgttg_part1_5.s16.amr-efr b/tests/ref-files/hhgttg_part1_5.s16.amr-efr
index 00a11b0..00a11b0 100644
--- a/test/ref-files/hhgttg_part1_5.s16.amr-efr
+++ b/tests/ref-files/hhgttg_part1_5.s16.amr-efr
Binary files differ
diff --git a/test/ref-files/hhgttg_part1_5.s16.amr-efr.s16 b/tests/ref-files/hhgttg_part1_5.s16.amr-efr.s16
index 829ed67..829ed67 100644
--- a/test/ref-files/hhgttg_part1_5.s16.amr-efr.s16
+++ b/tests/ref-files/hhgttg_part1_5.s16.amr-efr.s16
Binary files differ
diff --git a/test/ref-files/hhgttg_part1_5.s16.gsm b/tests/ref-files/hhgttg_part1_5.s16.gsm
index ad3cf8f..ad3cf8f 100644
--- a/test/ref-files/hhgttg_part1_5.s16.gsm
+++ b/tests/ref-files/hhgttg_part1_5.s16.gsm
Binary files differ
diff --git a/test/ref-files/hhgttg_part1_5.s16.gsm.s16 b/tests/ref-files/hhgttg_part1_5.s16.gsm.s16
index 28aa317..28aa317 100644
--- a/test/ref-files/hhgttg_part1_5.s16.gsm.s16
+++ b/tests/ref-files/hhgttg_part1_5.s16.gsm.s16
Binary files differ
diff --git a/test/ref-files/hhgttg_part1_5.s16.racal-efr b/tests/ref-files/hhgttg_part1_5.s16.racal-efr
index b96be2e..b96be2e 100644
--- a/test/ref-files/hhgttg_part1_5.s16.racal-efr
+++ b/tests/ref-files/hhgttg_part1_5.s16.racal-efr
Binary files differ
diff --git a/test/ref-files/hhgttg_part1_5.s16.racal-efr.s16 b/tests/ref-files/hhgttg_part1_5.s16.racal-efr.s16
index 829ed67..829ed67 100644
--- a/test/ref-files/hhgttg_part1_5.s16.racal-efr.s16
+++ b/tests/ref-files/hhgttg_part1_5.s16.racal-efr.s16
Binary files differ
diff --git a/test/ref-files/hhgttg_part1_5.s16.racal-fr b/tests/ref-files/hhgttg_part1_5.s16.racal-fr
index e0a5397..e0a5397 100644
--- a/test/ref-files/hhgttg_part1_5.s16.racal-fr
+++ b/tests/ref-files/hhgttg_part1_5.s16.racal-fr
Binary files differ
diff --git a/test/ref-files/hhgttg_part1_5.s16.racal-fr.s16 b/tests/ref-files/hhgttg_part1_5.s16.racal-fr.s16
index 28aa317..28aa317 100644
--- a/test/ref-files/hhgttg_part1_5.s16.racal-fr.s16
+++ b/tests/ref-files/hhgttg_part1_5.s16.racal-fr.s16
Binary files differ
diff --git a/test/ref-files/hhgttg_part1_5.s16.racal-hr b/tests/ref-files/hhgttg_part1_5.s16.racal-hr
index 95881af..95881af 100644
--- a/test/ref-files/hhgttg_part1_5.s16.racal-hr
+++ b/tests/ref-files/hhgttg_part1_5.s16.racal-hr
Binary files differ
diff --git a/test/ref-files/hhgttg_part1_5.s16.racal-hr.s16 b/tests/ref-files/hhgttg_part1_5.s16.racal-hr.s16
index 27dfd5f..27dfd5f 100644
--- a/test/ref-files/hhgttg_part1_5.s16.racal-hr.s16
+++ b/tests/ref-files/hhgttg_part1_5.s16.racal-hr.s16
Binary files differ
diff --git a/test/ref-files/hhgttg_part1_5.s16.rtp-efr b/tests/ref-files/hhgttg_part1_5.s16.rtp-efr
index 9e4615e..9e4615e 100644
--- a/test/ref-files/hhgttg_part1_5.s16.rtp-efr
+++ b/tests/ref-files/hhgttg_part1_5.s16.rtp-efr
Binary files differ
diff --git a/test/ref-files/hhgttg_part1_5.s16.rtp-efr.s16 b/tests/ref-files/hhgttg_part1_5.s16.rtp-efr.s16
index 829ed67..829ed67 100644
--- a/test/ref-files/hhgttg_part1_5.s16.rtp-efr.s16
+++ b/tests/ref-files/hhgttg_part1_5.s16.rtp-efr.s16
Binary files differ
diff --git a/test/ref-files/hhgttg_part1_5.s16.rtp-hr-etsi b/tests/ref-files/hhgttg_part1_5.s16.rtp-hr-etsi
index 9a145d3..9a145d3 100644
--- a/test/ref-files/hhgttg_part1_5.s16.rtp-hr-etsi
+++ b/tests/ref-files/hhgttg_part1_5.s16.rtp-hr-etsi
Binary files differ
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
index 27dfd5f..27dfd5f 100644
--- a/test/ref-files/hhgttg_part1_5.s16.rtp-hr-etsi.s16
+++ b/tests/ref-files/hhgttg_part1_5.s16.rtp-hr-etsi.s16
Binary files differ
diff --git a/test/ref-files/hhgttg_part1_5.s16.rtp-hr-ietf b/tests/ref-files/hhgttg_part1_5.s16.rtp-hr-ietf
index e956e9d..e956e9d 100644
--- a/test/ref-files/hhgttg_part1_5.s16.rtp-hr-ietf
+++ b/tests/ref-files/hhgttg_part1_5.s16.rtp-hr-ietf
Binary files differ
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
index 27dfd5f..27dfd5f 100644
--- a/test/ref-files/hhgttg_part1_5.s16.rtp-hr-ietf.s16
+++ b/tests/ref-files/hhgttg_part1_5.s16.rtp-hr-ietf.s16
Binary files differ
diff --git a/test/ref-files/hhgttg_part1_5.s16.ti-efr b/tests/ref-files/hhgttg_part1_5.s16.ti-efr
index 9dd01ae..9dd01ae 100644
--- a/test/ref-files/hhgttg_part1_5.s16.ti-efr
+++ b/tests/ref-files/hhgttg_part1_5.s16.ti-efr
Binary files differ
diff --git a/test/ref-files/hhgttg_part1_5.s16.ti-efr.s16 b/tests/ref-files/hhgttg_part1_5.s16.ti-efr.s16
index e8f759f..e8f759f 100644
--- a/test/ref-files/hhgttg_part1_5.s16.ti-efr.s16
+++ b/tests/ref-files/hhgttg_part1_5.s16.ti-efr.s16
Binary files differ
diff --git a/test/ref-files/hhgttg_part1_5.s16.ti-fr b/tests/ref-files/hhgttg_part1_5.s16.ti-fr
index 5bf4b83..5bf4b83 100644
--- a/test/ref-files/hhgttg_part1_5.s16.ti-fr
+++ b/tests/ref-files/hhgttg_part1_5.s16.ti-fr
Binary files differ
diff --git a/test/ref-files/hhgttg_part1_5.s16.ti-fr.s16 b/tests/ref-files/hhgttg_part1_5.s16.ti-fr.s16
index 28aa317..28aa317 100644
--- a/test/ref-files/hhgttg_part1_5.s16.ti-fr.s16
+++ b/tests/ref-files/hhgttg_part1_5.s16.ti-fr.s16
Binary files differ
diff --git a/test/ref-files/hhgttg_part1_5.s16.ti-hr b/tests/ref-files/hhgttg_part1_5.s16.ti-hr
index 29a16bf..29a16bf 100644
--- a/test/ref-files/hhgttg_part1_5.s16.ti-hr
+++ b/tests/ref-files/hhgttg_part1_5.s16.ti-hr
Binary files differ
diff --git a/test/ref-files/hhgttg_part1_5.s16.ti-hr.s16 b/tests/ref-files/hhgttg_part1_5.s16.ti-hr.s16
index 27dfd5f..27dfd5f 100644
--- a/test/ref-files/hhgttg_part1_5.s16.ti-hr.s16
+++ b/tests/ref-files/hhgttg_part1_5.s16.ti-hr.s16
Binary files differ
diff --git a/test/test_all_formats.sh b/tests/test_all_formats.sh
index 6da27af..6da27af 100755
--- a/test/test_all_formats.sh
+++ b/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
index 1d095ef..1d095ef 100755
--- a/test/update_ref_files.sh
+++ b/tests/update_ref_files.sh