summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJonas Smedegaard <dr@jones.dk>2018-01-08 14:56:48 +0100
committerJonas Smedegaard <dr@jones.dk>2018-01-08 14:56:48 +0100
commitfe7cb816ef28e35f4c6e6724604b4cc5b15dc92c (patch)
tree788c26905465482a58346f5ccaecbfbdfc110bd4
parent03cac4fb3669ffe76f11057ead5b241edaec32b3 (diff)
parentb7869a262cbca2241c70549af9c877529706c15f (diff)
Update upstream source from tag 'upstream/0.5.7'
Update to upstream version '0.5.7' with Debian dir 7917999b8a336b3f50f0f8f70d106923ec5ecf5e
-rw-r--r--Makefile2
-rw-r--r--README.md5
-rw-r--r--docs/ChangeLog93
-rw-r--r--docs/THANKS1
-rw-r--r--docs/examples/config3
-rw-r--r--include/baresip.h38
-rw-r--r--mk/Doxyfile2
-rw-r--r--mk/modules.mk10
-rw-r--r--modules/alsa/alsa.c22
-rw-r--r--modules/alsa/alsa.h2
-rw-r--r--modules/alsa/alsa_play.c30
-rw-r--r--modules/alsa/alsa_src.c34
-rw-r--r--modules/aubridge/play.c7
-rw-r--r--modules/aubridge/src.c7
-rw-r--r--modules/audiounit/player.c29
-rw-r--r--modules/audiounit/recorder.c30
-rw-r--r--modules/aufile/aufile.c6
-rw-r--r--modules/auloop/auloop.c37
-rw-r--r--modules/avahi/avahi.c2
-rw-r--r--modules/avcodec/encode.c4
-rw-r--r--modules/avformat/avformat.c14
-rw-r--r--modules/coreaudio/player.c3
-rw-r--r--modules/coreaudio/recorder.c2
-rw-r--r--modules/debug_cmd/debug_cmd.c22
-rw-r--r--modules/gst/gst.c2
-rw-r--r--modules/gst1/gst.c8
-rw-r--r--modules/gzrtp/gzrtp.cpp220
-rw-r--r--modules/gzrtp/messages.cpp256
-rw-r--r--modules/gzrtp/module.mk38
-rw-r--r--modules/gzrtp/session.cpp214
-rw-r--r--modules/gzrtp/session.h50
-rw-r--r--modules/gzrtp/srtp.cpp327
-rw-r--r--modules/gzrtp/srtp.h46
-rw-r--r--modules/gzrtp/stream.cpp707
-rw-r--r--modules/gzrtp/stream.h138
-rw-r--r--modules/h265/fmt.c9
-rw-r--r--modules/jack/jack_play.c6
-rw-r--r--modules/jack/jack_src.c6
-rw-r--r--modules/menu/menu.c22
-rw-r--r--modules/mqtt/README.md59
-rw-r--r--modules/mqtt/module.mk14
-rw-r--r--modules/mqtt/mqtt.c157
-rw-r--r--modules/mqtt/mqtt.h26
-rw-r--r--modules/mqtt/publish.c104
-rw-r--r--modules/mqtt/subscribe.c146
-rw-r--r--modules/omx/module.c4
-rw-r--r--modules/omx/omx.c4
-rw-r--r--modules/opensles/player.c7
-rw-r--r--modules/opensles/recorder.c7
-rw-r--r--modules/opus/opus.c15
-rw-r--r--modules/oss/oss.c4
-rw-r--r--modules/portaudio/portaudio.c15
-rw-r--r--modules/pulse/player.c21
-rw-r--r--modules/pulse/recorder.c54
-rw-r--r--modules/quicktime/module.mk11
-rw-r--r--modules/quicktime/quicktime.c323
-rw-r--r--modules/rst/audio.c28
-rw-r--r--modules/sndio/sndio.c12
-rw-r--r--modules/v4l2/module.mk2
-rw-r--r--modules/v4l2/v4l2.c4
-rw-r--r--modules/vidloop/vidloop.c3
-rw-r--r--modules/winwave/play.c6
-rw-r--r--modules/winwave/src.c6
-rw-r--r--modules/zrtp/zrtp.c148
-rw-r--r--rpm/baresip.spec2
-rw-r--r--src/account.c2
-rw-r--r--src/audio.c475
-rw-r--r--src/cmd.c34
-rw-r--r--src/conf.c2
-rw-r--r--src/config.c105
-rw-r--r--src/core.h68
-rw-r--r--src/event.c171
-rw-r--r--src/log.c6
-rw-r--r--src/main.c2
-rw-r--r--src/play.c3
-rw-r--r--src/srcs.mk1
-rw-r--r--src/stream.c14
-rw-r--r--src/ua.c30
-rw-r--r--src/video.c4
-rw-r--r--test/call.c87
-rw-r--r--test/main.c1
-rw-r--r--test/mock/mock_auplay.c5
-rw-r--r--test/mock/mock_ausrc.c5
-rw-r--r--test/play.c2
-rw-r--r--test/test.h3
85 files changed, 4058 insertions, 598 deletions
diff --git a/Makefile b/Makefile
index 887ff35..f5b4c25 100644
--- a/Makefile
+++ b/Makefile
@@ -13,7 +13,7 @@
USE_VIDEO := 1
PROJECT := baresip
-VERSION := 0.5.6
+VERSION := 0.5.7
DESCR := "Baresip is a modular SIP User-Agent with audio and video support"
# Verbose and silent build modes
diff --git a/README.md b/README.md
index 18f9ca1..8edddee 100644
--- a/README.md
+++ b/README.md
@@ -106,7 +106,7 @@ Distributed under BSD license
- FFmpeg/libav libavformat/avdevice input
- Cairo video-source test module
- Direct Show video-source
- - MacOSX QTcapture/quicktime video-source
+ - MacOSX QTcapture/AVCapture video-source
- RST media player
- Linux V4L/V4L2 video-source
- X11 grabber video-source
@@ -246,6 +246,7 @@ gst1 Gstreamer 1.0 audio source
gst_video Gstreamer video codec
gst_video1 Gstreamer 1.0 video codec
gtk GTK+ 2.0 UI
+gzrtp ZRTP module using GNU ZRTP C++ library
h265 H.265 video codec
httpd HTTP webserver UI-module
ice ICE protocol for NAT Traversal
@@ -256,6 +257,7 @@ l16 L16 audio codec
libsrtp Secure RTP encryption using libsrtp
menu Interactive menu
mpa MPA Speech and Audio Codec
+mqtt MQTT (Message Queue Telemetry Transport) module
mwi Message Waiting Indication
natbd NAT Behavior Discovery Module
natpmp NAT Port Mapping Protocol (NAT-PMP) module
@@ -271,7 +273,6 @@ portaudio Portaudio driver
pulse Pulseaudio driver
presence Presence module
qtcapture Apple QTCapture video source driver
-quicktime Apple Quicktime video source driver (deprecated)
rst Radio streamer using mpg123
sdl Simple DirectMedia Layer (SDL) video output driver
sdl2 Simple DirectMedia Layer v2 (SDL2) video output driver
diff --git a/docs/ChangeLog b/docs/ChangeLog
index 10c9a41..d79818e 100644
--- a/docs/ChangeLog
+++ b/docs/ChangeLog
@@ -1,3 +1,96 @@
+2017-12-25 Alfred E. Heggestad <alfred.heggestad@gmail.com>
+
+ * Version 0.5.7
+
+ * GIT URL: https://github.com/alfredh/baresip.git
+ * GIT tag: v0.5.7
+ * NOTE: Requires libre v0.5.5 or later
+ Requires librem v0.5.0 or later
+
+ * Credits: Thanks to Swedish Radio who sponsored many new
+ features in this release.
+
+ * new commands:
+ - 'conf_reload' -- Reload config file
+
+ * new modules:
+ - gzrtp ZRTP module using GNU ZRTP C++ library
+ (thanks glenvt18)
+
+ - mqtt MQTT (Message Queue Telemetry Transport) module
+ (sponsored by Swedish Radio)
+
+ * config:
+ - audio_txmode poll|thread Set audio transmit mode
+ - auplay_format s16|float|s24_3le Set playback sample format
+ - ausrc_format s16|float|s24_3le Set source sample format
+ - sdp_ebuacip yes|no Enable EBU-ACIP parameters
+ - zrtp_hash yes|no Enable/disable ZRTP hash
+
+ * baresip-core:
+ - audio: add sample format conversion
+ - audio: add sample format for source/playback
+ - audio: check timestamps on incoming RTP packets
+ - audio: pace outgoing packets in txmode=thread
+ - audio: remove txmode with realtime thread
+ - audio: remove txmode with timer
+ - audio: set EBUACIP parameters in SDP
+ - auplay: add sample format to auplay_prm
+ - auplay: change write handler to any sample format
+ - ausrc: add sample format to ausrc_prm
+ - ausrc: change read handler to any sample format
+ - event.c: new file for generic event handling
+ - event: add event_encode_dict to encode event to a dictionary
+ - event: added UA_EVENT_CALL_RTCP for received RTCP
+ - log: print to stdout (ref #320)
+
+ * selftest:
+ - add test for different audio tx-modes
+ - add test for float audio sample format
+
+ * Modules:
+
+ * alsa: add support for multiple sample formats
+
+ * audiounit: add support for FLOAT sample format
+
+ * auloop: add support for multiple sample formats
+
+ * avahi: Bugfix: Destroy resolver after callback (#318)
+ (thanks Jonathan Sieber)
+
+ * avcodec: change x264 rate control mode to ABR (#334)
+ (thanks Jonathan Sieber)
+
+ * debug_cmd: add command 'conf_reload' to reload config file
+
+ * gzrtp: ZRTP module using GNU ZRTP C++ library
+ (thanks glenvt18)
+
+ * menu: add config 'ringback_disabled' to disable playing
+ of ringback tone.
+
+ * mqtt: MQTT (Message Queue Telemetry Transport) module
+ new module using libmosquitto as the backend.
+
+ * opus: fix encoder bitrate, ref #305
+ add opus_stereo config parameter (thanks Ola Palm)
+ add config param opus_sprop_stereo (thanks Ola Palm)
+
+ * portaudio: add support for FLOAT sample format
+
+ * pulse: add support for FLOAT sample format
+ remove garbage at the beginning of a recording (#323)
+
+ * quicktime: module was removed
+
+ * rst: add support for multiple sample formats
+
+ * zrtp: add signaling hash support (#311)
+
+
+
+
2017-10-14 Alfred E. Heggestad <alfred.heggestad@gmail.com>
* Version 0.5.6
diff --git a/docs/THANKS b/docs/THANKS
index de0fb7a..b7f0949 100644
--- a/docs/THANKS
+++ b/docs/THANKS
@@ -1,5 +1,6 @@
@GGGO
@elektm93
+@glenvt18
@jungle-boogie
Aaron Herting
AlexMarlo
diff --git a/docs/examples/config b/docs/examples/config
index 0bb37e5..1eabd62 100644
--- a/docs/examples/config
+++ b/docs/examples/config
@@ -170,5 +170,8 @@ ice_debug no
ice_nomination regular # {regular,aggressive}
ice_mode full # {full,lite}
+# ZRTP
+#zrtp_hash no # Disable SDP zrtp-hash (not recommended)
+
# sndfile #
snd_path /tmp/
diff --git a/include/baresip.h b/include/baresip.h
index e394e75..746a95e 100644
--- a/include/baresip.h
+++ b/include/baresip.h
@@ -13,7 +13,7 @@ extern "C" {
/** Defines the Baresip version string */
-#define BARESIP_VERSION "0.5.6"
+#define BARESIP_VERSION "0.5.7"
#ifndef NET_MAX_NS
@@ -26,6 +26,7 @@ struct sa;
struct sdp_media;
struct sdp_session;
struct sip_msg;
+struct stream;
struct ua;
struct vidframe;
struct vidrect;
@@ -167,8 +168,6 @@ static inline bool in_range(const struct range *rng, uint32_t val)
enum audio_mode {
AUDIO_MODE_POLL = 0, /**< Polling mode */
AUDIO_MODE_THREAD, /**< Use dedicated thread */
- AUDIO_MODE_THREAD_REALTIME, /**< Use dedicated realtime-thread */
- AUDIO_MODE_TMR /**< Use timer */
};
@@ -204,6 +203,8 @@ struct config_audio {
bool src_first; /**< Audio source opened first */
enum audio_mode txmode; /**< Audio transmit mode */
bool level; /**< Enable audio level indication */
+ int src_fmt; /**< Audio source sample format */
+ int play_fmt; /**< Audio playback sample format */
};
#ifdef USE_VIDEO
@@ -248,6 +249,11 @@ struct config_bfcp {
};
#endif
+/** SDP */
+struct config_sdp {
+ bool ebuacip; /**< Enable EBU-ACIP parameters */
+};
+
/** Core configuration */
struct config {
@@ -268,6 +274,8 @@ struct config {
#ifdef USE_VIDEO
struct config_bfcp bfcp;
#endif
+
+ struct config_sdp sdp;
};
int config_parse_conf(struct config *cfg, const struct conf *conf);
@@ -358,9 +366,10 @@ struct ausrc_prm {
uint32_t srate; /**< Sampling rate in [Hz] */
uint8_t ch; /**< Number of channels */
uint32_t ptime; /**< Wanted packet-time in [ms] */
+ int fmt; /**< Sample format (enum aufmt) */
};
-typedef void (ausrc_read_h)(const int16_t *sampv, size_t sampc, void *arg);
+typedef void (ausrc_read_h)(const void *sampv, size_t sampc, void *arg);
typedef void (ausrc_error_h)(int err, const char *str, void *arg);
typedef int (ausrc_alloc_h)(struct ausrc_st **stp, const struct ausrc *ausrc,
@@ -390,9 +399,10 @@ struct auplay_prm {
uint32_t srate; /**< Sampling rate in [Hz] */
uint8_t ch; /**< Number of channels */
uint32_t ptime; /**< Wanted packet-time in [ms] */
+ int fmt; /**< Sample format (enum aufmt) */
};
-typedef void (auplay_write_h)(int16_t *sampv, size_t sampc, void *arg);
+typedef void (auplay_write_h)(void *sampv, size_t sampc, void *arg);
typedef int (auplay_alloc_h)(struct auplay_st **stp, const struct auplay *ap,
struct auplay_prm *prm, const char *device,
@@ -577,6 +587,7 @@ enum ua_event {
UA_EVENT_CALL_TRANSFER_FAILED,
UA_EVENT_CALL_DTMF_START,
UA_EVENT_CALL_DTMF_END,
+ UA_EVENT_CALL_RTCP,
UA_EVENT_MAX,
};
@@ -974,6 +985,7 @@ int audio_set_player(struct audio *au, const char *mod, const char *device);
void audio_encoder_cycle(struct audio *audio);
int audio_level_get(const struct audio *au, double *level);
int audio_debug(struct re_printf *pf, const struct audio *a);
+struct stream *audio_strm(const struct audio *a);
/*
@@ -993,6 +1005,14 @@ void video_encoder_cycle(struct video *video);
int video_debug(struct re_printf *pf, const struct video *v);
uint32_t video_calc_rtp_timestamp(int64_t pts, unsigned fps);
double video_calc_seconds(uint32_t rtp_ts);
+struct stream *video_strm(const struct video *v);
+
+
+/*
+ * Generic stream
+ */
+
+const struct rtcp_stats *stream_rtcp_stats(const struct stream *strm);
/*
@@ -1152,6 +1172,14 @@ double mos_calculate(double *r_factor, double rtt,
/*
+ * Generic event
+ */
+
+int event_encode_dict(struct odict *od, struct ua *ua, enum ua_event ev,
+ struct call *call, const char *prm);
+
+
+/*
* Baresip instance
*/
diff --git a/mk/Doxyfile b/mk/Doxyfile
index 30ee43a..3606286 100644
--- a/mk/Doxyfile
+++ b/mk/Doxyfile
@@ -4,7 +4,7 @@
# Project related configuration options
#---------------------------------------------------------------------------
PROJECT_NAME = baresip
-PROJECT_NUMBER = 0.5.6
+PROJECT_NUMBER = 0.5.7
OUTPUT_DIRECTORY = ../baresip-dox
CREATE_SUBDIRS = NO
OUTPUT_LANGUAGE = English
diff --git a/mk/modules.mk b/mk/modules.mk
index 20ff07a..9211dfd 100644
--- a/mk/modules.mk
+++ b/mk/modules.mk
@@ -187,6 +187,9 @@ USE_SPEEX_PP := $(shell [ -f $(SYSROOT)/include/speex_preprocess.h ] || \
USE_SYSLOG := $(shell [ -f $(SYSROOT)/include/syslog.h ] || \
[ -f $(SYSROOT_ALT)/include/syslog.h ] || \
[ -f $(SYSROOT)/local/include/syslog.h ] && echo "yes")
+HAVE_LIBMQTT := $(shell [ -f $(SYSROOT)/include/mosquitto.h ] || \
+ [ -f $(SYSROOT)/local/include/mosquitto.h ] \
+ && echo "yes")
USE_V4L := $(shell [ -f $(SYSROOT)/include/libv4l1.h ] || \
[ -f $(SYSROOT)/local/include/libv4l1.h ] \
&& echo "yes")
@@ -273,6 +276,10 @@ MODULES += srtp
MODULES += uuid
MODULES += debug_cmd
+ifneq ($(HAVE_LIBMQTT),)
+MODULES += mqtt
+endif
+
ifneq ($(HAVE_PTHREAD),)
MODULES += aubridge aufile
endif
@@ -452,6 +459,9 @@ endif
ifneq ($(USE_ZRTP),)
MODULES += zrtp
endif
+ifneq ($(USE_GZRTP),)
+MODULES += gzrtp
+endif
ifneq ($(USE_DSHOW),)
MODULES += dshow
endif
diff --git a/modules/alsa/alsa.c b/modules/alsa/alsa.c
index d695056..78de296 100644
--- a/modules/alsa/alsa.c
+++ b/modules/alsa/alsa.c
@@ -29,7 +29,6 @@
char alsa_dev[64] = "default";
-enum aufmt alsa_sample_format = AUFMT_S16LE;
static struct ausrc *ausrc;
static struct auplay *auplay;
@@ -146,24 +145,13 @@ static int alsa_init(void)
struct pl val;
int err;
+ /* XXX: remove check later */
if (0 == conf_get(conf_cur(), "alsa_sample_format", &val)) {
- if (0 == pl_strcasecmp(&val, "s16")) {
- alsa_sample_format = AUFMT_S16LE;
- }
- else if (0 == pl_strcasecmp(&val, "float")) {
- alsa_sample_format = AUFMT_FLOAT;
- }
- else if (0 == pl_strcasecmp(&val, "s24_3le")) {
- alsa_sample_format = AUFMT_S24_3LE;
- }
- else {
- warning("alsa: unknown sample format '%r'\n", &val);
- return EINVAL;
- }
-
- info("alsa: configured sample format `%s'\n",
- aufmt_name(alsa_sample_format));
+ warning("alsa: alsa_sample_format is deprecated"
+ " -- use ausrc_format or auplay_format instead\n");
+
+ (void)val;
}
err = ausrc_register(&ausrc, baresip_ausrcl(),
diff --git a/modules/alsa/alsa.h b/modules/alsa/alsa.h
index 61c408c..fbd67f2 100644
--- a/modules/alsa/alsa.h
+++ b/modules/alsa/alsa.h
@@ -6,7 +6,7 @@
extern char alsa_dev[64];
-extern enum aufmt alsa_sample_format;
+
int alsa_reset(snd_pcm_t *pcm, uint32_t srate, uint32_t ch,
uint32_t num_frames, snd_pcm_format_t pcmfmt);
diff --git a/modules/alsa/alsa_play.c b/modules/alsa/alsa_play.c
index aca8da2..6547e21 100644
--- a/modules/alsa/alsa_play.c
+++ b/modules/alsa/alsa_play.c
@@ -22,14 +22,12 @@ struct auplay_st {
pthread_t thread;
bool run;
snd_pcm_t *write;
- int16_t *sampv;
- void *xsampv;
+ void *sampv;
size_t sampc;
auplay_write_h *wh;
void *arg;
struct auplay_prm prm;
char *device;
- enum aufmt aufmt;
};
@@ -48,7 +46,6 @@ static void auplay_destructor(void *arg)
snd_pcm_close(st->write);
mem_deref(st->sampv);
- mem_deref(st->xsampv);
mem_deref(st->device);
}
@@ -67,14 +64,7 @@ static void *write_thread(void *arg)
st->wh(st->sampv, st->sampc, st->arg);
- if (st->aufmt == AUFMT_S16LE) {
- sampv = st->sampv;
- }
- else {
- sampv = st->xsampv;
- auconv_from_s16(st->aufmt, st->xsampv,
- st->sampv, st->sampc);
- }
+ sampv = st->sampv;
n = snd_pcm_writei(st->write, sampv, samples);
@@ -127,26 +117,16 @@ int alsa_play_alloc(struct auplay_st **stp, const struct auplay *ap,
st->ap = ap;
st->wh = wh;
st->arg = arg;
- st->aufmt = alsa_sample_format;
st->sampc = prm->srate * prm->ch * prm->ptime / 1000;
num_frames = st->prm.srate * st->prm.ptime / 1000;
- st->sampv = mem_alloc(2 * st->sampc, NULL);
+ st->sampv = mem_alloc(aufmt_sample_size(prm->fmt) * st->sampc, NULL);
if (!st->sampv) {
err = ENOMEM;
goto out;
}
- if (st->aufmt != AUFMT_S16LE) {
- size_t sz = aufmt_sample_size(st->aufmt) * st->sampc;
- st->xsampv = mem_alloc(sz, NULL);
- if (!st->xsampv) {
- err = ENOMEM;
- goto out;
- }
- }
-
err = snd_pcm_open(&st->write, st->device, SND_PCM_STREAM_PLAYBACK, 0);
if (err < 0) {
warning("alsa: could not open auplay device '%s' (%s)\n",
@@ -154,10 +134,10 @@ int alsa_play_alloc(struct auplay_st **stp, const struct auplay *ap,
goto out;
}
- pcmfmt = aufmt_to_alsaformat(st->aufmt);
+ pcmfmt = aufmt_to_alsaformat(prm->fmt);
if (pcmfmt == SND_PCM_FORMAT_UNKNOWN) {
warning("alsa: unknown sample format '%s'\n",
- aufmt_name(st->aufmt));
+ aufmt_name(prm->fmt));
err = EINVAL;
goto out;
}
diff --git a/modules/alsa/alsa_src.c b/modules/alsa/alsa_src.c
index 6e850a7..a21f846 100644
--- a/modules/alsa/alsa_src.c
+++ b/modules/alsa/alsa_src.c
@@ -22,14 +22,12 @@ struct ausrc_st {
pthread_t thread;
bool run;
snd_pcm_t *read;
- int16_t *sampv;
- void *xsampv;
+ void *sampv;
size_t sampc;
ausrc_read_h *rh;
void *arg;
struct ausrc_prm prm;
char *device;
- enum aufmt aufmt;
};
@@ -48,7 +46,6 @@ static void ausrc_destructor(void *arg)
snd_pcm_close(st->read);
mem_deref(st->sampv);
- mem_deref(st->xsampv);
mem_deref(st->device);
}
@@ -73,10 +70,7 @@ static void *read_thread(void *arg)
size_t sampc;
void *sampv;
- if (st->aufmt == AUFMT_S16LE)
- sampv = st->sampv;
- else
- sampv = st->xsampv;
+ sampv = st->sampv;
err = snd_pcm_readi(st->read, sampv, num_frames);
if (err == -EPIPE) {
@@ -89,11 +83,6 @@ static void *read_thread(void *arg)
sampc = err * st->prm.ch;
- if (st->aufmt != AUFMT_S16LE) {
- auconv_to_s16(st->sampv, st->aufmt,
- st->xsampv, sampc);
- }
-
st->rh(st->sampv, sampc, st->arg);
}
@@ -132,26 +121,16 @@ int alsa_src_alloc(struct ausrc_st **stp, const struct ausrc *as,
st->as = as;
st->rh = rh;
st->arg = arg;
- st->aufmt = alsa_sample_format;
st->sampc = prm->srate * prm->ch * prm->ptime / 1000;
num_frames = st->prm.srate * st->prm.ptime / 1000;
- st->sampv = mem_alloc(2 * st->sampc, NULL);
+ st->sampv = mem_alloc(aufmt_sample_size(prm->fmt) * st->sampc, NULL);
if (!st->sampv) {
err = ENOMEM;
goto out;
}
- if (st->aufmt != AUFMT_S16LE) {
- size_t sz = aufmt_sample_size(st->aufmt) * st->sampc;
- st->xsampv = mem_alloc(sz, NULL);
- if (!st->xsampv) {
- err = ENOMEM;
- goto out;
- }
- }
-
err = snd_pcm_open(&st->read, st->device, SND_PCM_STREAM_CAPTURE, 0);
if (err < 0) {
warning("alsa: could not open ausrc device '%s' (%s)\n",
@@ -159,10 +138,10 @@ int alsa_src_alloc(struct ausrc_st **stp, const struct ausrc *as,
goto out;
}
- pcmfmt = aufmt_to_alsaformat(st->aufmt);
+ pcmfmt = aufmt_to_alsaformat(prm->fmt);
if (pcmfmt == SND_PCM_FORMAT_UNKNOWN) {
warning("alsa: unknown sample format '%s'\n",
- aufmt_name(st->aufmt));
+ aufmt_name(prm->fmt));
err = EINVAL;
goto out;
}
@@ -182,7 +161,8 @@ int alsa_src_alloc(struct ausrc_st **stp, const struct ausrc *as,
goto out;
}
- debug("alsa: recording started (%s)\n", st->device);
+ debug("alsa: recording started (%s) format=%s\n",
+ st->device, aufmt_name(prm->fmt));
out:
if (err)
diff --git a/modules/aubridge/play.c b/modules/aubridge/play.c
index 37c04fa..ff0b98f 100644
--- a/modules/aubridge/play.c
+++ b/modules/aubridge/play.c
@@ -4,6 +4,7 @@
* Copyright (C) 2010 Creytiv.com
*/
#include <re.h>
+#include <rem.h>
#include <baresip.h>
#include "aubridge.h"
@@ -28,6 +29,12 @@ int play_alloc(struct auplay_st **stp, const struct auplay *ap,
if (!stp || !ap || !prm)
return EINVAL;
+ if (prm->fmt != AUFMT_S16LE) {
+ warning("aubridge: playback: unsupported sample format (%s)\n",
+ aufmt_name(prm->fmt));
+ return ENOTSUP;
+ }
+
st = mem_zalloc(sizeof(*st), auplay_destructor);
if (!st)
return ENOMEM;
diff --git a/modules/aubridge/src.c b/modules/aubridge/src.c
index 22ce13a..87fd64a 100644
--- a/modules/aubridge/src.c
+++ b/modules/aubridge/src.c
@@ -4,6 +4,7 @@
* Copyright (C) 2010 Creytiv.com
*/
#include <re.h>
+#include <rem.h>
#include <baresip.h>
#include "aubridge.h"
@@ -31,6 +32,12 @@ int src_alloc(struct ausrc_st **stp, const struct ausrc *as,
if (!stp || !as || !prm)
return EINVAL;
+ if (prm->fmt != AUFMT_S16LE) {
+ warning("aubridge: source: unsupported sample format (%s)\n",
+ aufmt_name(prm->fmt));
+ return ENOTSUP;
+ }
+
st = mem_zalloc(sizeof(*st), ausrc_destructor);
if (!st)
return ENOMEM;
diff --git a/modules/audiounit/player.c b/modules/audiounit/player.c
index 372f139..230b1ad 100644
--- a/modules/audiounit/player.c
+++ b/modules/audiounit/player.c
@@ -7,6 +7,7 @@
#include <AudioToolbox/AudioToolbox.h>
#include <pthread.h>
#include <re.h>
+#include <rem.h>
#include <baresip.h>
#include "audiounit.h"
@@ -18,6 +19,7 @@ struct auplay_st {
pthread_mutex_t mutex;
auplay_write_h *wh;
void *arg;
+ uint32_t sampsz;
};
@@ -68,7 +70,7 @@ static OSStatus output_callback(void *inRefCon,
AudioBuffer *ab = &ioData->mBuffers[i];
- wh(ab->mData, ab->mDataByteSize/2, arg);
+ wh(ab->mData, ab->mDataByteSize/st->sampsz, arg);
}
return 0;
@@ -86,6 +88,17 @@ static void interrupt_handler(bool interrupted, void *arg)
}
+static uint32_t aufmt_to_formatflags(enum aufmt fmt)
+{
+ switch (fmt) {
+
+ case AUFMT_S16LE: return kLinearPCMFormatFlagIsSignedInteger;
+ case AUFMT_FLOAT: return kLinearPCMFormatFlagIsFloat;
+ default: return 0;
+ }
+}
+
+
int audiounit_player_alloc(struct auplay_st **stp, const struct auplay *ap,
struct auplay_prm *prm, const char *device,
auplay_write_h *wh, void *arg)
@@ -130,21 +143,23 @@ int audiounit_player_alloc(struct auplay_st **stp, const struct auplay *ap,
goto out;
}
+ st->sampsz = (uint32_t)aufmt_sample_size(prm->fmt);
+
fmt.mSampleRate = prm->srate;
fmt.mFormatID = kAudioFormatLinearPCM;
#if TARGET_OS_IPHONE
- fmt.mFormatFlags = kAudioFormatFlagIsSignedInteger
+ fmt.mFormatFlags = aufmt_to_formatflags(prm->fmt)
| kAudioFormatFlagsNativeEndian
| kAudioFormatFlagIsPacked;
#else
- fmt.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger
- | kLinearPCMFormatFlagIsPacked;
+ fmt.mFormatFlags = aufmt_to_formatflags(prm->fmt)
+ | kAudioFormatFlagIsPacked;
#endif
- fmt.mBitsPerChannel = 16;
+ fmt.mBitsPerChannel = 8 * st->sampsz;
fmt.mChannelsPerFrame = prm->ch;
- fmt.mBytesPerFrame = 2 * prm->ch;
+ fmt.mBytesPerFrame = st->sampsz * prm->ch;
fmt.mFramesPerPacket = 1;
- fmt.mBytesPerPacket = 2 * prm->ch;
+ fmt.mBytesPerPacket = st->sampsz * prm->ch;
ret = AudioUnitInitialize(st->au);
if (ret)
diff --git a/modules/audiounit/recorder.c b/modules/audiounit/recorder.c
index cf6a1af..b66ba07 100644
--- a/modules/audiounit/recorder.c
+++ b/modules/audiounit/recorder.c
@@ -8,6 +8,7 @@
#include <TargetConditionals.h>
#include <pthread.h>
#include <re.h>
+#include <rem.h>
#include <baresip.h>
#include "audiounit.h"
@@ -20,6 +21,7 @@ struct ausrc_st {
int ch;
ausrc_read_h *rh;
void *arg;
+ uint32_t sampsz;
};
@@ -67,7 +69,7 @@ static OSStatus input_callback(void *inRefCon,
abl.mNumberBuffers = 1;
abl.mBuffers[0].mNumberChannels = st->ch;
abl.mBuffers[0].mData = NULL;
- abl.mBuffers[0].mDataByteSize = inNumberFrames * 2;
+ abl.mBuffers[0].mDataByteSize = inNumberFrames * st->sampsz;
ret = AudioUnitRender(st->au,
ioActionFlags,
@@ -80,7 +82,8 @@ static OSStatus input_callback(void *inRefCon,
return ret;
}
- rh(abl.mBuffers[0].mData, abl.mBuffers[0].mDataByteSize/2, arg);
+ rh(abl.mBuffers[0].mData,
+ abl.mBuffers[0].mDataByteSize/st->sampsz, arg);
return 0;
}
@@ -97,6 +100,17 @@ static void interrupt_handler(bool interrupted, void *arg)
}
+static uint32_t aufmt_to_formatflags(enum aufmt fmt)
+{
+ switch (fmt) {
+
+ case AUFMT_S16LE: return kLinearPCMFormatFlagIsSignedInteger;
+ case AUFMT_FLOAT: return kLinearPCMFormatFlagIsFloat;
+ default: return 0;
+ }
+}
+
+
int audiounit_recorder_alloc(struct ausrc_st **stp, const struct ausrc *as,
struct media_ctx **ctx,
struct ausrc_prm *prm, const char *device,
@@ -178,21 +192,23 @@ int audiounit_recorder_alloc(struct ausrc_st **stp, const struct ausrc *as,
goto out;
#endif
+ st->sampsz = (uint32_t)aufmt_sample_size(prm->fmt);
+
fmt.mSampleRate = prm->srate;
fmt.mFormatID = kAudioFormatLinearPCM;
#if TARGET_OS_IPHONE
- fmt.mFormatFlags = kAudioFormatFlagIsSignedInteger
+ fmt.mFormatFlags = aufmt_to_formatflags(prm->fmt)
| kAudioFormatFlagsNativeEndian
| kAudioFormatFlagIsPacked;
#else
- fmt.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger
+ fmt.mFormatFlags = aufmt_to_formatflags(prm->fmt)
| kLinearPCMFormatFlagIsPacked;
#endif
- fmt.mBitsPerChannel = 16;
+ fmt.mBitsPerChannel = 8 * st->sampsz;
fmt.mChannelsPerFrame = prm->ch;
- fmt.mBytesPerFrame = 2 * prm->ch;
+ fmt.mBytesPerFrame = st->sampsz * prm->ch;
fmt.mFramesPerPacket = 1;
- fmt.mBytesPerPacket = 2 * prm->ch;
+ fmt.mBytesPerPacket = st->sampsz * prm->ch;
fmt.mReserved = 0;
ret = AudioUnitSetProperty(st->au, kAudioUnitProperty_StreamFormat,
diff --git a/modules/aufile/aufile.c b/modules/aufile/aufile.c
index 3f9c77f..307aee1 100644
--- a/modules/aufile/aufile.c
+++ b/modules/aufile/aufile.c
@@ -159,6 +159,12 @@ static int alloc_handler(struct ausrc_st **stp, const struct ausrc *as,
if (!stp || !as || !prm || !rh)
return EINVAL;
+ if (prm->fmt != AUFMT_S16LE) {
+ warning("aufile: unsupported sample format (%s)\n",
+ aufmt_name(prm->fmt));
+ return ENOTSUP;
+ }
+
info("aufile: loading input file '%s'\n", dev);
st = mem_zalloc(sizeof(*st), destructor);
diff --git a/modules/auloop/auloop.c b/modules/auloop/auloop.c
index 22dd1b0..8324a88 100644
--- a/modules/auloop/auloop.c
+++ b/modules/auloop/auloop.c
@@ -44,6 +44,7 @@ struct audio_loop {
struct tmr tmr;
uint32_t srate;
uint32_t ch;
+ enum aufmt fmt;
uint32_t n_read;
uint32_t n_write;
@@ -90,13 +91,15 @@ static void print_stats(struct audio_loop *al)
if (al->n_write)
rw_ratio = 1.0 * al->n_read / al->n_write;
- (void)re_fprintf(stderr, "\r%uHz %dch "
+ (void)re_fprintf(stdout, "\r%uHz %dch %s "
" n_read=%u n_write=%u rw_ratio=%.2f",
- al->srate, al->ch,
+ al->srate, al->ch, aufmt_name(al->fmt),
al->n_read, al->n_write, rw_ratio);
if (str_isset(aucodec))
- (void)re_fprintf(stderr, " codec='%s'", aucodec);
+ (void)re_fprintf(stdout, " codec='%s'", aucodec);
+
+ fflush(stdout);
}
@@ -136,23 +139,25 @@ static int codec_read(struct audio_loop *al, int16_t *sampv, size_t sampc)
}
-static void read_handler(const int16_t *sampv, size_t sampc, void *arg)
+static void read_handler(const void *sampv, size_t sampc, void *arg)
{
struct audio_loop *al = arg;
+ size_t num_bytes = sampc * aufmt_sample_size(al->fmt);
int err;
++al->n_read;
- err = aubuf_write_samp(al->ab, sampv, sampc);
+ err = aubuf_write(al->ab, sampv, num_bytes);
if (err) {
warning("auloop: aubuf_write: %m\n", err);
}
}
-static void write_handler(int16_t *sampv, size_t sampc, void *arg)
+static void write_handler(void *sampv, size_t sampc, void *arg)
{
struct audio_loop *al = arg;
+ size_t num_bytes = sampc * aufmt_sample_size(al->fmt);
int err;
++al->n_write;
@@ -166,7 +171,7 @@ static void write_handler(int16_t *sampv, size_t sampc, void *arg)
}
}
else {
- aubuf_read_samp(al->ab, sampv, sampc);
+ aubuf_read(al->ab, sampv, num_bytes);
}
}
@@ -218,9 +223,23 @@ static int auloop_reset(struct audio_loop *al)
if (!cfg)
return ENOENT;
+ if (cfg->audio.src_fmt != cfg->audio.play_fmt) {
+ warning("auloop: ausrc_format and auplay_format"
+ " must be the same\n");
+ return EINVAL;
+ }
+
+ al->fmt = cfg->audio.src_fmt;
+
/* Optional audio codec */
- if (str_isset(aucodec))
+ if (str_isset(aucodec)) {
+ if (cfg->audio.src_fmt != AUFMT_S16LE) {
+ warning("auloop: only s16 supported with codec\n");
+ return EINVAL;
+ }
+
start_codec(al, aucodec);
+ }
/* audio player/source must be stopped first */
al->auplay = mem_deref(al->auplay);
@@ -248,6 +267,7 @@ static int auloop_reset(struct audio_loop *al)
auplay_prm.srate = al->srate;
auplay_prm.ch = al->ch;
auplay_prm.ptime = PTIME;
+ auplay_prm.fmt = al->fmt;
err = auplay_alloc(&al->auplay, baresip_auplayl(),
cfg->audio.play_mod, &auplay_prm,
cfg->audio.play_dev, write_handler, al);
@@ -261,6 +281,7 @@ static int auloop_reset(struct audio_loop *al)
ausrc_prm.srate = al->srate;
ausrc_prm.ch = al->ch;
ausrc_prm.ptime = PTIME;
+ ausrc_prm.fmt = al->fmt;
err = ausrc_alloc(&al->ausrc, baresip_ausrcl(),
NULL, cfg->audio.src_mod,
&ausrc_prm, cfg->audio.src_dev,
diff --git a/modules/avahi/avahi.c b/modules/avahi/avahi.c
index 500610f..035d51a 100644
--- a/modules/avahi/avahi.c
+++ b/modules/avahi/avahi.c
@@ -250,6 +250,8 @@ static void resolve_callback(
warning("avahi: Resolver Error on %s: %s\n", name,
avahi_strerror(avahi_client_errno(avahi->client)));
}
+
+ avahi_service_resolver_free(r);
}
diff --git a/modules/avcodec/encode.c b/modules/avcodec/encode.c
index 11d30a0..d0d5d83 100644
--- a/modules/avcodec/encode.c
+++ b/modules/avcodec/encode.c
@@ -429,7 +429,7 @@ static int open_encoder_x264(struct videnc_state *st, struct videnc_param *prm,
xprm.i_fps_num = prm->fps;
xprm.i_fps_den = 1;
xprm.rc.i_bitrate = prm->bitrate / 1000; /* kbit/s */
- xprm.rc.i_rc_method = X264_RC_CQP;
+ xprm.rc.i_rc_method = X264_RC_ABR;
xprm.i_log_level = X264_LOG_WARNING;
/* ultrafast preset */
@@ -616,7 +616,7 @@ int encode_x264(struct videnc_state *st, bool update,
ret = x264_encoder_encode(st->x264, &nal, &i_nal, &pic_in, &pic_out);
if (ret < 0) {
- fprintf(stderr, "x264 [error]: x264_encoder_encode failed\n");
+ warning("avcodec: x264 [error]: x264_encoder_encode failed\n");
}
if (i_nal == 0)
return 0;
diff --git a/modules/avformat/avformat.c b/modules/avformat/avformat.c
index c0338a2..68ef088 100644
--- a/modules/avformat/avformat.c
+++ b/modules/avformat/avformat.c
@@ -252,6 +252,8 @@ static int alloc(struct vidsrc_st **stp, const struct vidsrc *vs,
if (!stp || !vs || !prm || !size || !frameh)
return EINVAL;
+ debug("avformat: alloc dev='%s'\n", dev);
+
st = mem_zalloc(sizeof(*st), destructor);
if (!st)
return ENOMEM;
@@ -270,6 +272,12 @@ static int alloc(struct vidsrc_st **stp, const struct vidsrc *vs,
#if LIBAVFORMAT_VERSION_INT >= ((52<<16) + (110<<8) + 0)
(void)fmt;
ret = avformat_open_input(&st->ic, dev, NULL, NULL);
+ if (ret < 0) {
+ warning("avformat: avformat_open_input(%s) failed (ret=%d)\n",
+ dev, ret);
+ err = ENOENT;
+ goto out;
+ }
#else
/* Params */
@@ -284,12 +292,14 @@ static int alloc(struct vidsrc_st **stp, const struct vidsrc *vs,
ret = av_open_input_file(&st->ic, dev, av_find_input_format(fmt),
0, &prms);
-#endif
-
if (ret < 0) {
+ warning("avformat: av_open_input_file(%s) failed (ret=%d)\n",
+ dev, ret);
err = ENOENT;
goto out;
}
+#endif
+
#if LIBAVFORMAT_VERSION_INT >= ((53<<16) + (4<<8) + 0)
ret = avformat_find_stream_info(st->ic, NULL);
diff --git a/modules/coreaudio/player.c b/modules/coreaudio/player.c
index 16cfa33..7d247cd 100644
--- a/modules/coreaudio/player.c
+++ b/modules/coreaudio/player.c
@@ -88,6 +88,9 @@ int coreaudio_player_alloc(struct auplay_st **stp, const struct auplay *ap,
(void)device;
+ if (!stp || !ap || !prm || prm->fmt != AUFMT_S16LE)
+ return EINVAL;
+
st = mem_zalloc(sizeof(*st), auplay_destructor);
if (!st)
return ENOMEM;
diff --git a/modules/coreaudio/recorder.c b/modules/coreaudio/recorder.c
index c913485..9ceae60 100644
--- a/modules/coreaudio/recorder.c
+++ b/modules/coreaudio/recorder.c
@@ -102,7 +102,7 @@ int coreaudio_recorder_alloc(struct ausrc_st **stp, const struct ausrc *as,
(void)device;
(void)errh;
- if (!stp || !as || !prm)
+ if (!stp || !as || !prm || prm->fmt != AUFMT_S16LE)
return EINVAL;
st = mem_zalloc(sizeof(*st), ausrc_destructor);
diff --git a/modules/debug_cmd/debug_cmd.c b/modules/debug_cmd/debug_cmd.c
index 56c1a8f..faee4ee 100644
--- a/modules/debug_cmd/debug_cmd.c
+++ b/modules/debug_cmd/debug_cmd.c
@@ -98,6 +98,27 @@ static int cmd_play_file(struct re_printf *pf, void *arg)
}
+static int reload_config(struct re_printf *pf, void *arg)
+{
+ int err;
+ (void)arg;
+
+ err = re_hprintf(pf, "reloading config file ..\n");
+ if (err)
+ return err;
+
+ err = conf_configure();
+ if (err) {
+ (void)re_hprintf(pf, "reload_config failed: %m\n", err);
+ return err;
+ }
+
+ (void)re_hprintf(pf, "done\n");
+
+ return 0;
+}
+
+
static const struct cmd debugcmdv[] = {
{"main", 0, 0, "Main loop debug", re_debug },
{"config", 0, 0, "Print configuration", cmd_config_print },
@@ -109,6 +130,7 @@ static const struct cmd debugcmdv[] = {
{"uastat", 'u', 0, "UA debug", cmd_ua_debug },
{"memstat", 'y', 0, "Memory status", mem_status },
{"play", 0, CMD_PRM, "Play audio file", cmd_play_file },
+{"conf_reload",0, 0, "Reload config file", reload_config },
};
diff --git a/modules/gst/gst.c b/modules/gst/gst.c
index dc00dd4..d729b70 100644
--- a/modules/gst/gst.c
+++ b/modules/gst/gst.c
@@ -376,6 +376,8 @@ static int gst_alloc(struct ausrc_st **stp, const struct ausrc *as,
if (!prm)
return EINVAL;
+ if (prm->fmt != AUFMT_S16LE)
+ return ENOTSUP;
st = mem_zalloc(sizeof(*st), gst_destructor);
if (!st)
diff --git a/modules/gst1/gst.c b/modules/gst1/gst.c
index 6f704e5..380334a 100644
--- a/modules/gst1/gst.c
+++ b/modules/gst1/gst.c
@@ -3,9 +3,9 @@
*
* Copyright (C) 2010 - 2015 Creytiv.com
*/
+#define _DEFAULT_SOURCE 1
#include <stdlib.h>
#include <string.h>
-#define __USE_POSIX199309
#include <time.h>
#include <pthread.h>
#include <gst/gst.h>
@@ -383,6 +383,12 @@ static int gst_alloc(struct ausrc_st **stp, const struct ausrc *as,
if (!prm)
return EINVAL;
+ if (prm->fmt != AUFMT_S16LE) {
+ warning("gst: unsupported sample format (%s)\n",
+ aufmt_name(prm->fmt));
+ return ENOTSUP;
+ }
+
st = mem_zalloc(sizeof(*st), gst_destructor);
if (!st)
return ENOMEM;
diff --git a/modules/gzrtp/gzrtp.cpp b/modules/gzrtp/gzrtp.cpp
new file mode 100644
index 0000000..19aaf9d
--- /dev/null
+++ b/modules/gzrtp/gzrtp.cpp
@@ -0,0 +1,220 @@
+/**
+ * @file gzrtp.cpp GNU ZRTP: Media Path Key Agreement for Unicast Secure RTP
+ *
+ * Copyright (C) 2010 - 2017 Creytiv.com
+ */
+#include <stdint.h>
+
+#include <re.h>
+#include <baresip.h>
+
+#include <string.h>
+
+#include <libzrtpcpp/ZRtp.h>
+
+#include "session.h"
+#include "stream.h"
+
+
+/**
+ * @defgroup gzrtp gzrtp
+ *
+ * ZRTP: Media Path Key Agreement for Unicast Secure RTP
+ *
+ * Experimental support for ZRTP
+ *
+ * See http://tools.ietf.org/html/rfc6189
+ *
+ *
+ * This module is using GNU ZRTP C++ library
+ *
+ * https://github.com/wernerd/ZRTPCPP
+ *
+ * Configuration options:
+ *
+ \verbatim
+ zrtp_parallel {yes,no} # Start all streams at once
+ \endverbatim
+ *
+ */
+
+
+static ZRTPConfig *s_zrtp_config = NULL;
+
+
+struct menc_sess {
+ Session *session;
+};
+
+
+struct menc_media {
+ Stream *stream;
+};
+
+
+static void session_destructor(void *arg)
+{
+ struct menc_sess *st = (struct menc_sess *)arg;
+
+ delete st->session;
+}
+
+
+static void media_destructor(void *arg)
+{
+ struct menc_media *st = (struct menc_media *)arg;
+
+ delete st->stream;
+}
+
+
+static int session_alloc(struct menc_sess **sessp, struct sdp_session *sdp,
+ bool offerer, menc_error_h *errorh, void *arg)
+{
+ struct menc_sess *st;
+ (void)offerer;
+ (void)errorh;
+ (void)arg;
+ int err = 0;
+
+ if (!sessp || !sdp)
+ return EINVAL;
+
+ st = (struct menc_sess *)mem_zalloc(sizeof(*st), session_destructor);
+ if (!st)
+ return ENOMEM;
+
+ st->session = new Session(*s_zrtp_config);
+ if (!st->session)
+ err = ENOMEM;
+
+ if (err)
+ mem_deref(st);
+ else
+ *sessp = st;
+
+ return err;
+}
+
+
+static int media_alloc(struct menc_media **stp, struct menc_sess *sess,
+ struct rtp_sock *rtp,
+ int proto, void *rtpsock, void *rtcpsock,
+ struct sdp_media *sdpm)
+{
+ struct menc_media *st;
+ int err = 0;
+ StreamMediaType med_type;
+ const char *med_name;
+
+ if (!stp || !sess || !sess->session || proto != IPPROTO_UDP)
+ return EINVAL;
+
+ st = *stp;
+ if (st)
+ goto start;
+
+ st = (struct menc_media *)mem_zalloc(sizeof(*st), media_destructor);
+ if (!st)
+ return ENOMEM;
+
+ med_name = sdp_media_name(sdpm);
+ if (str_cmp(med_name, "audio") == 0)
+ med_type = MT_AUDIO;
+ else if (str_cmp(med_name, "video") == 0)
+ med_type = MT_VIDEO;
+ else if (str_cmp(med_name, "text") == 0)
+ med_type = MT_TEXT;
+ else if (str_cmp(med_name, "application") == 0)
+ med_type = MT_APPLICATION;
+ else if (str_cmp(med_name, "message") == 0)
+ med_type = MT_MESSAGE;
+ else
+ med_type = MT_UNKNOWN;
+
+ st->stream = sess->session->create_stream(
+ *s_zrtp_config,
+ (struct udp_sock *)rtpsock,
+ (struct udp_sock *)rtcpsock,
+ rtp_sess_ssrc(rtp), med_type);
+ if (!st->stream) {
+ err = ENOMEM;
+ goto out;
+ }
+
+ st->stream->sdp_encode(sdpm);
+
+ out:
+ if (err) {
+ mem_deref(st);
+ return err;
+ }
+ else
+ *stp = st;
+
+ start:
+ if (sa_isset(sdp_media_raddr(sdpm), SA_ALL)) {
+ st->stream->sdp_decode(sdpm);
+ err = sess->session->start_stream(st->stream);
+ if (err) {
+ warning("zrtp: stream start failed: %d\n", err);
+ }
+ }
+
+ return err;
+}
+
+
+static struct menc menc_zrtp = {
+ LE_INIT, "zrtp", "RTP/AVP", session_alloc, media_alloc
+};
+
+
+static const struct cmd cmdv[] = {
+ {"zrtp_verify", 0, CMD_PRM, "Verify ZRTP SAS <session ID>",
+ Session::cmd_verify_sas },
+ {"zrtp_unverify", 0, CMD_PRM, "Unverify ZRTP SAS <session ID>",
+ Session::cmd_unverify_sas },
+};
+
+
+static int module_init(void)
+{
+ char config_path[256];
+ int err = 0;
+
+ err = conf_path_get(config_path, sizeof(config_path));
+ if (err) {
+ warning("zrtp: could not get config path: %m\n", err);
+ return err;
+ }
+
+ s_zrtp_config = new ZRTPConfig(conf_cur(), config_path);
+ if (!s_zrtp_config)
+ return ENOMEM;
+
+ menc_register(baresip_mencl(), &menc_zrtp);
+
+ return cmd_register(baresip_commands(), cmdv, ARRAY_SIZE(cmdv));
+}
+
+
+static int module_close(void)
+{
+ delete s_zrtp_config;
+ s_zrtp_config = NULL;
+
+ cmd_unregister(baresip_commands(), cmdv);
+
+ menc_unregister(&menc_zrtp);
+
+ return 0;
+}
+
+
+extern "C" EXPORT_SYM const struct mod_export DECL_EXPORTS(gzrtp) = {
+ "gzrtp",
+ "menc",
+ module_init,
+ module_close
+};
diff --git a/modules/gzrtp/messages.cpp b/modules/gzrtp/messages.cpp
new file mode 100644
index 0000000..db3a7db
--- /dev/null
+++ b/modules/gzrtp/messages.cpp
@@ -0,0 +1,256 @@
+/**
+ * @file messages.cpp GNU ZRTP: Engine messages
+ *
+ * Copyright (C) 2010 - 2017 Creytiv.com
+ */
+#include <stdint.h>
+
+#include <re.h>
+#include <baresip.h>
+
+#include <libzrtpcpp/ZRtp.h>
+
+#include "stream.h"
+
+
+using namespace GnuZrtpCodes;
+
+
+#define NO_MESSAGE "NO MESSAGE DEFINED"
+
+
+static const char *info_msg(int32_t subcode)
+{
+ const char *msg;
+
+ switch (subcode) {
+ case InfoHelloReceived:
+ msg = "Hello received and prepared a Commit, "
+ "ready to get peer's hello hash";
+ break;
+ case InfoCommitDHGenerated:
+ msg = "Commit: Generated a public DH key";
+ break;
+ case InfoRespCommitReceived:
+ msg = "Responder: Commit received, preparing DHPart1";
+ break;
+ case InfoDH1DHGenerated:
+ msg = "DH1Part: Generated a public DH key";
+ break;
+ case InfoInitDH1Received:
+ msg = "Initiator: DHPart1 received, preparing DHPart2";
+ break;
+ case InfoRespDH2Received:
+ msg = "Responder: DHPart2 received, preparing Confirm1";
+ break;
+ case InfoInitConf1Received:
+ msg = "Initiator: Confirm1 received, preparing Confirm2";
+ break;
+ case InfoRespConf2Received:
+ msg = "Responder: Confirm2 received, preparing Conf2Ack";
+ break;
+ case InfoRSMatchFound:
+ msg = "At least one retained secret matches - security OK";
+ break;
+ case InfoSecureStateOn:
+ msg = "Entered secure state";
+ break;
+ case InfoSecureStateOff:
+ msg = "No more security for this session";
+ break;
+ default:
+ msg = NO_MESSAGE;
+ break;
+ }
+
+ return msg;
+}
+
+
+static const char *warning_msg(int32_t subcode)
+{
+ const char *msg;
+
+ switch (subcode) {
+ case WarningDHAESmismatch:
+ msg = "Commit contains an AES256 cipher but does not offer a "
+ "Diffie-Helman 4096 - not used DH4096 was discarded";
+ break;
+ case WarningGoClearReceived:
+ msg = "Received a GoClear message";
+ break;
+ case WarningDHShort:
+ msg = "Hello offers an AES256 cipher but does not offer a "
+ "Diffie-Helman 4096- not used DH4096 was discarded";
+ break;
+ case WarningNoRSMatch:
+ msg = "No retained shared secrets available - must verify SAS";
+ break;
+ case WarningCRCmismatch:
+ msg = "Internal ZRTP packet checksum mismatch - "
+ "packet dropped";
+ break;
+ case WarningSRTPauthError:
+ msg = "Dropping packet because SRTP authentication failed!";
+ break;
+ case WarningSRTPreplayError:
+ msg = "Dropping packet because SRTP replay check failed!";
+ break;
+ case WarningNoExpectedRSMatch:
+ msg = "Valid retained shared secrets availabe but no matches "
+ "found - must verify SAS";
+ break;
+ case WarningNoExpectedAuxMatch:
+ msg = "Our AUX secret was set but the other peer's AUX secret "
+ "does not match ours";
+ break;
+ default:
+ msg = NO_MESSAGE;
+ break;
+ }
+
+ return msg;
+}
+
+
+static const char *severe_msg(int32_t subcode)
+{
+ const char *msg;
+
+ switch (subcode) {
+ case SevereHelloHMACFailed:
+ msg = "Hash HMAC check of Hello failed!";
+ break;
+ case SevereCommitHMACFailed:
+ msg = "Hash HMAC check of Commit failed!";
+ break;
+ case SevereDH1HMACFailed:
+ msg = "Hash HMAC check of DHPart1 failed!";
+ break;
+ case SevereDH2HMACFailed:
+ msg = "Hash HMAC check of DHPart2 failed!";
+ break;
+ case SevereCannotSend:
+ msg = "Cannot send data - connection or peer down?";
+ break;
+ case SevereProtocolError:
+ msg = "Internal protocol error occured!";
+ break;
+ case SevereNoTimer:
+ msg = "Cannot start a timer - internal resources exhausted?";
+ break;
+ case SevereTooMuchRetries:
+ msg = "Too much retries during ZRTP negotiation - connection "
+ "or peer down?";
+ break;
+ default:
+ msg = NO_MESSAGE;
+ break;
+ }
+
+ return msg;
+}
+
+
+static const char *zrtp_msg(int32_t subcode)
+{
+ const char *msg;
+
+ switch (subcode) {
+ case MalformedPacket:
+ msg = "Malformed packet (CRC OK, but wrong structure)";
+ break;
+ case CriticalSWError:
+ msg = "Critical software error";
+ break;
+ case UnsuppZRTPVersion:
+ msg = "Unsupported ZRTP version";
+ break;
+ case HelloCompMismatch:
+ msg = "Hello components mismatch";
+ break;
+ case UnsuppHashType:
+ msg = "Hash type not supported";
+ break;
+ case UnsuppCiphertype:
+ msg = "Cipher type not supported";
+ break;
+ case UnsuppPKExchange:
+ msg = "Public key exchange not supported";
+ break;
+ case UnsuppSRTPAuthTag:
+ msg = "SRTP auth. tag not supported";
+ break;
+ case UnsuppSASScheme:
+ msg = "SAS scheme not supported";
+ break;
+ case NoSharedSecret:
+ msg = "No shared secret available, DH mode required";
+ break;
+ case DHErrorWrongPV:
+ msg = "DH Error: bad pvi or pvr ( == 1, 0, or p-1)";
+ break;
+ case DHErrorWrongHVI:
+ msg = "DH Error: hvi != hashed data";
+ break;
+ case SASuntrustedMiTM:
+ msg = "Received relayed SAS from untrusted MiTM";
+ break;
+ case ConfirmHMACWrong:
+ msg = "Auth. Error: Bad Confirm pkt HMAC";
+ break;
+ case NonceReused:
+ msg = "Nonce reuse";
+ break;
+ case EqualZIDHello:
+ msg = "Equal ZIDs in Hello";
+ break;
+ case GoCleatNotAllowed:
+ msg = "GoClear packet received, but not allowed";
+ break;
+ default:
+ msg = NO_MESSAGE;
+ break;
+ }
+
+ return msg;
+}
+
+
+void Stream::print_message(GnuZrtpCodes::MessageSeverity severity,
+ int32_t subcode)
+{
+ switch (severity) {
+ case Info:
+ debug("zrtp: INFO<%s>: %s\n",
+ media_name(), info_msg(subcode));
+ break;
+ case Warning:
+ warning("zrtp: WARNING<%s>: %s\n",
+ media_name(), warning_msg(subcode));
+ break;
+ case Severe:
+ warning("zrtp: SEVERE<%s>: %s\n",
+ media_name(), severe_msg(subcode));
+ break;
+ case ZrtpError:
+ warning("zrtp: ZRTP_ERR<%s>: %s\n",
+ media_name(), zrtp_msg(subcode));
+ break;
+ default:
+ return;
+ }
+}
+
+
+const char *Stream::media_name() const
+{
+ switch (m_media_type) {
+ case MT_AUDIO: return "audio";
+ case MT_VIDEO: return "video";
+ case MT_TEXT: return "text";
+ case MT_APPLICATION: return "application";
+ case MT_MESSAGE: return "message";
+ default: return "UNKNOWN";
+ }
+}
diff --git a/modules/gzrtp/module.mk b/modules/gzrtp/module.mk
new file mode 100644
index 0000000..42e408e
--- /dev/null
+++ b/modules/gzrtp/module.mk
@@ -0,0 +1,38 @@
+#
+# module.mk
+#
+# Copyright (C) 2010 - 2017 Creytiv.com
+#
+
+#
+# To build libzrtpcppcore run the following commands:
+#
+# git clone https://github.com/wernerd/ZRTPCPP.git
+# cd ZRTPCPP
+# mkdir build
+# cd build
+# cmake -DCMAKE_POSITION_INDEPENDENT_CODE=1 -DCORE_LIB=1 -DSDES=1 \
+# -DBUILD_STATIC=1 ..
+# make
+#
+
+# GNU ZRTP C++ library (ZRTPCPP) source directory
+ZRTP_PATH ?= ../ZRTPCPP
+
+ZRTP_LIB := $(shell find $(ZRTP_PATH) -name libzrtpcppcore.a)
+
+MOD := gzrtp
+$(MOD)_SRCS += gzrtp.cpp session.cpp stream.cpp messages.cpp srtp.cpp
+$(MOD)_LFLAGS += $(ZRTP_LIB) -lstdc++
+$(MOD)_CXXFLAGS += \
+ -I$(ZRTP_PATH) \
+ -I$(ZRTP_PATH)/zrtp \
+ -I$(ZRTP_PATH)/srtp
+
+$(MOD)_CXXFLAGS += -O2 -Wall -fPIC
+
+# Uncomment this if you want to use libre SRTP facilities instead of the ones
+# provided by ZRTPCPP. In this case only standard ciphers (AES) are supported.
+#$(MOD)_CXXFLAGS += -DGZRTP_USE_RE_SRTP=1
+
+include mk/mod.mk
diff --git a/modules/gzrtp/session.cpp b/modules/gzrtp/session.cpp
new file mode 100644
index 0000000..422bee4
--- /dev/null
+++ b/modules/gzrtp/session.cpp
@@ -0,0 +1,214 @@
+/**
+ * @file session.h GNU ZRTP: Session class implementation
+ *
+ * Copyright (C) 2010 - 2017 Creytiv.com
+ */
+#include <stdint.h>
+
+#include <re.h>
+#include <baresip.h>
+
+#include "session.h"
+
+
+std::vector<Session *> Session::s_sessl;
+
+
+Session::Session(const ZRTPConfig& config)
+ : m_start_parallel(config.start_parallel)
+ , m_master(NULL)
+ , m_encrypted(0)
+{
+ int newid = 1;
+ for (std::vector<Session *>::iterator it = s_sessl.begin();
+ it != s_sessl.end(); ++it) {
+
+ if ((*it)->id() >= newid)
+ newid = (*it)->id() + 1;
+ }
+
+ m_id = newid;
+
+ s_sessl.push_back(this);
+
+ debug("zrtp: New session <%d>\n", id());
+}
+
+
+Session::~Session()
+{
+ for (std::vector<Session *>::iterator it = s_sessl.begin();
+ it != s_sessl.end(); ++it) {
+
+ if (*it == this) {
+ s_sessl.erase(it);
+ break;
+ }
+ }
+
+ debug("zrtp: Session <%d> is destroyed\n", id());
+}
+
+
+Stream *Session::create_stream(const ZRTPConfig& config,
+ udp_sock *rtpsock,
+ udp_sock *rtcpsock,
+ uint32_t local_ssrc,
+ StreamMediaType media_type)
+{
+ int err = 0;
+
+ Stream *st = new Stream (err, config, this, rtpsock, rtcpsock,
+ local_ssrc, media_type);
+ if (!st || err) {
+ delete st;
+ return NULL;
+ }
+
+ return st;
+}
+
+
+int Session::start_stream(Stream *stream)
+{
+ if (stream->started())
+ return 0;
+
+ m_streams.push_back(stream);
+
+ // Start all streams in parallel using DH mode. This is a kind of
+ // probing. The first stream to receive HelloACK will be the master
+ // stream. If disabled, only the first stream starts in DH (master)
+ // mode.
+ if (m_start_parallel) {
+ if (m_master && m_encrypted)
+ // If we already have a master in secure state,
+ // start in multistream mode
+ return stream->start(m_master);
+ else
+ // Start a new stream in DH mode
+ return stream->start(NULL);
+ }
+ else {
+ if (!m_master) {
+ // Start the first stream in DH mode
+ m_master = stream;
+ return stream->start(NULL);
+ }
+ else if (m_encrypted) {
+ // Master is in secure state; multistream
+ return stream->start(m_master);
+ }
+ }
+
+ return 0;
+}
+
+
+bool Session::request_master(Stream *stream)
+{
+ if (!m_start_parallel)
+ return true;
+
+ if (m_master)
+ return false;
+
+ // This is the first stream to receive HelloACK. It will be
+ // used as the master for the other streams in the session.
+ m_master = stream;
+ // Stop other DH-mode streams. They will be started in the
+ // multistream mode after the master enters secure state.
+ for (std::vector<Stream *>::iterator it = m_streams.begin();
+ it != m_streams.end(); ++it) {
+
+ if (*it != m_master) {
+ (*it)->stop();
+ }
+ }
+
+ return true;
+}
+
+
+void Session::on_secure(Stream *stream)
+{
+ ++m_encrypted;
+
+ if (m_encrypted == m_streams.size() && m_master) {
+ info("zrtp: All streams are encrypted (%s), "
+ "SAS is [%s] (%s)\n",
+ m_master->get_ciphers(),
+ m_master->get_sas(),
+ (m_master->sas_verified())? "verified" : "NOT VERIFIED");
+ return;
+ }
+
+ if (stream != m_master)
+ return;
+
+ // Master stream has just entered secure state. Start other
+ // streams in the multistream mode.
+
+ debug("zrtp: Starting other streams (%d)\n", m_streams.size() - 1);
+
+ for (std::vector<Stream *>::iterator it = m_streams.begin();
+ it != m_streams.end(); ++it) {
+
+ if (*it != m_master) {
+ (*it)->start(m_master);
+ }
+ }
+}
+
+
+int Session::cmd_verify_sas(struct re_printf *pf, void *arg)
+{
+ return cmd_sas(true, pf, arg);
+}
+
+
+int Session::cmd_unverify_sas(struct re_printf *pf, void *arg)
+{
+ return cmd_sas(false, pf, arg);
+}
+
+
+int Session::cmd_sas(bool verify, struct re_printf *pf, void *arg)
+{
+ const struct cmd_arg *carg = (struct cmd_arg *)arg;
+ (void)pf;
+ int id = -1;
+ Session *sess = NULL;
+
+ if (str_isset(carg->prm))
+ id = atoi(carg->prm);
+
+ for (std::vector<Session *>::iterator it = s_sessl.begin();
+ it != s_sessl.end(); ++it) {
+
+ if ((*it)->id() == id) {
+ sess = *it;
+ break;
+ }
+ }
+
+ if (!sess) {
+ warning("zrtp: No session with id %d\n", id);
+ return EINVAL;
+ }
+
+ if (!sess->m_master) {
+ warning("zrtp: No master stream for the session with id %d\n",
+ sess->id());
+ return EFAULT;
+ }
+
+ sess->m_master->verify_sas(verify);
+
+ info("zrtp: Session <%d>: SAS [%s] is %s\n", sess->id(),
+ sess->m_master->get_sas(),
+ (sess->m_master->sas_verified())? "verified" : "NOT VERIFIED");
+
+ return 0;
+}
+
diff --git a/modules/gzrtp/session.h b/modules/gzrtp/session.h
new file mode 100644
index 0000000..90c12d6
--- /dev/null
+++ b/modules/gzrtp/session.h
@@ -0,0 +1,50 @@
+/**
+ * @file session.h GNU ZRTP: Session class
+ *
+ * Copyright (C) 2010 - 2017 Creytiv.com
+ */
+#ifndef __SESSION_H
+#define __SESSION_H
+
+
+#include "stream.h"
+
+
+class Stream;
+class ZRTPConfig;
+
+class Session {
+public:
+ Session(const ZRTPConfig& config);
+
+ ~Session();
+
+ Stream *create_stream(const ZRTPConfig& config,
+ udp_sock *rtpsock,
+ udp_sock *rtcpsock,
+ uint32_t local_ssrc,
+ StreamMediaType media_type);
+
+ int start_stream(Stream *stream);
+ int id() const { return m_id; }
+
+ bool request_master(Stream *stream);
+ void on_secure(Stream *stream);
+
+ static int cmd_verify_sas(struct re_printf *pf, void *arg);
+ static int cmd_unverify_sas(struct re_printf *pf, void *arg);
+ static int cmd_sas(bool verify, struct re_printf *pf, void *arg);
+
+private:
+ static std::vector<Session *> s_sessl;
+
+ const bool m_start_parallel;
+ int m_id;
+ std::vector<Stream *> m_streams;
+ Stream *m_master;
+ unsigned int m_encrypted;
+};
+
+
+#endif // __SESSION_H
+
diff --git a/modules/gzrtp/srtp.cpp b/modules/gzrtp/srtp.cpp
new file mode 100644
index 0000000..9aaa4b3
--- /dev/null
+++ b/modules/gzrtp/srtp.cpp
@@ -0,0 +1,327 @@
+/**
+ * @file srtp.cpp GNU ZRTP: SRTP processing
+ *
+ * Copyright (C) 2010 - 2017 Creytiv.com
+ */
+#include <stdint.h>
+
+#include <re.h>
+#include <baresip.h>
+
+#ifdef GZRTP_USE_RE_SRTP
+#include <string.h>
+#else
+#include <srtp/CryptoContext.h>
+#include <srtp/CryptoContextCtrl.h>
+#include <srtp/SrtpHandler.h>
+#endif
+
+#include "srtp.h"
+
+
+Srtp::Srtp(int& err, const SrtpSecret_t *secrets, EnableSecurity part)
+
+{
+ const uint8_t *key, *salt;
+ uint32_t key_len, salt_len;
+
+ err = EPERM;
+
+#ifdef GZRTP_USE_RE_SRTP
+ m_srtp = NULL;
+#else
+ m_cc = NULL;
+ m_cc_ctrl = NULL;
+#endif
+
+ if (part == ForSender) {
+ // To encrypt packets: intiator uses initiator keys,
+ // responder uses responder keys
+ if (secrets->role == Initiator) {
+ key = secrets->keyInitiator;
+ key_len = secrets->initKeyLen / 8;
+ salt = secrets->saltInitiator;
+ salt_len = secrets->initSaltLen / 8;
+ }
+ else {
+ key = secrets->keyResponder;
+ key_len = secrets->respKeyLen / 8;
+ salt = secrets->saltResponder;
+ salt_len = secrets->respSaltLen / 8;
+ }
+ }
+ else if (part == ForReceiver) {
+ // To decrypt packets: intiator uses responder keys,
+ // responder initiator keys
+ if (secrets->role == Initiator) {
+ key = secrets->keyResponder;
+ key_len = secrets->respKeyLen / 8;
+ salt = secrets->saltResponder;
+ salt_len = secrets->respSaltLen / 8;
+ }
+ else {
+ key = secrets->keyInitiator;
+ key_len = secrets->initKeyLen / 8;
+ salt = secrets->saltInitiator;
+ salt_len = secrets->initSaltLen / 8;
+ }
+ }
+ else {
+ err = EINVAL;
+ return;
+ }
+
+#ifdef GZRTP_USE_RE_SRTP
+
+ uint8_t key_buf[32 + 14]; // max key + salt
+ enum srtp_suite suite;
+ struct srtp *st;
+
+ if (secrets->symEncAlgorithm == Aes &&
+ secrets->authAlgorithm == Sha1) {
+
+ if (key_len == 16 && secrets->srtpAuthTagLen == 32)
+ suite = SRTP_AES_CM_128_HMAC_SHA1_32;
+
+ else if (key_len == 16 && secrets->srtpAuthTagLen == 80)
+ suite = SRTP_AES_CM_128_HMAC_SHA1_80;
+
+ else if (key_len == 32 && secrets->srtpAuthTagLen == 32)
+ suite = SRTP_AES_256_CM_HMAC_SHA1_32;
+
+ else if (key_len == 32 && secrets->srtpAuthTagLen == 80)
+ suite = SRTP_AES_256_CM_HMAC_SHA1_80;
+
+ else {
+ err = ENOTSUP;
+ return;
+ }
+ }
+ else {
+ err = ENOTSUP;
+ return;
+ }
+
+ if (salt_len != 14) {
+ err = EINVAL;
+ return;
+ }
+
+ memcpy(key_buf, key, key_len);
+ memcpy(key_buf + key_len, salt, salt_len);
+
+ err = srtp_alloc(&st, suite, key_buf, key_len + salt_len, 0);
+ if (err)
+ return;
+
+ m_auth_tag_len = secrets->srtpAuthTagLen / 8;
+ m_srtp = st;
+
+ err = 0;
+#else
+
+ CryptoContext *cc = NULL;
+ CryptoContextCtrl *cc_ctrl = NULL;
+ int cipher;
+ int authn;
+ int auth_key_len;
+
+ switch (secrets->authAlgorithm) {
+ case Sha1:
+ authn = SrtpAuthenticationSha1Hmac;
+ auth_key_len = 20;
+ break;
+ case Skein:
+ authn = SrtpAuthenticationSkeinHmac;
+ auth_key_len = 32;
+ break;
+ default:
+ err = ENOTSUP;
+ return;
+ }
+
+ switch (secrets->symEncAlgorithm) {
+ case Aes:
+ cipher = SrtpEncryptionAESCM;
+ break;
+ case TwoFish:
+ cipher = SrtpEncryptionTWOCM;
+ break;
+ default:
+ err = ENOTSUP;
+ return;
+ }
+
+ cc = new CryptoContext(
+ 0, // SSRC (used for lookup)
+ 0, // Roll-Over-Counter (ROC)
+ 0L, // keyderivation << 48,
+ cipher, // encryption algo
+ authn, // authtentication algo
+ (uint8_t *)key, // Master Key
+ key_len, // Master Key length
+ (uint8_t *)salt, // Master Salt
+ salt_len, // Master Salt length
+ key_len, // encryption keyl
+ auth_key_len, // authentication key len
+ salt_len, // session salt len
+ secrets->srtpAuthTagLen / 8); // authentication tag lenA
+
+ cc_ctrl = new CryptoContextCtrl(
+ 0, // SSRC (used for lookup)
+ cipher, // encryption algo
+ authn, // authtentication algo
+ (uint8_t *)key, // Master Key
+ key_len, // Master Key length
+ (uint8_t *)salt, // Master Salt
+ salt_len, // Master Salt length
+ key_len, // encryption keyl
+ auth_key_len, // authentication key len
+ salt_len, // session salt len
+ secrets->srtpAuthTagLen / 8); // authentication tag lenA
+
+ if (!cc || !cc_ctrl) {
+ delete cc;
+ delete cc_ctrl;
+
+ err = ENOMEM;
+ return;
+ }
+
+ cc->deriveSrtpKeys(0L);
+ cc_ctrl->deriveSrtcpKeys();
+
+ m_cc = cc;
+ m_cc_ctrl = cc_ctrl;
+
+ err = 0;
+#endif
+}
+
+
+Srtp::~Srtp()
+{
+#ifdef GZRTP_USE_RE_SRTP
+ mem_deref(m_srtp);
+#else
+ delete m_cc;
+ delete m_cc_ctrl;
+#endif
+}
+
+
+int Srtp::protect_int(struct mbuf *mb, bool control)
+{
+ size_t len = mbuf_get_left(mb);
+
+ int32_t extra = (mbuf_get_space(mb) > len)?
+ mbuf_get_space(mb) - len : 0;
+
+#ifdef GZRTP_USE_RE_SRTP
+ if (m_auth_tag_len + (control? 4 : 0) > extra)
+ return ENOMEM;
+
+ if (control)
+ return srtcp_encrypt(m_srtp, mb);
+ else
+ return srtp_encrypt(m_srtp, mb);
+#else
+ if (control) {
+ if (m_cc_ctrl->getTagLength() + 4 +
+ m_cc_ctrl->getMkiLength() > extra)
+ return ENOMEM;
+ }
+ else {
+ if (m_cc->getTagLength() +
+ m_cc->getMkiLength() > extra)
+ return ENOMEM;
+ }
+
+ bool rc;
+
+ if (control)
+ rc = SrtpHandler::protectCtrl(m_cc_ctrl, mbuf_buf(mb),
+ len, &len);
+ else
+ rc = SrtpHandler::protect(m_cc, mbuf_buf(mb), len, &len);
+ if (!rc)
+ return EPROTO;
+
+ if (len > mbuf_get_space(mb)) {
+ // this should never happen
+ error_msg("zrtp: protect: length > space (%u > %u)\n",
+ len, mbuf_get_space(mb));
+ abort();
+ }
+
+ mb->end = mb->pos + len;
+
+ return 0;
+#endif
+}
+
+
+int Srtp::protect(struct mbuf *mb)
+{
+ return protect_int(mb, false);
+}
+
+
+int Srtp::protect_ctrl(struct mbuf *mb)
+{
+ return protect_int(mb, true);
+}
+
+
+// return value:
+// 0 - OK
+// EBADMSG - SRTP/RTP packet decode error
+// EAUTH - SRTP authentication failed
+// EALREADY - SRTP replay check failed
+// other errors
+int Srtp::unprotect_int(struct mbuf *mb, bool control)
+{
+#ifdef GZRTP_USE_RE_SRTP
+ if (control)
+ return srtcp_decrypt(m_srtp, mb);
+ else
+ return srtp_decrypt(m_srtp, mb);
+#else
+ size_t len = mbuf_get_left(mb);
+ uint32_t rc;
+ int err;
+
+ if (control)
+ rc = SrtpHandler::unprotectCtrl(m_cc_ctrl, mbuf_buf(mb),
+ len, &len);
+ else
+ rc = SrtpHandler::unprotect(m_cc, mbuf_buf(mb),
+ len, &len, NULL);
+
+ switch (rc) {
+ case 1: err = 0; break;
+ case 0: err = EBADMSG; break;
+ case -1: err = EAUTH; break;
+ case -2: err = EALREADY; break;
+ default: err = EINVAL;
+ }
+
+ if (!err)
+ mb->end = mb->pos + len;
+
+ return err;
+#endif
+}
+
+
+int Srtp::unprotect(struct mbuf *mb)
+{
+ return unprotect_int(mb, false);
+}
+
+
+int Srtp::unprotect_ctrl(struct mbuf *mb)
+{
+ return unprotect_int(mb, true);
+}
+
diff --git a/modules/gzrtp/srtp.h b/modules/gzrtp/srtp.h
new file mode 100644
index 0000000..d42f4cf
--- /dev/null
+++ b/modules/gzrtp/srtp.h
@@ -0,0 +1,46 @@
+/**
+ * @file srtp.h GNU ZRTP: SRTP processing
+ *
+ * Copyright (C) 2010 - 2017 Creytiv.com
+ */
+#ifndef __SRTP_H
+#define __SRTP_H
+
+
+#include <libzrtpcpp/ZrtpCallback.h>
+
+
+#ifdef GZRTP_USE_RE_SRTP
+struct srtp;
+#else
+class CryptoContext;
+class CryptoContextCtrl;
+#endif
+
+
+class Srtp {
+public:
+ Srtp(int& err, const SrtpSecret_t *secrets, EnableSecurity part);
+ ~Srtp();
+
+ int protect(struct mbuf *mb);
+ int protect_ctrl(struct mbuf *mb);
+ int unprotect(struct mbuf *mb);
+ int unprotect_ctrl(struct mbuf *mb);
+
+private:
+ int protect_int(struct mbuf *mb, bool control);
+ int unprotect_int(struct mbuf *mb, bool control);
+
+#ifdef GZRTP_USE_RE_SRTP
+ int32_t m_auth_tag_len;
+ struct srtp *m_srtp;
+#else
+ CryptoContext *m_cc;
+ CryptoContextCtrl *m_cc_ctrl;
+#endif
+};
+
+
+#endif // __SRTP_H
+
diff --git a/modules/gzrtp/stream.cpp b/modules/gzrtp/stream.cpp
new file mode 100644
index 0000000..84ad5e4
--- /dev/null
+++ b/modules/gzrtp/stream.cpp
@@ -0,0 +1,707 @@
+/**
+ * @file stream.cpp GNU ZRTP: Stream class implementation
+ *
+ * Copyright (C) 2010 - 2017 Creytiv.com
+ */
+#include <stdint.h>
+#include <pthread.h>
+
+#include <re.h>
+#include <baresip.h>
+
+#include <libzrtpcpp/ZRtp.h>
+#include <libzrtpcpp/ZrtpStateClass.h>
+
+#include "session.h"
+#include "stream.h"
+#include "srtp.h"
+
+
+// A burst of SRTP/SRTCP errors enough to display a warning
+// Set to 1 to display all warnings
+#define SRTP_ERR_BURST_THRESHOLD 20
+
+
+enum {
+ PRESZ = 36 /* Preamble size for TURN/STUN header */
+};
+
+
+enum pkt_type {
+ PKT_TYPE_UNKNOWN = 0,
+ PKT_TYPE_RTP = 1,
+ PKT_TYPE_RTCP = 2,
+ PKT_TYPE_ZRTP = 4
+};
+
+
+static enum pkt_type get_packet_type(const struct mbuf *mb)
+{
+ uint8_t b, pt;
+ uint32_t magic;
+
+ if (mbuf_get_left(mb) < 8)
+ return PKT_TYPE_UNKNOWN;
+
+ b = mbuf_buf(mb)[0];
+
+ if (127 < b && b < 192) {
+ pt = mbuf_buf(mb)[1] & 0x7f;
+ if (72 <= pt && pt <= 76)
+ return PKT_TYPE_RTCP;
+ else
+ return PKT_TYPE_RTP;
+ }
+ else {
+ memcpy(&magic, &mbuf_buf(mb)[4], 4);
+ magic = ntohl(magic);
+ if (magic == ZRTP_MAGIC)
+ return PKT_TYPE_ZRTP;
+ }
+
+ return PKT_TYPE_UNKNOWN;
+}
+
+
+ZRTPConfig::ZRTPConfig(const struct conf *conf, const char *conf_dir)
+{
+#ifdef GZRTP_USE_RE_SRTP
+ // Standard ciphers only
+ zrtp.clear();
+
+ zrtp.addAlgo(HashAlgorithm, zrtpHashes.getByName(s256));
+
+ zrtp.addAlgo(CipherAlgorithm, zrtpSymCiphers.getByName(aes3));
+ zrtp.addAlgo(CipherAlgorithm, zrtpSymCiphers.getByName(aes1));
+
+ zrtp.addAlgo(PubKeyAlgorithm, zrtpPubKeys.getByName(ec25));
+ zrtp.addAlgo(PubKeyAlgorithm, zrtpPubKeys.getByName(dh3k));
+ zrtp.addAlgo(PubKeyAlgorithm, zrtpPubKeys.getByName(ec38));
+ zrtp.addAlgo(PubKeyAlgorithm, zrtpPubKeys.getByName(dh2k));
+ zrtp.addAlgo(PubKeyAlgorithm, zrtpPubKeys.getByName(mult));
+
+ zrtp.addAlgo(SasType, zrtpSasTypes.getByName(b32));
+
+ zrtp.addAlgo(AuthLength, zrtpAuthLengths.getByName(hs32));
+ zrtp.addAlgo(AuthLength, zrtpAuthLengths.getByName(hs80));
+#else
+ zrtp.setStandardConfig();
+#endif
+
+ str_ncpy(client_id, "baresip/gzrtp", sizeof(client_id));
+
+ re_snprintf(zid_filename, sizeof(zid_filename),
+ "%s/gzrtp.zid", conf_dir);
+
+ start_parallel = true;
+ (void)conf_get_bool(conf, "zrtp_parallel", &start_parallel);
+}
+
+SRTPStat::SRTPStat(const Stream *st, bool srtcp, uint64_t threshold)
+ : m_stream(st)
+ , m_control(srtcp)
+ , m_threshold(threshold)
+{
+ reset();
+}
+
+
+void SRTPStat::update(int ret_code, bool quiet)
+{
+ const char *err_msg;
+ uint64_t *burst;
+
+ // Srtp::unprotect/unprotect_ctrl return codes
+ switch (ret_code) {
+ case 0:
+ ++m_ok;
+ m_decode_burst = 0;
+ m_auth_burst = 0;
+ m_replay_burst = 0;
+ return;
+ case EBADMSG:
+ ++m_decode;
+ burst = &m_decode_burst;
+ err_msg = "packet decode error";
+ break;
+ case EAUTH:
+ ++m_auth;
+ burst = &m_auth_burst;
+ err_msg = "authentication failed";
+ break;
+ case EALREADY:
+ ++m_replay;
+ burst = &m_replay_burst;
+ err_msg = "replay check failed";
+ break;
+ default:
+ warning("zrtp: %s unprotect failed: %m\n",
+ (m_control)? "SRTCP" : "SRTP", ret_code);
+ return;
+ }
+
+ ++(*burst);
+ if (*burst == m_threshold) {
+ *burst = 0;
+
+ if (!quiet)
+ warning("zrtp: Stream <%s>: %s %s, %d packets\n",
+ m_stream->media_name(),
+ (m_control)? "SRTCP" : "SRTP",
+ err_msg,
+ m_threshold);
+ }
+}
+
+
+void SRTPStat::reset()
+{
+ m_ok = 0;
+ m_decode = 0; m_auth = 0; m_replay = 0;
+ m_decode_burst = 0; m_auth_burst = 0; m_replay_burst = 0;
+}
+
+
+Stream::Stream(int& err, const ZRTPConfig& config, Session *session,
+ udp_sock *rtpsock, udp_sock *rtcpsock,
+ uint32_t local_ssrc, StreamMediaType media_type)
+ : m_session(session)
+ , m_zrtp(NULL)
+ , m_started(false)
+ , m_local_ssrc(local_ssrc)
+ , m_peer_ssrc(0)
+ , m_rtpsock(NULL)
+ , m_rtcpsock(NULL)
+ , m_uh_rtp(NULL)
+ , m_uh_rtcp(NULL)
+ , m_media_type(media_type)
+ , m_send_srtp(NULL)
+ , m_recv_srtp(NULL)
+ , m_srtp_stat(this, false, SRTP_ERR_BURST_THRESHOLD)
+ , m_srtcp_stat(this, true, SRTP_ERR_BURST_THRESHOLD)
+{
+ err = 0;
+
+ m_zrtp_seq = 1; // TODO: randomize
+ sa_init(&m_raddr, AF_INET);
+ tmr_init(&m_zrtp_timer);
+
+ pthread_mutexattr_t attr;
+ err = pthread_mutexattr_init(&attr);
+ err |= pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK);
+ err |= pthread_mutex_init(&m_zrtp_mutex, &attr);
+ err |= pthread_mutex_init(&m_send_mutex, &attr);
+ if (err)
+ return;
+
+ int layer = 10; // above zero
+ if (rtpsock) {
+ m_rtpsock = (struct udp_sock *)mem_ref(rtpsock);
+ err |= udp_register_helper(&m_uh_rtp, rtpsock, layer,
+ Stream::udp_helper_send_cb,
+ Stream::udp_helper_recv_cb,
+ this);
+ }
+ if (rtcpsock && (rtcpsock != rtpsock)) {
+ m_rtcpsock = (struct udp_sock *)mem_ref(rtcpsock);
+ err |= udp_register_helper(&m_uh_rtcp, rtcpsock, layer,
+ Stream::udp_helper_send_cb,
+ Stream::udp_helper_recv_cb,
+ this);
+ }
+ if (err)
+ return;
+
+ ZIDCache* zf = getZidCacheInstance();
+ if (!zf->isOpen()) {
+ if (zf->open((char *)config.zid_filename) == -1) {
+ warning("zrtp: Couldn't open/create ZID file %s\n",
+ config.zid_filename);
+ err = ENOENT;
+ return;
+ }
+ }
+
+ m_zrtp = new ZRtp((uint8_t *)zf->getZid(), this, config.client_id,
+ (ZrtpConfigure *)&config.zrtp, false, false);
+ if (!m_zrtp) {
+ err = ENOMEM;
+ return;
+ }
+
+ return;
+}
+
+
+Stream::~Stream()
+{
+ stop();
+
+ delete m_zrtp;
+
+ mem_deref(m_uh_rtp);
+ mem_deref(m_uh_rtcp);
+ mem_deref(m_rtpsock);
+ mem_deref(m_rtcpsock);
+
+ pthread_mutex_destroy(&m_zrtp_mutex);
+ pthread_mutex_destroy(&m_send_mutex);
+
+ tmr_cancel(&m_zrtp_timer);
+}
+
+
+int Stream::start(Stream *master)
+{
+ if (started())
+ return EPERM;
+
+ if (master) {
+ ZRtp *zrtp_master;
+
+ std::string params =
+ master->m_zrtp->getMultiStrParams(&zrtp_master);
+ if (params.empty())
+ return EPROTO;
+
+ m_zrtp->setMultiStrParams(params, zrtp_master);
+ }
+
+ debug("zrtp: Starting <%s> stream%s\n", media_name(),
+ (m_zrtp->isMultiStream())? " (multistream)" : "");
+
+ m_srtp_stat.reset();
+ m_srtcp_stat.reset();
+ m_sas.clear();
+ m_ciphers.clear();
+
+ m_started = true;
+ m_zrtp->startZrtpEngine();
+
+ return 0;
+}
+
+
+void Stream::stop()
+{
+ if (!started())
+ return;
+
+ m_started = false;
+
+ // If we got only a small amount of valid SRTP packets after ZRTP
+ // negotiation then assume that our peer couldn't store the RS data,
+ // thus make sure we have a second retained shared secret available.
+ // Refer to RFC 6189bis, chapter 4.6.1 50 packets are about 1 second
+ // of audio data
+ if (!m_zrtp->isMultiStream() && m_recv_srtp && m_srtp_stat.ok() < 20) {
+
+ debug("zrtp: Stream <%s>: received too few valid SRTP "
+ "packets (%u), storing RS2\n",
+ media_name(), m_srtp_stat.ok());
+
+ m_zrtp->setRs2Valid();
+ }
+
+ debug("zrtp: Stopping <%s> stream\n", media_name());
+
+ m_zrtp->stopZrtp();
+
+ pthread_mutex_lock(&m_send_mutex);
+ delete m_send_srtp;
+ m_send_srtp = NULL;
+ pthread_mutex_unlock(&m_send_mutex);
+
+ delete m_recv_srtp;
+ m_recv_srtp = NULL;
+
+ debug("zrtp: Stream <%s> stopped\n", media_name());
+}
+
+
+int Stream::sdp_encode(struct sdp_media *sdpm)
+{
+ // TODO: signaling hash
+ return 0;
+}
+
+
+int Stream::sdp_decode(const struct sdp_media *sdpm)
+{
+ if (sa_isset(sdp_media_raddr(sdpm), SA_ALL)) {
+ m_raddr = *sdp_media_raddr(sdpm);
+ }
+ // TODO: signaling hash
+
+ return 0;
+}
+
+
+bool Stream::udp_helper_send_cb(int *err, struct sa *src, struct mbuf *mb,
+ void *arg)
+{
+ Stream *st = (Stream *)arg;
+
+ if (st)
+ return st->udp_helper_send(err, src, mb);
+
+ return false;
+}
+
+
+bool Stream::udp_helper_send(int *err, struct sa *src, struct mbuf *mb)
+{
+ bool ret = false;
+ enum pkt_type ptype = get_packet_type(mb);
+ size_t len = mbuf_get_left(mb);
+ int rerr = 0;
+
+ pthread_mutex_lock(&m_send_mutex);
+
+ if (ptype == PKT_TYPE_RTCP && m_send_srtp && len > 8) {
+
+ rerr = m_send_srtp->protect_ctrl(mb);
+ }
+ else if (ptype == PKT_TYPE_RTP && m_send_srtp &&
+ len > RTP_HEADER_SIZE) {
+
+ rerr = m_send_srtp->protect(mb);
+ }
+ else
+ goto out;
+
+ if (rerr) {
+ warning("zrtp: protect/protect_ctrl failed (len=%u): %m\n",
+ len, rerr);
+
+ if (rerr == ENOMEM)
+ *err = rerr;
+ // drop
+ ret = true;
+ }
+
+ out:
+ pthread_mutex_unlock(&m_send_mutex);
+
+ return ret;
+}
+
+
+bool Stream::udp_helper_recv_cb(struct sa *src, struct mbuf *mb, void *arg)
+{
+ Stream *st = (Stream *)arg;
+
+ if (st)
+ return st->udp_helper_recv(src, mb);
+
+ return false;
+}
+
+
+bool Stream::udp_helper_recv(struct sa *src, struct mbuf *mb)
+{
+ if (!started())
+ return false;
+
+ enum pkt_type ptype = get_packet_type(mb);
+ int err = 0;
+
+ if (ptype == PKT_TYPE_RTCP && m_recv_srtp) {
+
+ err = m_recv_srtp->unprotect_ctrl(mb);
+
+ m_srtcp_stat.update(err);
+ }
+ else if (ptype == PKT_TYPE_RTP && m_recv_srtp) {
+
+ err = m_recv_srtp->unprotect(mb);
+
+ m_srtp_stat.update(err);
+
+ if (!err) {
+ // Got a good SRTP, check state and if in WaitConfAck
+ // (an Initiator state) then simulate a conf2Ack,
+ // refer to RFC 6189, chapter 4.6, last paragraph
+ if (m_zrtp->inState(WaitConfAck))
+ m_zrtp->conf2AckSecure();
+ }
+ }
+ else if (ptype == PKT_TYPE_ZRTP) {
+ return recv_zrtp(mb);
+ }
+ else
+ return false;
+
+ if (err)
+ // drop
+ return true;
+
+ return false;
+}
+
+
+// <RTP> + <ext. header> + <ZRTP message type> + CRC32
+#define ZRTP_MIN_PACKET_LENGTH (RTP_HEADER_SIZE + 4 + 8 + 4)
+
+bool Stream::recv_zrtp(struct mbuf *mb)
+{
+ uint32_t crc32;
+ uint8_t *buf = mbuf_buf(mb);
+ size_t size = mbuf_get_left(mb);
+
+ if (size < ZRTP_MIN_PACKET_LENGTH) {
+ warning("zrtp: incoming packet size (%d) is too small\n",
+ size);
+ return false;
+ }
+
+ // check CRC
+ memcpy(&crc32, buf + size - 4, 4);
+ crc32 = ntohl(crc32);
+ if (!zrtpCheckCksum(buf, size - 4, crc32)) {
+ sendInfo(GnuZrtpCodes::Warning,
+ GnuZrtpCodes::WarningCRCmismatch);
+ return false;
+ }
+
+ // store peer's SSRC for creating the CryptoContext
+ memcpy(&m_peer_ssrc, buf + 8, 4);
+ m_peer_ssrc = ntohl(m_peer_ssrc);
+
+ m_zrtp->processZrtpMessage(buf + RTP_HEADER_SIZE, m_peer_ssrc, size);
+
+ return true;
+}
+
+
+void Stream::verify_sas(bool verify)
+{
+ if (verify)
+ m_zrtp->SASVerified();
+ else
+ m_zrtp->resetSASVerified();
+}
+
+
+bool Stream::sas_verified()
+{
+ return m_zrtp->isSASVerified();
+}
+
+
+//
+// callbacks
+//
+
+
+int32_t Stream::sendDataZRTP(const uint8_t* data, int32_t length)
+{
+ struct mbuf *mb;
+ uint8_t *crc_buf;
+ uint32_t crc32;
+ size_t start_pos = PRESZ;
+ int err = 0;
+
+ if (!sa_isset(&m_raddr, SA_ALL))
+ return 0;
+
+ mb = mbuf_alloc(start_pos + RTP_HEADER_SIZE + length);
+ if (!mb)
+ return 0;
+
+ mbuf_set_end(mb, start_pos);
+ mbuf_set_pos(mb, start_pos);
+ crc_buf = mbuf_buf(mb);
+
+ // write RTP header
+ err = mbuf_write_u8(mb, 0x10);
+ err |= mbuf_write_u8(mb, 0x00);
+ err |= mbuf_write_u16(mb, htons(m_zrtp_seq++));
+ err |= mbuf_write_u32(mb, htonl(ZRTP_MAGIC));
+ err |= mbuf_write_u32(mb, htonl(m_local_ssrc));
+
+ // copy ZRTP message data
+ err |= mbuf_write_mem(mb, data, length - 4);
+
+ // compute CRC
+ crc32 = zrtpGenerateCksum(crc_buf, RTP_HEADER_SIZE + length - 4);
+ crc32 = zrtpEndCksum(crc32);
+
+ // store CRC
+ err |= mbuf_write_u32(mb, htonl(crc32));
+ if (err)
+ goto out;
+
+ // send ZRTP packet using RTP socket
+ mbuf_set_pos(mb, start_pos);
+ err = udp_send_helper(m_rtpsock, &m_raddr, mb, m_uh_rtp);
+ if (err)
+ warning("zrtp: udp_send_helper: %m\n", err);
+
+ out:
+ mem_deref(mb);
+
+ return (err == 0);
+}
+
+
+void Stream::zrtp_timer_cb(void *arg)
+{
+ Stream *s = (Stream *)arg;
+
+ s->m_zrtp->processTimeout();
+}
+
+
+int32_t Stream::activateTimer(int32_t time)
+{
+ tmr_start(&m_zrtp_timer, time, &Stream::zrtp_timer_cb, this);
+ return 1;
+}
+
+
+int32_t Stream::cancelTimer()
+{
+ tmr_cancel(&m_zrtp_timer);
+ return 1;
+}
+
+
+void Stream::sendInfo(GnuZrtpCodes::MessageSeverity severity, int32_t subCode)
+{
+ print_message(severity, subCode);
+
+ if (severity == GnuZrtpCodes::Info) {
+ if (subCode == GnuZrtpCodes::InfoSecureStateOn) {
+ m_session->on_secure(this);
+ }
+ else if (subCode == GnuZrtpCodes::InfoHelloReceived &&
+ !m_zrtp->isMultiStream()) {
+
+ m_session->request_master(this);
+ }
+ }
+}
+
+
+bool Stream::srtpSecretsReady(SrtpSecret_t* secrets, EnableSecurity part)
+{
+ Srtp *s;
+ int err = 0;
+
+ debug("zrtp: Stream <%s>: secrets are ready for %s\n",
+ media_name(),
+ (part == ForSender)? "sender" : "receiver");
+
+ s = new Srtp(err, secrets, part);
+ if (!s || err) {
+ warning("zrtp: Stream <%s>: Srtp creation failed: %m\n",
+ media_name(), err);
+ delete s;
+ return false;
+ }
+
+ if (part == ForSender) {
+ pthread_mutex_lock(&m_send_mutex);
+ m_send_srtp = s;
+ pthread_mutex_unlock(&m_send_mutex);
+ }
+ else if (part == ForReceiver)
+ m_recv_srtp = s;
+ else
+ return false;
+
+ return true;
+}
+
+
+void Stream::srtpSecretsOff(EnableSecurity part)
+{
+ debug("zrtp: Stream <%s>: secrets are off for %s\n",
+ media_name(),
+ (part == ForSender)? "sender" : "receiver");
+
+ if (part == ForSender) {
+ pthread_mutex_lock(&m_send_mutex);
+ delete m_send_srtp;
+ m_send_srtp = NULL;
+ pthread_mutex_unlock(&m_send_mutex);
+ }
+
+ if (part == ForReceiver) {
+ delete m_recv_srtp;
+ m_recv_srtp = NULL;
+ }
+}
+
+
+void Stream::srtpSecretsOn(std::string c, std::string s, bool verified)
+{
+ m_sas = s;
+ m_ciphers = c;
+
+ if (s.empty()) {
+ info("zrtp: Stream <%s> is encrypted (%s)\n",
+ media_name(), c.c_str());
+ }
+ else {
+ info("zrtp: Stream <%s> is encrypted (%s), "
+ "SAS is [%s] (%s)\n",
+ media_name(), c.c_str(), s.c_str(),
+ (verified)? "verified" : "NOT VERIFIED");
+ if (!verified)
+ warning("zrtp: SAS is not verified, type "
+ "'/zrtp_verify %d' to verify\n",
+ m_session->id());
+ }
+}
+
+
+void Stream::handleGoClear()
+{
+}
+
+
+void Stream::zrtpNegotiationFailed(GnuZrtpCodes::MessageSeverity severity,
+ int32_t subCode)
+{
+}
+
+
+void Stream::zrtpNotSuppOther()
+{
+}
+
+
+void Stream::synchEnter()
+{
+ pthread_mutex_lock(&m_zrtp_mutex);
+}
+
+
+void Stream::synchLeave()
+{
+ pthread_mutex_unlock(&m_zrtp_mutex);
+}
+
+
+void Stream::zrtpAskEnrollment(GnuZrtpCodes::InfoEnrollment info)
+{
+}
+
+
+void Stream::zrtpInformEnrollment(GnuZrtpCodes::InfoEnrollment info)
+{
+}
+
+
+void Stream::signSAS(uint8_t* sasHash)
+{
+}
+
+
+bool Stream::checkSASSignature(uint8_t* sasHash)
+{
+ return true;
+}
+
diff --git a/modules/gzrtp/stream.h b/modules/gzrtp/stream.h
new file mode 100644
index 0000000..d25a5c1
--- /dev/null
+++ b/modules/gzrtp/stream.h
@@ -0,0 +1,138 @@
+/**
+ * @file stream.h GNU ZRTP: Stream class
+ *
+ * Copyright (C) 2010 - 2017 Creytiv.com
+ */
+#ifndef __STREAM_H
+#define __STREAM_H
+
+
+#include <libzrtpcpp/ZRtp.h>
+
+
+enum StreamMediaType {
+ MT_UNKNOWN = 0,
+ MT_AUDIO,
+ MT_VIDEO,
+ MT_TEXT,
+ MT_APPLICATION,
+ MT_MESSAGE
+};
+
+
+class ZRTPConfig {
+public:
+ ZRTPConfig(const struct conf *conf, const char *conf_dir);
+private:
+ friend class Stream;
+ friend class Session;
+
+ ZrtpConfigure zrtp;
+
+ char client_id[CLIENT_ID_SIZE + 1];
+ char zid_filename[256];
+
+ bool start_parallel;
+};
+
+
+class Stream;
+
+class SRTPStat {
+public:
+ SRTPStat(const Stream *st, bool srtcp, uint64_t threshold);
+ void update(int ret_code, bool quiet = false);
+ void reset();
+ uint64_t ok() { return m_ok; }
+private:
+ const Stream *m_stream;
+ const bool m_control;
+ const uint64_t m_threshold;
+ uint64_t m_ok, m_decode, m_auth, m_replay;
+ uint64_t m_decode_burst, m_auth_burst, m_replay_burst;
+};
+
+
+class Session;
+class Srtp;
+
+class Stream : public ZrtpCallback {
+public:
+ Stream(int& err, const ZRTPConfig& config, Session *session,
+ udp_sock *rtpsock, udp_sock *rtcpsock,
+ uint32_t local_ssrc, StreamMediaType media_type);
+
+ virtual ~Stream();
+
+ int start(Stream *master);
+ void stop();
+ bool started() { return m_started; }
+
+ int sdp_encode(struct sdp_media *sdpm);
+ int sdp_decode(const struct sdp_media *sdpm);
+
+ const char *media_name() const;
+
+ const char *get_sas() const { return m_sas.c_str(); }
+ const char *get_ciphers() const { return m_ciphers.c_str(); }
+ bool sas_verified();
+ void verify_sas(bool verify);
+
+private:
+ static void zrtp_timer_cb(void *arg);
+ static bool udp_helper_send_cb(int *err, struct sa *src,
+ struct mbuf *mb, void *arg);
+ static bool udp_helper_recv_cb(struct sa *src, struct mbuf *mb,
+ void *arg);
+
+ bool udp_helper_send(int *err, struct sa *src, struct mbuf *mb);
+ bool udp_helper_recv(struct sa *src, struct mbuf *mb);
+ bool recv_zrtp(struct mbuf *mb);
+
+ void print_message(GnuZrtpCodes::MessageSeverity severity,
+ int32_t subcode);
+
+ Session *m_session;
+ ZRtp *m_zrtp;
+ bool m_started;
+ struct tmr m_zrtp_timer;
+ pthread_mutex_t m_zrtp_mutex;
+ uint16_t m_zrtp_seq;
+ uint32_t m_local_ssrc, m_peer_ssrc;
+ struct sa m_raddr;
+ struct udp_sock *m_rtpsock, *m_rtcpsock;
+ struct udp_helper *m_uh_rtp;
+ struct udp_helper *m_uh_rtcp;
+ StreamMediaType m_media_type;
+ Srtp *m_send_srtp, *m_recv_srtp;
+ pthread_mutex_t m_send_mutex;
+ SRTPStat m_srtp_stat, m_srtcp_stat;
+ std::string m_sas, m_ciphers;
+
+protected:
+ virtual int32_t sendDataZRTP(const uint8_t* data, int32_t length);
+ virtual int32_t activateTimer(int32_t time);
+ virtual int32_t cancelTimer();
+ virtual void sendInfo(GnuZrtpCodes::MessageSeverity severity,
+ int32_t subCode);
+ virtual bool srtpSecretsReady(SrtpSecret_t* secrets,
+ EnableSecurity part);
+ virtual void srtpSecretsOff(EnableSecurity part);
+ virtual void srtpSecretsOn(std::string c, std::string s,
+ bool verified);
+ virtual void handleGoClear();
+ virtual void zrtpNegotiationFailed(
+ GnuZrtpCodes::MessageSeverity severity,
+ int32_t subCode);
+ virtual void zrtpNotSuppOther();
+ virtual void synchEnter();
+ virtual void synchLeave();
+ virtual void zrtpAskEnrollment(GnuZrtpCodes::InfoEnrollment info);
+ virtual void zrtpInformEnrollment(GnuZrtpCodes::InfoEnrollment info);
+ virtual void signSAS(uint8_t* sasHash);
+ virtual bool checkSASSignature(uint8_t* sasHash);
+};
+
+
+#endif // __STREAM_H
+
diff --git a/modules/h265/fmt.c b/modules/h265/fmt.c
index b30df2c..ac71b10 100644
--- a/modules/h265/fmt.c
+++ b/modules/h265/fmt.c
@@ -1,3 +1,8 @@
+/**
+ * @file h265/fmt.c H.265 Video Codec -- protocol format
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
#include <string.h>
#include <re.h>
@@ -58,11 +63,11 @@ int h265_nal_decode(struct h265_nal *nal, const uint8_t *p)
nal->nuh_temporal_id_plus1 = p[1] & 0x07;
if (forbidden_zero_bit) {
- re_fprintf(stderr, "?!?!?!?! FORBIDDEN !!! ?!?!?!*\n");
+ warning("h265: nal_decode: FORBIDDEN bit set\n");
return EBADMSG;
}
if (nuh_layer_id != 0) {
- re_fprintf(stderr, "h265_nal_decode: LayerId MUST be zero\n");
+ warning("h265: nal_decode: LayerId MUST be zero\n");
return EBADMSG;
}
diff --git a/modules/jack/jack_play.c b/modules/jack/jack_play.c
index 4f19b68..f890ddd 100644
--- a/modules/jack/jack_play.c
+++ b/modules/jack/jack_play.c
@@ -201,6 +201,12 @@ int jack_play_alloc(struct auplay_st **stp, const struct auplay *ap,
if (prm->ch > ARRAY_SIZE(st->portv))
return EINVAL;
+ if (prm->fmt != AUFMT_S16LE) {
+ warning("jack: playback: unsupported sample format (%s)\n",
+ aufmt_name(prm->fmt));
+ return ENOTSUP;
+ }
+
st = mem_zalloc(sizeof(*st), auplay_destructor);
if (!st)
return ENOMEM;
diff --git a/modules/jack/jack_src.c b/modules/jack/jack_src.c
index e4662c3..0ef1e42 100644
--- a/modules/jack/jack_src.c
+++ b/modules/jack/jack_src.c
@@ -196,6 +196,12 @@ int jack_src_alloc(struct ausrc_st **stp, const struct ausrc *as,
if (prm->ch > ARRAY_SIZE(st->portv))
return EINVAL;
+ if (prm->fmt != AUFMT_S16LE) {
+ warning("jack: source: unsupported sample format (%s)\n",
+ aufmt_name(prm->fmt));
+ return ENOTSUP;
+ }
+
st = mem_zalloc(sizeof(*st), ausrc_destructor);
if (!st)
return ENOMEM;
diff --git a/modules/menu/menu.c b/modules/menu/menu.c
index 23aae45..c7f1646 100644
--- a/modules/menu/menu.c
+++ b/modules/menu/menu.c
@@ -37,7 +37,7 @@ static struct {
struct play *play;
struct message_lsnr *message;
bool bell;
-
+ bool ringback_disabled; /**< no ringback on sip 180 respons */
struct tmr tmr_redial; /**< Timer for auto-reconnect */
uint32_t redial_delay; /**< Redial delay in [seconds] */
uint32_t redial_attempts; /**< Number of re-dial attempts */
@@ -376,6 +376,8 @@ static int create_ua(struct re_printf *pf, void *arg)
static int cmd_ua_next(struct re_printf *pf, void *unused)
{
+ int err;
+
(void)pf;
(void)unused;
@@ -386,13 +388,13 @@ static int cmd_ua_next(struct re_printf *pf, void *unused)
le_cur = le_cur->next ? le_cur->next : list_head(uag_list());
- (void)re_fprintf(stderr, "ua: %s\n", ua_aor(list_ledata(le_cur)));
+ err = re_hprintf(pf, "ua: %s\n", ua_aor(list_ledata(le_cur)));
uag_current_set(list_ledata(le_cur));
update_callstatus();
- return 0;
+ return err;
}
@@ -977,7 +979,13 @@ static void ua_event_handler(struct ua *ua, enum ua_event ev,
/* stop any ringtones */
menu.play = mem_deref(menu.play);
- (void)play_file(&menu.play, player, "ringback.wav", -1);
+ if (menu.ringback_disabled) {
+ info("\nRingback disabled\n");
+ }
+ else {
+ (void)play_file(&menu.play, player,
+ "ringback.wav",-1);
+ }
break;
case UA_EVENT_CALL_ESTABLISHED:
@@ -1058,8 +1066,8 @@ static void message_handler(const struct pl *peer, const struct pl *ctype,
(void)ctype;
(void)arg;
- (void)re_fprintf(stderr, "\r%r: \"%b\"\n", peer,
- mbuf_buf(body), mbuf_get_left(body));
+ ui_output(baresip_uis(), "\r%r: \"%b\"\n",
+ peer, mbuf_buf(body), mbuf_get_left(body));
(void)play_file(NULL, baresip_player(), "message.wav", 0);
}
@@ -1074,6 +1082,8 @@ static int module_init(void)
* Read the config values
*/
conf_get_bool(conf_cur(), "menu_bell", &menu.bell);
+ conf_get_bool(conf_cur(), "ringback_disabled",
+ &menu.ringback_disabled);
if (0 == conf_get(conf_cur(), "redial_attempts", &val) &&
0 == pl_strcasecmp(&val, "inf")) {
diff --git a/modules/mqtt/README.md b/modules/mqtt/README.md
new file mode 100644
index 0000000..6a86b84
--- /dev/null
+++ b/modules/mqtt/README.md
@@ -0,0 +1,59 @@
+README
+------
+
+
+This module implements an MQTT (Message Queue Telemetry Transport) client
+for publishing and subscribing to topics.
+
+
+The module is using libmosquitto
+
+
+Starting the MQTT broker:
+
+```
+$ /usr/local/sbin/mosquitto -v
+```
+
+
+Subscribing to all topics:
+
+```
+$ mosquitto_sub -t /baresip/+
+```
+
+
+Publishing to the topic:
+
+```
+$ mosquitto_pub -t /baresip/xxx -m foo=42
+```
+
+
+## Topic patterns
+
+(Outgoing direction is from baresip mqtt module to broker,
+ incoming direction is from broker to baresip mqtt module)
+
+* /baresip/event Outgoing events from ua_event
+* /baresip/command Incoming long command request
+* /baresip/command_resp Outgoing long command response
+
+
+## Examples
+
+```
+/baresip/event sip:aeh@iptel.org,REGISTERING
+/baresip/event sip:aeh@iptel.org,REGISTER_OK
+/baresip/event sip:aeh@iptel.org,SHUTDOWN
+```
+
+```
+mosquitto_pub -t /baresip/command -m "/dial music"
+
+/baresip/command /dial music
+/baresip/command_resp (null)
+/baresip/event sip:aeh@iptel.org,CALL_ESTABLISHED
+/baresip/event sip:aeh@iptel.org,CALL_CLOSED
+```
+
diff --git a/modules/mqtt/module.mk b/modules/mqtt/module.mk
new file mode 100644
index 0000000..cb4c379
--- /dev/null
+++ b/modules/mqtt/module.mk
@@ -0,0 +1,14 @@
+#
+# module.mk
+#
+# Copyright (C) 2010 Creytiv.com
+#
+
+MOD := mqtt
+$(MOD)_SRCS += mqtt.c
+$(MOD)_SRCS += publish.c
+$(MOD)_SRCS += subscribe.c
+$(MOD)_LFLAGS += -lmosquitto
+$(MOD)_CFLAGS +=
+
+include mk/mod.mk
diff --git a/modules/mqtt/mqtt.c b/modules/mqtt/mqtt.c
new file mode 100644
index 0000000..a927959
--- /dev/null
+++ b/modules/mqtt/mqtt.c
@@ -0,0 +1,157 @@
+/**
+ * @file mqtt.c Message Queue Telemetry Transport (MQTT) client
+ *
+ * Copyright (C) 2017 Creytiv.com
+ */
+
+#include <mosquitto.h>
+#include <re.h>
+#include <baresip.h>
+#include "mqtt.h"
+
+
+static char broker_host[256] = "127.0.0.1";
+static uint32_t broker_port = 1883;
+
+static struct mqtt s_mqtt;
+
+
+static void fd_handler(int flags, void *arg)
+{
+ struct mqtt *mqtt = arg;
+
+ mosquitto_loop_read(mqtt->mosq, 1);
+
+ mosquitto_loop_write(mqtt->mosq, 1);
+}
+
+
+/* XXX: use mosquitto_socket and fd_listen instead? */
+static void tmr_handler(void *data)
+{
+ struct mqtt *mqtt = data;
+ int ret;
+
+ tmr_start(&mqtt->tmr, 500, tmr_handler, mqtt);
+
+ ret = mosquitto_loop_misc(mqtt->mosq);
+ if (ret != MOSQ_ERR_SUCCESS) {
+ warning("mqtt: error in loop (%s)\n", mosquitto_strerror(ret));
+ }
+}
+
+
+/*
+ * This is called when the broker sends a CONNACK message
+ * in response to a connection.
+ */
+static void connect_callback(struct mosquitto *mosq, void *obj, int result)
+{
+ struct mqtt *mqtt = obj;
+ int err;
+ (void)mqtt;
+
+ if (result != MOSQ_ERR_SUCCESS) {
+ warning("mqtt: could not connect to broker (%s)\n",
+ mosquitto_strerror(result));
+ return;
+ }
+
+ info("mqtt: connected to broker at %s:%d\n",
+ broker_host, broker_port);
+
+ err = mqtt_subscribe_start(mqtt);
+ if (err) {
+ warning("mqtt: subscribe_init failed (%m)\n", err);
+ }
+}
+
+
+static int module_init(void)
+{
+ const int keepalive = 60;
+ int ret;
+ int err = 0;
+
+ tmr_init(&s_mqtt.tmr);
+
+ mosquitto_lib_init();
+
+ conf_get_str(conf_cur(), "mqtt_broker_host",
+ broker_host, sizeof(broker_host));
+ conf_get_u32(conf_cur(), "mqtt_broker_port", &broker_port);
+
+ s_mqtt.mosq = mosquitto_new("baresip", true, &s_mqtt);
+ if (!s_mqtt.mosq) {
+ warning("mqtt: failed to create client instance\n");
+ return ENOMEM;
+ }
+
+ err = mqtt_subscribe_init(&s_mqtt);
+ if (err)
+ return err;
+
+ mosquitto_connect_callback_set(s_mqtt.mosq, connect_callback);
+
+ ret = mosquitto_connect(s_mqtt.mosq, broker_host, broker_port,
+ keepalive);
+ if (ret != MOSQ_ERR_SUCCESS) {
+
+ err = ret == MOSQ_ERR_ERRNO ? errno : EIO;
+
+ warning("mqtt: failed to connect to %s:%d (%s)\n",
+ broker_host, broker_port,
+ mosquitto_strerror(ret));
+ return err;
+ }
+
+ tmr_start(&s_mqtt.tmr, 1, tmr_handler, &s_mqtt);
+
+ err = mqtt_publish_init(&s_mqtt);
+ if (err)
+ return err;
+
+ s_mqtt.fd = mosquitto_socket(s_mqtt.mosq);
+
+ err = fd_listen(s_mqtt.fd, FD_READ, fd_handler, &s_mqtt);
+ if (err)
+ return err;
+
+ info("mqtt: module loaded\n");
+
+ return err;
+}
+
+
+static int module_close(void)
+{
+ fd_close(s_mqtt.fd);
+
+ mqtt_publish_close();
+
+ mqtt_subscribe_close();
+
+ tmr_cancel(&s_mqtt.tmr);
+
+ if (s_mqtt.mosq) {
+
+ mosquitto_disconnect(s_mqtt.mosq);
+
+ mosquitto_destroy(s_mqtt.mosq);
+ s_mqtt.mosq = NULL;
+ }
+
+ mosquitto_lib_cleanup();
+
+ info("mqtt: module unloaded\n");
+
+ return 0;
+}
+
+
+const struct mod_export DECL_EXPORTS(mqtt) = {
+ "mqtt",
+ "application",
+ module_init,
+ module_close
+};
diff --git a/modules/mqtt/mqtt.h b/modules/mqtt/mqtt.h
new file mode 100644
index 0000000..e79fb6e
--- /dev/null
+++ b/modules/mqtt/mqtt.h
@@ -0,0 +1,26 @@
+
+
+struct mqtt {
+ struct mosquitto *mosq;
+ struct tmr tmr;
+ int fd;
+};
+
+
+/*
+ * Subscribe direction (incoming)
+ */
+
+int mqtt_subscribe_init(struct mqtt *mqtt);
+int mqtt_subscribe_start(struct mqtt *mqtt);
+void mqtt_subscribe_close(void);
+
+
+/*
+ * Publish direction (outgoing)
+ */
+
+int mqtt_publish_init(struct mqtt *mqtt);
+void mqtt_publish_close(void);
+int mqtt_publish_message(struct mqtt *mqtt, const char *topic,
+ const char *fmt, ...);
diff --git a/modules/mqtt/publish.c b/modules/mqtt/publish.c
new file mode 100644
index 0000000..524b0a2
--- /dev/null
+++ b/modules/mqtt/publish.c
@@ -0,0 +1,104 @@
+/**
+ * @file publish.c MQTT client -- publish
+ *
+ * Copyright (C) 2017 Creytiv.com
+ */
+
+#include <mosquitto.h>
+#include <re.h>
+#include <baresip.h>
+#include "mqtt.h"
+
+
+/*
+ * This file contains functions for sending outgoing messages
+ * from baresip to broker (publish)
+ */
+
+
+/*
+ * Relay UA events as publish messages to the Broker
+ *
+ * XXX: move JSON encoding to baresip core
+ */
+static void ua_event_handler(struct ua *ua, enum ua_event ev,
+ struct call *call, const char *prm, void *arg)
+{
+ struct mqtt *mqtt = arg;
+ struct odict *od = NULL;
+ int err;
+
+ err = odict_alloc(&od, 8);
+ if (err)
+ return;
+
+ err = event_encode_dict(od, ua, ev, call, prm);
+ if (err)
+ goto out;
+
+ err = mqtt_publish_message(mqtt, "/baresip/event", "%H",
+ json_encode_odict, od);
+ if (err) {
+ warning("mqtt: failed to publish message (%m)\n", err);
+ goto out;
+ }
+
+ out:
+ mem_deref(od);
+}
+
+
+int mqtt_publish_message(struct mqtt *mqtt, const char *topic,
+ const char *fmt, ...)
+{
+ char *message;
+ va_list ap;
+ int ret;
+ int err = 0;
+
+ if (!mqtt || !topic || !fmt)
+ return EINVAL;
+
+ va_start(ap, fmt);
+ err = re_vsdprintf(&message, fmt, ap);
+ va_end(ap);
+
+ if (err)
+ return err;
+
+ ret = mosquitto_publish(mqtt->mosq,
+ NULL,
+ topic,
+ (int)str_len(message),
+ message,
+ 0,
+ false);
+ if (ret != MOSQ_ERR_SUCCESS) {
+ warning("mqtt: failed to publish (%s)\n",
+ mosquitto_strerror(ret));
+ err = EINVAL;
+ goto out;
+ }
+
+ out:
+ mem_deref(message);
+ return err;
+}
+
+
+int mqtt_publish_init(struct mqtt *mqtt)
+{
+ int err;
+
+ err = uag_event_register(ua_event_handler, mqtt);
+ if (err)
+ return err;
+
+ return err;
+}
+
+
+void mqtt_publish_close(void)
+{
+ uag_event_unregister(&ua_event_handler);
+}
diff --git a/modules/mqtt/subscribe.c b/modules/mqtt/subscribe.c
new file mode 100644
index 0000000..d36ece2
--- /dev/null
+++ b/modules/mqtt/subscribe.c
@@ -0,0 +1,146 @@
+/**
+ * @file subscribe.c MQTT client -- subscribe
+ *
+ * Copyright (C) 2017 Creytiv.com
+ */
+
+#include <mosquitto.h>
+#include <re.h>
+#include <baresip.h>
+#include "mqtt.h"
+
+
+static const char *subscription_pattern = "/baresip/+";
+
+
+static int print_handler(const char *p, size_t size, void *arg)
+{
+ struct mbuf *mb = arg;
+
+ return mbuf_write_mem(mb, (void *)p, size);
+}
+
+
+static void handle_command(struct mqtt *mqtt, const struct pl *msg)
+{
+ struct mbuf *resp = mbuf_alloc(1024);
+ struct re_printf pf = {print_handler, resp};
+ struct odict *od = NULL;
+ const struct odict_entry *oe_cmd, *oe_prm, *oe_tok;
+ char buf[256], resp_topic[256];
+ int err;
+
+ /* XXX: add transaction ID ? */
+
+ err = json_decode_odict(&od, 32, msg->p, msg->l, 16);
+ if (err) {
+ warning("mqtt: failed to decode JSON with %zu bytes (%m)\n",
+ msg->l, err);
+ return;
+ }
+
+ oe_cmd = odict_lookup(od, "command");
+ oe_prm = odict_lookup(od, "params");
+ oe_tok = odict_lookup(od, "token");
+ if (!oe_cmd) {
+ warning("mqtt: missing json entries\n");
+ goto out;
+ }
+
+ debug("mqtt: handle_command: cmd='%s', token='%s'\n",
+ oe_cmd ? oe_cmd->u.str : "",
+ oe_tok ? oe_tok->u.str : "");
+
+ re_snprintf(buf, sizeof(buf), "%s%s%s",
+ oe_cmd->u.str,
+ oe_prm ? " " : "",
+ oe_prm ? oe_prm->u.str : "");
+
+ /* Relay message to long commands */
+ err = cmd_process_long(baresip_commands(),
+ buf,
+ str_len(buf),
+ &pf, NULL);
+ if (err) {
+ warning("mqtt: error processing command (%m)\n", err);
+ }
+
+ /* NOTE: the command will now write the response
+ to the resp mbuf, send it back to broker */
+
+ re_snprintf(resp_topic, sizeof(resp_topic),
+ "/baresip/command_resp/%s",
+ oe_tok ? oe_tok->u.str : "nil");
+
+ err = mqtt_publish_message(mqtt, resp_topic,
+ "%b",
+ resp->buf, resp->end);
+ if (err) {
+ warning("mqtt: failed to publish message (%m)\n", err);
+ goto out;
+ }
+
+ out:
+ mem_deref(resp);
+ mem_deref(od);
+}
+
+
+/*
+ * This is called when a message is received from the broker.
+ */
+static void message_callback(struct mosquitto *mosq, void *obj,
+ const struct mosquitto_message *message)
+{
+ struct mqtt *mqtt = obj;
+ struct pl msg;
+ bool match = false;
+
+ info("mqtt: got message '%b' for topic '%s'\n",
+ (char*) message->payload, (size_t)message->payloadlen,
+ message->topic);
+
+ msg.p = message->payload;
+ msg.l = message->payloadlen;
+
+ mosquitto_topic_matches_sub("/baresip/command", message->topic,
+ &match);
+ if (match) {
+ info("mqtt: got message for '%s' topic\n", message->topic);
+
+ handle_command(mqtt, &msg);
+ }
+}
+
+
+int mqtt_subscribe_init(struct mqtt *mqtt)
+{
+ if (!mqtt)
+ return EINVAL;
+
+ mosquitto_message_callback_set(mqtt->mosq, message_callback);
+
+ return 0;
+}
+
+
+int mqtt_subscribe_start(struct mqtt *mqtt)
+{
+ int ret;
+
+ ret = mosquitto_subscribe(mqtt->mosq, NULL, subscription_pattern, 0);
+ if (ret != MOSQ_ERR_SUCCESS) {
+ warning("mqtt: failed to subscribe (%s)\n",
+ mosquitto_strerror(ret));
+ return EPROTO;
+ }
+
+ info("mqtt: subscribed to pattern '%s'\n", subscription_pattern);
+
+ return 0;
+}
+
+
+void mqtt_subscribe_close(void)
+{
+}
diff --git a/modules/omx/module.c b/modules/omx/module.c
index e1d136e..a5b6fa8 100644
--- a/modules/omx/module.c
+++ b/modules/omx/module.c
@@ -10,8 +10,8 @@
#include <stdlib.h>
-#include <re/re.h>
-#include <rem/rem.h>
+#include <re.h>
+#include <rem.h>
#include <baresip.h>
int omx_vidisp_alloc(struct vidisp_st **vp, const struct vidisp* vd,
diff --git a/modules/omx/omx.c b/modules/omx/omx.c
index 7cc4a45..6b08d19 100644
--- a/modules/omx/omx.c
+++ b/modules/omx/omx.c
@@ -7,8 +7,8 @@
#include "omx.h"
-#include <re/re.h>
-#include <rem/rem.h>
+#include <re.h>
+#include <rem.h>
#include <baresip.h>
#include <stdio.h>
diff --git a/modules/opensles/player.c b/modules/opensles/player.c
index 347240f..59964f0 100644
--- a/modules/opensles/player.c
+++ b/modules/opensles/player.c
@@ -4,6 +4,7 @@
* Copyright (C) 2010 Creytiv.com
*/
#include <re.h>
+#include <rem.h>
#include <baresip.h>
#include <SLES/OpenSLES.h>
#include "SLES/OpenSLES_Android.h"
@@ -153,6 +154,12 @@ int opensles_player_alloc(struct auplay_st **stp, const struct auplay *ap,
if (!stp || !ap || !prm || !wh)
return EINVAL;
+ if (prm->fmt != AUFMT_S16LE) {
+ warning("opensles: player: unsupported sample format (%s)\n",
+ aufmt_name(prm->fmt));
+ return ENOTSUP;
+ }
+
debug("opensles: opening player %uHz, %uchannels\n",
prm->srate, prm->ch);
diff --git a/modules/opensles/recorder.c b/modules/opensles/recorder.c
index 0a4c5ef..b26190b 100644
--- a/modules/opensles/recorder.c
+++ b/modules/opensles/recorder.c
@@ -4,6 +4,7 @@
* Copyright (C) 2010 Creytiv.com
*/
#include <re.h>
+#include <rem.h>
#include <baresip.h>
#include <string.h>
#include <SLES/OpenSLES.h>
@@ -165,6 +166,12 @@ int opensles_recorder_alloc(struct ausrc_st **stp, const struct ausrc *as,
if (!stp || !as || !prm || !rh)
return EINVAL;
+ if (prm->fmt != AUFMT_S16LE) {
+ warning("opensles: record: unsupported sample format (%s)\n",
+ aufmt_name(prm->fmt));
+ return ENOTSUP;
+ }
+
debug("opensles: opening recorder %uHz, %uchannels\n",
prm->srate, prm->ch);
diff --git a/modules/opus/opus.c b/modules/opus/opus.c
index 881596e..7acccec 100644
--- a/modules/opus/opus.c
+++ b/modules/opus/opus.c
@@ -36,7 +36,7 @@
static bool opus_mirror;
-static char fmtp[256] = "stereo=1;sprop-stereo=1";
+static char fmtp[256] = "";
static char fmtp_mirror[256];
@@ -88,9 +88,20 @@ static int module_init(void)
struct conf *conf = conf_cur();
uint32_t value;
char *p = fmtp + str_len(fmtp);
- bool b;
+ bool b, stereo = true, sprop_stereo = true;
int n = 0;
+ conf_get_bool(conf, "opus_stereo", &stereo);
+ conf_get_bool(conf, "opus_sprop_stereo", &sprop_stereo);
+
+ /* always set stereo parameter first */
+ n = re_snprintf(p, sizeof(fmtp) - str_len(p),
+ "stereo=%d;sprop-stereo=%d", stereo, sprop_stereo);
+ if (n <= 0)
+ return ENOMEM;
+
+ p += n;
+
if (0 == conf_get_u32(conf, "opus_bitrate", &value)) {
n = re_snprintf(p, sizeof(fmtp) - str_len(p),
diff --git a/modules/oss/oss.c b/modules/oss/oss.c
index 4a8af92..0d47dd1 100644
--- a/modules/oss/oss.c
+++ b/modules/oss/oss.c
@@ -226,7 +226,7 @@ static int src_alloc(struct ausrc_st **stp, const struct ausrc *as,
(void)ctx;
(void)errh;
- if (!stp || !as || !prm || !rh)
+ if (!stp || !as || !prm || prm->fmt != AUFMT_S16LE || !rh)
return EINVAL;
st = mem_zalloc(sizeof(*st), ausrc_destructor);
@@ -285,7 +285,7 @@ static int play_alloc(struct auplay_st **stp, const struct auplay *ap,
struct auplay_st *st;
int err;
- if (!stp || !ap || !prm || !wh)
+ if (!stp || !ap || !prm || prm->fmt != AUFMT_S16LE || !wh)
return EINVAL;
st = mem_zalloc(sizeof(*st), auplay_destructor);
diff --git a/modules/portaudio/portaudio.c b/modules/portaudio/portaudio.c
index cd4b973..b856502 100644
--- a/modules/portaudio/portaudio.c
+++ b/modules/portaudio/portaudio.c
@@ -99,6 +99,17 @@ static int write_callback(const void *inputBuffer, void *outputBuffer,
}
+static PaSampleFormat aufmt_to_pasampleformat(enum aufmt fmt)
+{
+ switch (fmt) {
+
+ case AUFMT_S16LE: return paInt16;
+ case AUFMT_FLOAT: return paFloat32;
+ default: return 0;
+ }
+}
+
+
static int read_stream_open(struct ausrc_st *st, const struct ausrc_prm *prm,
uint32_t dev)
{
@@ -109,7 +120,7 @@ static int read_stream_open(struct ausrc_st *st, const struct ausrc_prm *prm,
memset(&prm_in, 0, sizeof(prm_in));
prm_in.device = dev;
prm_in.channelCount = prm->ch;
- prm_in.sampleFormat = paInt16;
+ prm_in.sampleFormat = aufmt_to_pasampleformat(prm->fmt);
prm_in.suggestedLatency = 0.100;
st->stream_rd = NULL;
@@ -142,7 +153,7 @@ static int write_stream_open(struct auplay_st *st,
memset(&prm_out, 0, sizeof(prm_out));
prm_out.device = dev;
prm_out.channelCount = prm->ch;
- prm_out.sampleFormat = paInt16;
+ prm_out.sampleFormat = aufmt_to_pasampleformat(prm->fmt);
prm_out.suggestedLatency = 0.100;
st->stream_wr = NULL;
diff --git a/modules/pulse/player.c b/modules/pulse/player.c
index 480ba6a..d65e7a8 100644
--- a/modules/pulse/player.c
+++ b/modules/pulse/player.c
@@ -18,8 +18,9 @@ struct auplay_st {
pa_simple *s;
pthread_t thread;
bool run;
- int16_t *sampv;
+ void *sampv;
size_t sampc;
+ size_t sampsz;
auplay_write_h *wh;
void *arg;
};
@@ -46,7 +47,7 @@ static void auplay_destructor(void *arg)
static void *write_thread(void *arg)
{
struct auplay_st *st = arg;
- const size_t num_bytes = st->sampc * 2;
+ const size_t num_bytes = st->sampc * st->sampsz;
int ret, pa_error = 0;
while (st->run) {
@@ -64,6 +65,17 @@ static void *write_thread(void *arg)
}
+static int aufmt_to_pulse_format(enum aufmt fmt)
+{
+ switch (fmt) {
+
+ case AUFMT_S16LE: return PA_SAMPLE_S16NE;
+ case AUFMT_FLOAT: return PA_SAMPLE_FLOAT32NE;
+ default: return 0;
+ }
+}
+
+
int pulse_player_alloc(struct auplay_st **stp, const struct auplay *ap,
struct auplay_prm *prm, const char *device,
auplay_write_h *wh, void *arg)
@@ -88,14 +100,15 @@ int pulse_player_alloc(struct auplay_st **stp, const struct auplay *ap,
st->arg = arg;
st->sampc = prm->srate * prm->ch * prm->ptime / 1000;
+ st->sampsz = aufmt_sample_size(prm->fmt);
- st->sampv = mem_alloc(2 * st->sampc, NULL);
+ st->sampv = mem_alloc(st->sampsz * st->sampc, NULL);
if (!st->sampv) {
err = ENOMEM;
goto out;
}
- ss.format = PA_SAMPLE_S16NE;
+ ss.format = aufmt_to_pulse_format(prm->fmt);
ss.channels = prm->ch;
ss.rate = prm->srate;
diff --git a/modules/pulse/recorder.c b/modules/pulse/recorder.c
index 64df6c4..ca6fb6a 100644
--- a/modules/pulse/recorder.c
+++ b/modules/pulse/recorder.c
@@ -18,8 +18,10 @@ struct ausrc_st {
pa_simple *s;
pthread_t thread;
bool run;
- int16_t *sampv;
+ void *sampv;
size_t sampc;
+ size_t sampsz;
+ uint32_t ptime;
ausrc_read_h *rh;
void *arg;
};
@@ -46,8 +48,18 @@ static void ausrc_destructor(void *arg)
static void *read_thread(void *arg)
{
struct ausrc_st *st = arg;
- const size_t num_bytes = st->sampc * 2;
+ const size_t num_bytes = st->sampc * st->sampsz;
int ret, pa_error = 0;
+ uint64_t now, last_read, diff;
+ unsigned dropped = 0;
+ bool init = true;
+
+ if (pa_simple_flush(st->s, &pa_error)) {
+ warning("pulse: pa_simple_flush error (%s)\n",
+ pa_strerror(pa_error));
+ }
+
+ last_read = tmr_jiffies();
while (st->run) {
@@ -58,6 +70,27 @@ static void *read_thread(void *arg)
continue;
}
+ /* Some devices might send a burst of samples right after the
+ initialization - filter them out */
+ if (init) {
+ now = tmr_jiffies();
+ diff = (now > last_read)? now - last_read : 0;
+
+ if (diff < st->ptime / 2) {
+ last_read = now;
+ ++dropped;
+ continue;
+ }
+ else {
+ init = false;
+
+ if (dropped)
+ debug("pulse: dropped %u frames of "
+ "garbage at the beginning of "
+ "the recording\n", dropped);
+ }
+ }
+
st->rh(st->sampv, st->sampc, st->arg);
}
@@ -65,6 +98,17 @@ static void *read_thread(void *arg)
}
+static int aufmt_to_pulse_format(enum aufmt fmt)
+{
+ switch (fmt) {
+
+ case AUFMT_S16LE: return PA_SAMPLE_S16NE;
+ case AUFMT_FLOAT: return PA_SAMPLE_FLOAT32NE;
+ default: return 0;
+ }
+}
+
+
int pulse_recorder_alloc(struct ausrc_st **stp, const struct ausrc *as,
struct media_ctx **ctx,
struct ausrc_prm *prm, const char *device,
@@ -95,14 +139,16 @@ int pulse_recorder_alloc(struct ausrc_st **stp, const struct ausrc *as,
st->arg = arg;
st->sampc = prm->srate * prm->ch * prm->ptime / 1000;
+ st->sampsz = aufmt_sample_size(prm->fmt);
+ st->ptime = prm->ptime;
- st->sampv = mem_alloc(2 * st->sampc, NULL);
+ st->sampv = mem_alloc(st->sampsz * st->sampc, NULL);
if (!st->sampv) {
err = ENOMEM;
goto out;
}
- ss.format = PA_SAMPLE_S16NE;
+ ss.format = aufmt_to_pulse_format(prm->fmt);
ss.channels = prm->ch;
ss.rate = prm->srate;
diff --git a/modules/quicktime/module.mk b/modules/quicktime/module.mk
deleted file mode 100644
index af67862..0000000
--- a/modules/quicktime/module.mk
+++ /dev/null
@@ -1,11 +0,0 @@
-#
-# module.mk
-#
-# Copyright (C) 2010 Creytiv.com
-#
-
-MOD := quicktime
-$(MOD)_SRCS += quicktime.c
-$(MOD)_LFLAGS += -framework QuickTime -lswscale
-
-include mk/mod.mk
diff --git a/modules/quicktime/quicktime.c b/modules/quicktime/quicktime.c
deleted file mode 100644
index 3811add..0000000
--- a/modules/quicktime/quicktime.c
+++ /dev/null
@@ -1,323 +0,0 @@
-/**
- * @file quicktime.c Quicktime video-source
- *
- * Copyright (C) 2010 Creytiv.com
- */
-#include <pthread.h>
-#include <re.h>
-#include <QuickTime/QuickTimeComponents.h>
-#include <libavformat/avformat.h>
-#include <libswscale/swscale.h>
-#include <baresip.h>
-
-
-/* this module is deprecated, in favour of qtcapture or avcapture */
-
-
-struct vidsrc_st {
- const struct vidsrc *vs; /* inheritance */
- pthread_t thread;
- pthread_mutex_t mutex;
- struct vidsz sz;
- SeqGrabComponent seq_grab;
- SGDataUPP upp;
- SGChannel ch;
- struct mbuf *buf;
- struct SwsContext *sws;
- vidsrc_frame_h *frameh;
- void *arg;
- bool run;
-};
-
-
-static struct vidsrc *vidsrc;
-
-
-static void destructor(void *arg)
-{
- struct vidsrc_st *st = arg;
-
- if (st->seq_grab) {
-
- pthread_mutex_lock(&st->mutex);
- SGStop(st->seq_grab);
- pthread_mutex_unlock(&st->mutex);
-
- if (st->run) {
- pthread_join(st->thread, NULL);
- }
-
- if (st->upp) {
- DisposeSGDataUPP(st->upp);
- }
-
- if (st->ch) {
- SGDisposeChannel(st->seq_grab, st->ch);
- }
-
- CloseComponent(st->seq_grab);
- }
-
- if (st->sws)
- sws_freeContext(st->sws);
-
- mem_deref(st->buf);
-}
-
-
-static OSErr frame_handler(SGChannel c, Ptr p, long len, long *offset,
- long chRefCon, TimeValue timeval, short writeType,
- long refCon)
-{
- struct vidsrc_st *st = (struct vidsrc_st *)refCon;
- ImageDescriptionHandle imageDesc;
- AVPicture pict_src, pict_dst;
- struct vidframe vidframe;
- ComponentResult result;
- int i, ret;
- int new_len;
- (void)c;
- (void)p;
- (void)len;
- (void)offset;
- (void)chRefCon;
- (void)timeval;
- (void)writeType;
- (void)refCon;
-
- if (!st->buf) {
-
- imageDesc = (ImageDescriptionHandle)NewHandle(0);
- if (!imageDesc)
- return noErr;
-
- result = SGGetChannelSampleDescription(c,(Handle)imageDesc);
- if (result != noErr) {
- warning("quicktime: GetChanSampDesc: %d\n", result);
- DisposeHandle((Handle)imageDesc);
- return noErr;
- }
-
- st->sz.w = (*imageDesc)->width;
- st->sz.h = (*imageDesc)->height;
-
- /* buffer size after scaling */
- new_len = avpicture_get_size(PIX_FMT_YUV420P,
- st->sz.w, st->sz.h);
-
-#if 1
- re_fprintf(stderr, "got frame len=%u (%ux%u) [%s] depth=%u\n",
- len, st->sz.w, st->sz.h,
- (*imageDesc)->name, (*imageDesc)->depth);
-#endif
-
- DisposeHandle((Handle)imageDesc);
-
- st->buf = mbuf_alloc(new_len);
- if (!st->buf)
- return noErr;
- }
-
- if (!st->sws) {
- st->sws = sws_getContext(st->sz.w, st->sz.h, PIX_FMT_YUYV422,
- st->sz.w, st->sz.h, PIX_FMT_YUV420P,
- SWS_BICUBIC, NULL, NULL, NULL);
- if (!st->sws)
- return noErr;
- }
-
- avpicture_fill(&pict_src, (uint8_t *)p, PIX_FMT_YUYV422,
- st->sz.w, st->sz.h);
-
- avpicture_fill(&pict_dst, st->buf->buf, PIX_FMT_YUV420P,
- st->sz.w, st->sz.h);
-
- ret = sws_scale(st->sws,
- pict_src.data, pict_src.linesize, 0, st->sz.h,
- pict_dst.data, pict_dst.linesize);
-
- if (ret <= 0) {
- re_fprintf(stderr, "scale: sws_scale: returned %d\n", ret);
- return noErr;
- }
-
- for (i=0; i<4; i++) {
- vidframe.data[i] = pict_dst.data[i];
- vidframe.linesize[i] = pict_dst.linesize[i];
- }
-
- vidframe.size = st->sz;
- vidframe.valid = true;
-
- st->frameh(&vidframe, st->arg);
-
- return noErr;
-}
-
-
-static void *read_thread(void *arg)
-{
- struct vidsrc_st *st = arg;
- ComponentResult result;
-
- for (;;) {
- pthread_mutex_lock(&st->mutex);
- result = SGIdle(st->seq_grab);
- pthread_mutex_unlock(&st->mutex);
-
- if (result != noErr)
- break;
-
- usleep(10000);
- }
-
- return NULL;
-}
-
-
-static int alloc(struct vidsrc_st **stp, const struct vidsrc *vs,
- struct media_ctx **ctx,
- struct vidsrc_prm *prm, const char *fmt,
- const char *dev, vidsrc_frame_h *frameh,
- vidsrc_error_h *errorh, void *arg)
-{
- ComponentResult result;
- Rect rect;
- struct vidsrc_st *st;
- int err;
-
- (void)ctx;
- (void)fmt;
- (void)dev;
- (void)errorh;
-
- st = mem_zalloc(sizeof(*st), destructor);
- if (!st)
- return ENOMEM;
-
- st->vs = vs;
- st->frameh = frameh;
- st->arg = arg;
-
- err = pthread_mutex_init(&st->mutex, NULL);
- if (err) {
- re_fprintf(stderr, "mutex error: %s\n", strerror(err));
- goto out;
- }
-
- st->seq_grab = OpenDefaultComponent(SeqGrabComponentType, 0);
- if (!st->seq_grab) {
- re_fprintf(stderr, "Unable to open component\n");
- err = ENODEV;
- goto out;
- }
-
- result = SGInitialize(st->seq_grab);
- if (result != noErr) {
- re_fprintf(stderr, "Unable to initialize sequence grabber\n");
- err = ENODEV;
- goto out;
- }
-
- result = SGSetGWorld(st->seq_grab, NULL, NULL);
- if (result != noErr) {
- re_fprintf(stderr, "Unable to set gworld\n");
- err = ENODEV;
- goto out;
- }
-
- result = SGSetDataRef(st->seq_grab, 0, 0, seqGrabDontMakeMovie);
- if (result != noErr) {
- re_fprintf(stderr, "Unable to set data ref\n");
- err = ENODEV;
- goto out;
- }
-
- result = SGNewChannel(st->seq_grab, VideoMediaType, &st->ch);
- if (result != noErr) {
- re_fprintf(stderr, "Unable to allocate channel (result=%d)\n",
- result);
- err = ENOMEM;
- goto out;
- }
-
- /* XXX: check flags */
- result = SGSetChannelUsage(st->ch,
- seqGrabRecord |
- seqGrabLowLatencyCapture);
- if (result != noErr) {
- re_fprintf(stderr, "Unable to set channel usage\n");
- err = ENODEV;
- goto out;
- }
-
- rect.top = 0;
- rect.left = 0;
- rect.bottom = prm->size.h;
- rect.right = prm->size.w;
-
- result = SGSetChannelBounds(st->ch, &rect);
- if (result != noErr) {
- re_fprintf(stderr, "Unable to set channel bounds\n");
- err = ENODEV;
- goto out;
- }
-
- st->upp = NewSGDataUPP(frame_handler);
- if (!st->upp) {
- re_fprintf(stderr, "Unable to allocate data upp\n");
- err = ENOMEM;
- goto out;
- }
-
- result = SGSetDataProc(st->seq_grab, st->upp, (long)st);
- if (result != noErr) {
- re_fprintf(stderr, "Unable to sg callback\n");
- err = ENODEV;
- goto out;
- }
-
- result = SGStartRecord(st->seq_grab);
- if (result != noErr) {
- re_fprintf(stderr, "Unable to start record: %d\n", result);
- err = ENODEV;
- goto out;
- }
-
- err = pthread_create(&st->thread, NULL, read_thread, st);
- if (err) {
- re_fprintf(stderr, "thread error: %s\n", strerror(err));
- goto out;
- }
-
- st->run = true;
-
- out:
- if (err)
- mem_deref(st);
- else
- *stp = st;
- return err;
-}
-
-
-static int qt_init(void)
-{
- return vidsrc_register(&vidsrc, baresip_vidsrcl(),
- "quicktime", alloc, NULL);
-}
-
-
-static int qt_close(void)
-{
- vidsrc = mem_deref(vidsrc);
- return 0;
-}
-
-
-EXPORT_SYM const struct mod_export DECL_EXPORTS(quicktime) = {
- "quicktime",
- "videosrc",
- qt_init,
- qt_close
-};
diff --git a/modules/rst/audio.c b/modules/rst/audio.c
index 666b09a..4bb7e0d 100644
--- a/modules/rst/audio.c
+++ b/modules/rst/audio.c
@@ -28,6 +28,7 @@ struct ausrc_st {
bool run;
uint32_t ptime;
size_t sampc;
+ size_t sampsz;
};
@@ -59,9 +60,10 @@ static void *play_thread(void *arg)
{
uint64_t now, ts = tmr_jiffies();
struct ausrc_st *st = arg;
- int16_t *sampv;
+ void *sampv;
+ size_t num_bytes = st->sampc * st->sampsz;
- sampv = mem_alloc(st->sampc * 2, NULL);
+ sampv = mem_alloc(num_bytes, NULL);
if (!sampv)
return NULL;
@@ -80,7 +82,7 @@ static void *play_thread(void *arg)
}
#endif
- aubuf_read_samp(st->aubuf, sampv, st->sampc);
+ aubuf_read(st->aubuf, sampv, num_bytes);
st->rh(sampv, st->sampc, st->arg);
@@ -148,6 +150,18 @@ void rst_audio_feed(struct ausrc_st *st, const uint8_t *buf, size_t sz)
}
+static int aufmt_to_encoding(enum aufmt fmt)
+{
+ switch (fmt) {
+
+ case AUFMT_S16LE: return MPG123_ENC_SIGNED_16;
+ case AUFMT_FLOAT: return MPG123_ENC_FLOAT_32;
+ case AUFMT_S24_3LE: return MPG123_ENC_SIGNED_24; /* NOTE: endian */
+ default: return 0;
+ }
+}
+
+
static int alloc_handler(struct ausrc_st **stp, const struct ausrc *as,
struct media_ctx **ctx,
struct ausrc_prm *prm, const char *dev,
@@ -184,10 +198,12 @@ static int alloc_handler(struct ausrc_st **stp, const struct ausrc *as,
/* Set wanted output format */
mpg123_format_none(st->mp3);
- mpg123_format(st->mp3, prm->srate, prm->ch, MPG123_ENC_SIGNED_16);
+ mpg123_format(st->mp3, prm->srate, prm->ch,
+ aufmt_to_encoding(prm->fmt));
mpg123_volume(st->mp3, 0.3);
st->sampc = prm->srate * prm->ch * prm->ptime / 1000;
+ st->sampsz = aufmt_sample_size(prm->fmt);
st->ptime = prm->ptime;
@@ -198,8 +214,8 @@ static int alloc_handler(struct ausrc_st **stp, const struct ausrc *as,
/* 1 - 20 seconds of audio */
err = aubuf_alloc(&st->aubuf,
- prm->srate * prm->ch * 2,
- prm->srate * prm->ch * 40);
+ prm->srate * prm->ch * st->sampsz,
+ prm->srate * prm->ch * st->sampsz * 20);
if (err)
goto out;
diff --git a/modules/sndio/sndio.c b/modules/sndio/sndio.c
index d3ca9d6..6ac4b67 100644
--- a/modules/sndio/sndio.c
+++ b/modules/sndio/sndio.c
@@ -154,6 +154,12 @@ static int src_alloc(struct ausrc_st **stp, const struct ausrc *as,
if (!stp || !as || !prm)
return EINVAL;
+ if (prm->fmt != AUFMT_S16LE) {
+ warning("sndio: source: unsupported sample format (%s)\n",
+ aufmt_name(prm->fmt));
+ return ENOTSUP;
+ }
+
name = (str_isset(device)) ? device : SIO_DEVANY;
if ((st = mem_zalloc(sizeof(*st), ausrc_destructor)) == NULL)
@@ -222,6 +228,12 @@ static int play_alloc(struct auplay_st **stp, const struct auplay *ap,
if (!stp || !ap || !prm)
return EINVAL;
+ if (prm->fmt != AUFMT_S16LE) {
+ warning("sndio: playback: unsupported sample format (%s)\n",
+ aufmt_name(prm->fmt));
+ return ENOTSUP;
+ }
+
name = (str_isset(device)) ? device : SIO_DEVANY;
if ((st = mem_zalloc(sizeof(*st), auplay_destructor)) == NULL)
diff --git a/modules/v4l2/module.mk b/modules/v4l2/module.mk
index 6fc9178..8fefae4 100644
--- a/modules/v4l2/module.mk
+++ b/modules/v4l2/module.mk
@@ -6,7 +6,7 @@
MOD := v4l2
$(MOD)_SRCS += v4l2.c
-ifeq ($(HAVE_LIBV4L2),yes)
+ifneq ($(HAVE_LIBV4L2),)
$(MOD)_LFLAGS += -lv4l2
$(MOD)_CFLAGS += -DHAVE_LIBV4L2
endif
diff --git a/modules/v4l2/v4l2.c b/modules/v4l2/v4l2.c
index e079fed..983ad53 100644
--- a/modules/v4l2/v4l2.c
+++ b/modules/v4l2/v4l2.c
@@ -84,7 +84,7 @@ static enum vidfmt match_fmt(u_int32_t fmt)
}
-static void print_video_input(struct vidsrc_st *st)
+static void print_video_input(const struct vidsrc_st *st)
{
struct v4l2_input input;
@@ -398,6 +398,8 @@ static void destructor(void *arg)
{
struct vidsrc_st *st = arg;
+ debug("v4l2: stopping video source..\n");
+
if (st->run) {
st->run = false;
pthread_join(st->thread, NULL);
diff --git a/modules/vidloop/vidloop.c b/modules/vidloop/vidloop.c
index 52f4359..d99fa49 100644
--- a/modules/vidloop/vidloop.c
+++ b/modules/vidloop/vidloop.c
@@ -285,7 +285,7 @@ static int enable_codec(struct video_loop *vl, const char *name)
static void print_status(struct video_loop *vl)
{
- (void)re_fprintf(stderr,
+ (void)re_fprintf(stdout,
"\rstatus:"
" [%s] [%s] intra=%zu "
" EFPS=%.1f %u kbit/s \r",
@@ -293,6 +293,7 @@ static void print_status(struct video_loop *vl)
vl->vc_dec ? vl->vc_dec->name : "",
vl->stat.n_intra,
vl->stat.efps, vl->stat.bitrate);
+ fflush(stdout);
}
diff --git a/modules/winwave/play.c b/modules/winwave/play.c
index b9129fa..e987fc7 100644
--- a/modules/winwave/play.c
+++ b/modules/winwave/play.c
@@ -202,6 +202,12 @@ int winwave_play_alloc(struct auplay_st **stp, const struct auplay *ap,
if (!stp || !ap || !prm)
return EINVAL;
+ if (prm->fmt != AUFMT_S16LE) {
+ warning("winwave: playback: unsupported sample format (%s)\n",
+ aufmt_name(prm->fmt));
+ return ENOTSUP;
+ }
+
st = mem_zalloc(sizeof(*st), auplay_destructor);
if (!st)
return ENOMEM;
diff --git a/modules/winwave/src.c b/modules/winwave/src.c
index e96915e..6240899 100644
--- a/modules/winwave/src.c
+++ b/modules/winwave/src.c
@@ -201,6 +201,12 @@ int winwave_src_alloc(struct ausrc_st **stp, const struct ausrc *as,
if (!stp || !as || !prm)
return EINVAL;
+ if (prm->fmt != AUFMT_S16LE) {
+ warning("winwave: source: unsupported sample format (%s)\n",
+ aufmt_name(prm->fmt));
+ return ENOTSUP;
+ }
+
st = mem_zalloc(sizeof(*st), ausrc_destructor);
if (!st)
return ENOMEM;
diff --git a/modules/zrtp/zrtp.c b/modules/zrtp/zrtp.c
index 115db1c..28ae48f 100644
--- a/modules/zrtp/zrtp.c
+++ b/modules/zrtp/zrtp.c
@@ -26,6 +26,13 @@
* Thanks:
*
* Ingo Feinerer
+ *
+ * Configuration options:
+ *
+ \verbatim
+ zrtp_hash {yes,no} # Enable SDP zrtp-hash (recommended)
+ \endverbatim
+ *
*/
@@ -35,10 +42,14 @@ enum {
struct menc_sess {
zrtp_session_t *zrtp_session;
+ menc_error_h *errorh;
+ void *arg;
+ struct tmr abort_timer;
+ int err;
};
struct menc_media {
- const struct menc_sess *sess;
+ struct menc_sess *sess;
struct udp_helper *uh_rtp;
struct udp_helper *uh_rtcp;
struct sa raddr;
@@ -53,6 +64,10 @@ static zrtp_config_t zrtp_config;
static zrtp_zid_t zid;
+/* RFC 6189, section 8.1. */
+static bool use_sig_hash = true;
+
+
enum pkt_type {
PKT_TYPE_UNKNOWN = 0,
PKT_TYPE_RTP = 1,
@@ -93,6 +108,8 @@ static void session_destructor(void *arg)
{
struct menc_sess *st = arg;
+ tmr_cancel(&st->abort_timer);
+
if (st->zrtp_session)
zrtp_session_down(st->zrtp_session);
}
@@ -112,6 +129,32 @@ static void media_destructor(void *arg)
}
+static void abort_timer_h(void *arg)
+{
+ struct menc_sess *sess = arg;
+
+ if (sess->errorh) {
+ sess->errorh(sess->err, sess->arg);
+ sess->errorh = NULL;
+ }
+}
+
+
+static void abort_call(struct menc_sess *sess)
+{
+ if (!sess->err) {
+ sess->err = EPIPE;
+ tmr_start(&sess->abort_timer, 0, abort_timer_h, sess);
+ }
+}
+
+
+static bool drop_packets(const struct menc_media *st)
+{
+ return (st)? st->sess->err != 0 : true;
+}
+
+
static bool udp_helper_send(int *err, struct sa *dst,
struct mbuf *mb, void *arg)
{
@@ -121,6 +164,9 @@ static bool udp_helper_send(int *err, struct sa *dst,
const char *proto_name = "rtp";
enum pkt_type ptype = get_packet_type(mb);
+ if (drop_packets(st))
+ return true;
+
length = (unsigned int)mbuf_get_left(mb);
/* only RTP/RTCP packets should be processed */
@@ -168,6 +214,9 @@ static bool udp_helper_recv(struct sa *src, struct mbuf *mb, void *arg)
const char *proto_name = "srtp";
enum pkt_type ptype = get_packet_type(mb);
+ if (drop_packets(st))
+ return true;
+
length = (unsigned int)mbuf_get_left(mb);
if (ptype == PKT_TYPE_RTCP) {
@@ -198,6 +247,63 @@ static bool udp_helper_recv(struct sa *src, struct mbuf *mb, void *arg)
}
+static int sig_hash_encode(struct zrtp_stream_t *stream,
+ struct sdp_media *m)
+{
+ char buf[ZRTP_SIGN_ZRTP_HASH_LENGTH + 1];
+ zrtp_status_t s;
+ int err = 0;
+
+ s = zrtp_signaling_hash_get(stream, buf, sizeof(buf));
+ if (s != zrtp_status_ok) {
+ warning("zrtp: zrtp_signaling_hash_get: status = %d\n", s);
+ return EINVAL;
+ }
+
+ err = sdp_media_set_lattr(m, true, "zrtp-hash", "%s %s",
+ ZRTP_PROTOCOL_VERSION, buf);
+ if (err) {
+ warning("zrtp: sdp_media_set_lattr: %d\n", err);
+ }
+
+ return err;
+}
+
+
+static void sig_hash_decode(struct zrtp_stream_t *stream,
+ const struct sdp_media *m)
+{
+ const char *attr_val;
+ struct pl major, minor, hash;
+ uint32_t version;
+ int err;
+ zrtp_status_t s;
+
+ attr_val = sdp_media_rattr(m, "zrtp-hash");
+ if (!attr_val)
+ return;
+
+ err = re_regex(attr_val, strlen(attr_val),
+ "[0-9]+.[0-9]2 [0-9a-f]+",
+ &major, &minor, &hash);
+ if (err || hash.l < ZRTP_SIGN_ZRTP_HASH_LENGTH) {
+ warning("zrtp: malformed zrtp-hash attribute, ignoring...\n");
+ return;
+ }
+
+ version = pl_u32(&major) * 100 + pl_u32(&minor);
+ /* more version checks? */
+ if (version < 110) {
+ warning("zrtp: zrtp-hash: version (%d) is too low, "
+ "ignoring...", version);
+ }
+
+ s = zrtp_signaling_hash_set(stream, hash.p, (uint32_t)hash.l);
+ if (s != zrtp_status_ok)
+ warning("zrtp: zrtp_signaling_hash_set: status = %d\n", s);
+}
+
+
static int session_alloc(struct menc_sess **sessp, struct sdp_session *sdp,
bool offerer, menc_error_h *errorh, void *arg)
{
@@ -205,8 +311,6 @@ static int session_alloc(struct menc_sess **sessp, struct sdp_session *sdp,
zrtp_status_t s;
int err = 0;
(void)offerer;
- (void)errorh;
- (void)arg;
if (!sessp || !sdp)
return EINVAL;
@@ -215,6 +319,11 @@ static int session_alloc(struct menc_sess **sessp, struct sdp_session *sdp,
if (!st)
return ENOMEM;
+ st->errorh = errorh;
+ st->arg = arg;
+ st->err = 0;
+ tmr_init(&st->abort_timer);
+
s = zrtp_session_init(zrtp_global, NULL, zid,
ZRTP_SIGNALING_ROLE_UNKNOWN, &st->zrtp_session);
if (s != zrtp_status_ok) {
@@ -277,6 +386,12 @@ static int media_alloc(struct menc_media **stp, struct menc_sess *sess,
zrtp_stream_set_userdata(st->zrtp_stream, st);
+ if (use_sig_hash) {
+ err = sig_hash_encode(st->zrtp_stream, sdpm);
+ if (err)
+ goto out;
+ }
+
out:
if (err) {
mem_deref(st);
@@ -289,6 +404,9 @@ static int media_alloc(struct menc_media **stp, struct menc_sess *sess,
if (sa_isset(sdp_media_raddr(sdpm), SA_ALL)) {
st->raddr = *sdp_media_raddr(sdpm);
+ if (use_sig_hash)
+ sig_hash_decode(st->zrtp_stream, sdpm);
+
s = zrtp_stream_start(st->zrtp_stream, rtp_sess_ssrc(rtp));
if (s != zrtp_status_ok) {
warning("zrtp: zrtp_stream_start: status = %d\n", s);
@@ -307,6 +425,9 @@ static int on_send_packet(const zrtp_stream_t *stream,
struct mbuf *mb;
int err;
+ if (drop_packets(st))
+ return zrtp_status_ok;
+
if (!sa_isset(&st->raddr, SA_ALL))
return zrtp_status_ok;
@@ -355,6 +476,23 @@ static void on_zrtp_secure(zrtp_stream_t *stream)
}
+static void on_zrtp_security_event(zrtp_stream_t *stream,
+ zrtp_security_event_t event)
+{
+ if (event == ZRTP_EVENT_WRONG_SIGNALING_HASH) {
+ const struct menc_media *st = zrtp_stream_get_userdata(stream);
+
+ warning("zrtp: Attack detected!!! Signaling hash from the "
+ "zrtp-hash SDP attribute doesn't match the hash of "
+ "the Hello message. Aborting the call.\n");
+
+ /* As this was called from zrtp_process_xxx(), we need
+ a safe shutdown. */
+ abort_call(st->sess);
+ }
+}
+
+
static struct menc menc_zrtp = {
LE_INIT, "zrtp", "RTP/AVP", session_alloc, media_alloc
};
@@ -428,6 +566,8 @@ static int module_init(void)
FILE *f;
int ret, err;
+ (void)conf_get_bool(conf_cur(), "zrtp_hash", &use_sig_hash);
+
zrtp_log_set_log_engine(zrtp_log);
zrtp_config_defaults(&zrtp_config);
@@ -439,6 +579,8 @@ static int module_init(void)
zrtp_config.cb.misc_cb.on_send_packet = on_send_packet;
zrtp_config.cb.event_cb.on_zrtp_secure = on_zrtp_secure;
+ zrtp_config.cb.event_cb.on_zrtp_security_event =
+ on_zrtp_security_event;
err = conf_path_get(config_path, sizeof(config_path));
if (err) {
diff --git a/rpm/baresip.spec b/rpm/baresip.spec
index d614502..de6170f 100644
--- a/rpm/baresip.spec
+++ b/rpm/baresip.spec
@@ -1,5 +1,5 @@
%define name baresip
-%define ver 0.5.6
+%define ver 0.5.7
%define rel 1
Summary: Modular SIP useragent
diff --git a/src/account.c b/src/account.c
index 8decb2e..2a99e58 100644
--- a/src/account.c
+++ b/src/account.c
@@ -362,7 +362,7 @@ int account_alloc(struct account **accp, const char *sipaddr)
pl_set_str(&pl, acc->buf);
err = sip_addr_decode(&acc->laddr, &pl);
if (err) {
- warning("account: invalid SIP address: `%r'\n", &pl);
+ warning("account: error parsing SIP address: '%r'\n", &pl);
goto out;
}
diff --git a/src/audio.c b/src/audio.c
index 116ffaa..17eb5f6 100644
--- a/src/audio.c
+++ b/src/audio.c
@@ -80,6 +80,8 @@ struct autx {
const struct aucodec *ac; /**< Current audio encoder */
struct auenc_state *enc; /**< Audio encoder state (optional) */
struct aubuf *aubuf; /**< Packetize outgoing stream */
+ size_t aubuf_maxsz; /**< Maximum aubuf size in [bytes] */
+ volatile bool aubuf_started;
struct auresamp resamp; /**< Optional resampler for DSP */
struct list filtl; /**< Audio filters in encoding order */
struct mbuf *mb; /**< Buffer for outgoing RTP packets */
@@ -87,22 +89,29 @@ struct autx {
int16_t *sampv; /**< Sample buffer */
int16_t *sampv_rs; /**< Sample buffer for resampler */
uint32_t ptime; /**< Packet time for sending */
- uint32_t ts; /**< Timestamp for outgoing RTP */
+ uint64_t ts_ext; /**< Ext. Timestamp for outgoing RTP */
+ uint32_t ts_base;
uint32_t ts_tel; /**< Timestamp for Telephony Events */
size_t psize; /**< Packet size for sending */
bool marker; /**< Marker bit for outgoing RTP */
bool muted; /**< Audio source is muted */
int cur_key; /**< Currently transmitted event */
+ enum aufmt src_fmt;
+ bool need_conv;
+
+ struct {
+ uint64_t aubuf_overrun;
+ uint64_t aubuf_underrun;
+ } stats;
- union {
- struct tmr tmr; /**< Timer for sending RTP packets */
#ifdef HAVE_PTHREAD
+ union {
struct {
pthread_t tid;/**< Audio transmit thread */
bool run; /**< Audio transmit thread running */
} thr;
-#endif
} u;
+#endif
};
@@ -127,6 +136,8 @@ struct aurx {
const struct aucodec *ac; /**< Current audio decoder */
struct audec_state *dec; /**< Audio decoder state (optional) */
struct aubuf *aubuf; /**< Incoming audio buffer */
+ size_t aubuf_maxsz; /**< Maximum aubuf size in [bytes] */
+ volatile bool aubuf_started;
struct auresamp resamp; /**< Optional resampler for DSP */
struct list filtl; /**< Audio filters in decoding order */
char device[64]; /**< Audio player device name */
@@ -136,6 +147,15 @@ struct aurx {
int pt; /**< Payload type for incoming RTP */
double level_last;
bool level_set;
+ enum aufmt play_fmt;
+ bool need_conv;
+ struct timestamp_recv ts_recv;
+ uint64_t n_discard;
+
+ struct {
+ uint64_t aubuf_overrun;
+ uint64_t aubuf_underrun;
+ } stats;
};
@@ -160,6 +180,43 @@ struct audio {
static const char *uri_aulevel = "urn:ietf:params:rtp-hdrext:ssrc-audio-level";
+static double audio_calc_seconds(uint64_t rtp_ts, uint32_t clock_rate)
+{
+ double timestamp;
+
+ /* convert from RTP clockrate to seconds */
+ timestamp = (double)rtp_ts / (double)clock_rate;
+
+ return timestamp;
+}
+
+
+static double autx_calc_seconds(const struct autx *autx)
+{
+ uint64_t dur;
+
+ if (!autx->ac)
+ return .0;
+
+ dur = autx->ts_ext - autx->ts_base;
+
+ return audio_calc_seconds(dur, autx->ac->crate);
+}
+
+
+static double aurx_calc_seconds(const struct aurx *aurx)
+{
+ uint64_t dur;
+
+ if (!aurx->ac)
+ return .0;
+
+ dur = timestamp_duration(&aurx->ts_recv);
+
+ return audio_calc_seconds(dur, aurx->ac->crate);
+}
+
+
static void stop_tx(struct autx *tx, struct audio *a)
{
if (!tx || !a)
@@ -169,17 +226,12 @@ static void stop_tx(struct autx *tx, struct audio *a)
#ifdef HAVE_PTHREAD
case AUDIO_MODE_THREAD:
- case AUDIO_MODE_THREAD_REALTIME:
if (tx->u.thr.run) {
tx->u.thr.run = false;
pthread_join(tx->u.thr.tid, NULL);
}
break;
#endif
- case AUDIO_MODE_TMR:
- tmr_cancel(&tx->u.tmr);
- break;
-
default:
break;
}
@@ -246,6 +298,16 @@ static inline uint32_t calc_nsamp(uint32_t srate, uint8_t channels,
}
+static inline double calc_ptime(size_t nsamp, uint32_t srate, uint8_t channels)
+{
+ double ptime;
+
+ ptime = 1000.0 * (double)nsamp / (double)(srate * channels);
+
+ return ptime;
+}
+
+
/**
* Get the DSP samplerate for an audio-codec (exception for G.722 and MPA)
*/
@@ -389,7 +451,7 @@ static void encode_rtp_send(struct audio *a, struct autx *tx,
err = tx->ac->ench(tx->enc, mbuf_buf(tx->mb), &len, sampv, sampc);
if ((err & 0xffff0000) == 0x00010000) {
/* MPA needs some special treatment here */
- tx->ts = err & 0xffff;
+ tx->ts_ext = err & 0xffff;
err = 0;
}
else if (err) {
@@ -403,9 +465,11 @@ static void encode_rtp_send(struct audio *a, struct autx *tx,
if (mbuf_get_left(tx->mb)) {
+ uint32_t rtp_ts = tx->ts_ext & 0xffffffff;
+
if (len) {
err = stream_send(a->strm, ext_len!=0, tx->marker, -1,
- tx->ts, tx->mb);
+ rtp_ts, tx->mb);
if (err)
goto out;
}
@@ -421,7 +485,7 @@ static void encode_rtp_send(struct audio *a, struct autx *tx,
*/
frame_size = sampc_rtp / get_ch(tx->ac);
- tx->ts += (uint32_t)frame_size;
+ tx->ts_ext += (uint32_t)frame_size;
out:
tx->marker = false;
@@ -436,13 +500,46 @@ static void poll_aubuf_tx(struct audio *a)
struct autx *tx = &a->tx;
int16_t *sampv = tx->sampv;
size_t sampc;
+ size_t sz;
+ size_t num_bytes;
struct le *le;
int err = 0;
- sampc = tx->psize / 2;
+ sz = aufmt_sample_size(tx->src_fmt);
+ if (!sz)
+ return;
+
+ num_bytes = tx->psize;
+ sampc = tx->psize / sz;
/* timed read from audio-buffer */
- aubuf_read_samp(tx->aubuf, tx->sampv, sampc);
+
+ if (tx->src_fmt == AUFMT_S16LE) {
+
+ aubuf_read(tx->aubuf, (uint8_t *)tx->sampv, num_bytes);
+ }
+ else {
+ /* Convert from ausrc format to 16-bit format */
+
+ void *tmp_sampv;
+
+ if (!tx->need_conv) {
+ info("audio: NOTE: source sample conversion"
+ " needed: %s --> %s\n",
+ aufmt_name(tx->src_fmt), aufmt_name(AUFMT_S16LE));
+ tx->need_conv = true;
+ }
+
+ tmp_sampv = mem_zalloc(num_bytes, NULL);
+ if (!tmp_sampv)
+ return;
+
+ aubuf_read(tx->aubuf, tmp_sampv, num_bytes);
+
+ auconv_to_s16(sampv, tx->src_fmt, tmp_sampv, sampc);
+
+ mem_deref(tmp_sampv);
+ }
/* optional resampler */
if (tx->resamp.resample) {
@@ -487,7 +584,7 @@ static void check_telev(struct audio *a, struct autx *tx)
return;
if (marker)
- tx->ts_tel = tx->ts;
+ tx->ts_tel = (uint32_t)tx->ts_ext;
fmt = sdp_media_rformat(stream_sdpmedia(audio_strm(a)), telev_rtpfmt);
if (!fmt)
@@ -511,15 +608,26 @@ static void check_telev(struct audio *a, struct autx *tx)
*
* @note This function may be called from any thread
*
+ * @note The sample format is set in rx->play_fmt
+ *
* @param buf Buffer to fill with audio samples
* @param sz Number of bytes in buffer
* @param arg Handler argument
*/
-static void auplay_write_handler(int16_t *sampv, size_t sampc, void *arg)
+static void auplay_write_handler(void *sampv, size_t sampc, void *arg)
{
struct aurx *rx = arg;
+ size_t num_bytes = sampc * aufmt_sample_size(rx->play_fmt);
+
+ if (rx->aubuf_started && aubuf_cur_size(rx->aubuf) < num_bytes) {
+
+ ++rx->stats.aubuf_underrun;
- aubuf_read_samp(rx->aubuf, sampv, sampc);
+ debug("audio: rx aubuf underrun (total %llu)\n",
+ rx->stats.aubuf_underrun);
+ }
+
+ aubuf_read(rx->aubuf, sampv, num_bytes);
}
@@ -534,15 +642,26 @@ static void auplay_write_handler(int16_t *sampv, size_t sampc, void *arg)
* @param sz Number of bytes in buffer
* @param arg Handler argument
*/
-static void ausrc_read_handler(const int16_t *sampv, size_t sampc, void *arg)
+static void ausrc_read_handler(const void *sampv, size_t sampc, void *arg)
{
struct audio *a = arg;
struct autx *tx = &a->tx;
+ size_t num_bytes = sampc * aufmt_sample_size(tx->src_fmt);
if (tx->muted)
- memset((void *)sampv, 0, sampc*2);
+ memset((void *)sampv, 0, num_bytes);
+
+ if (aubuf_cur_size(tx->aubuf) >= tx->aubuf_maxsz) {
+
+ ++tx->stats.aubuf_overrun;
+
+ debug("audio: tx aubuf overrun (total %llu)\n",
+ tx->stats.aubuf_overrun);
+ }
- (void)aubuf_write_samp(tx->aubuf, sampv, sampc);
+ (void)aubuf_write(tx->aubuf, sampv, num_bytes);
+
+ tx->aubuf_started = true;
if (a->cfg.txmode == AUDIO_MODE_POLL) {
unsigned i;
@@ -662,9 +781,49 @@ static int aurx_stream_decode(struct aurx *rx, struct mbuf *mb)
sampc = sampc_rs;
}
- err = aubuf_write_samp(rx->aubuf, sampv, sampc);
- if (err)
- goto out;
+ if (aubuf_cur_size(rx->aubuf) >= rx->aubuf_maxsz) {
+
+ ++rx->stats.aubuf_overrun;
+
+ debug("audio: rx aubuf overrun (total %llu)\n",
+ rx->stats.aubuf_overrun);
+ }
+
+ if (rx->play_fmt == AUFMT_S16LE) {
+ err = aubuf_write_samp(rx->aubuf, sampv, sampc);
+ if (err)
+ goto out;
+ }
+ else {
+
+ /* Convert from 16-bit to auplay format */
+
+ void *tmp_sampv;
+ size_t num_bytes = sampc * aufmt_sample_size(rx->play_fmt);
+
+ if (!rx->need_conv) {
+ info("audio: NOTE: playback sample conversion"
+ " needed: %s --> %s\n",
+ aufmt_name(AUFMT_S16LE),
+ aufmt_name(rx->play_fmt));
+ rx->need_conv = true;
+ }
+
+ tmp_sampv = mem_zalloc(num_bytes, NULL);
+ if (!tmp_sampv)
+ return ENOMEM;
+
+ auconv_from_s16(rx->play_fmt, tmp_sampv, sampv, sampc);
+
+ err = aubuf_write(rx->aubuf, tmp_sampv, num_bytes);
+
+ mem_deref(tmp_sampv);
+
+ if (err)
+ goto out;
+ }
+
+ rx->aubuf_started = true;
out:
return err;
@@ -678,7 +837,9 @@ static void stream_recv_handler(const struct rtp_header *hdr,
{
struct audio *a = arg;
struct aurx *rx = &a->rx;
+ bool discard = false;
size_t i;
+ int wrap;
int err;
if (!mb)
@@ -723,6 +884,71 @@ static void stream_recv_handler(const struct rtp_header *hdr,
}
}
+ /* Save timestamp for incoming RTP packets */
+
+ if (rx->ts_recv.is_set) {
+
+ uint64_t ext_last, ext_now;
+
+ ext_last = calc_extended_timestamp(rx->ts_recv.num_wraps,
+ rx->ts_recv.last);
+
+ ext_now = calc_extended_timestamp(rx->ts_recv.num_wraps,
+ hdr->ts);
+
+ if (ext_now <= ext_last) {
+ uint64_t delta;
+
+ delta = ext_last - ext_now;
+
+ warning("audio: [time=%.3f]"
+ " discard old frame (%.3f seconds old)\n",
+ aurx_calc_seconds(rx),
+ audio_calc_seconds(delta, rx->ac->crate));
+
+ discard = true;
+ }
+ }
+ else {
+ rx->ts_recv.first = hdr->ts;
+ rx->ts_recv.last = hdr->ts;
+ rx->ts_recv.is_set = true;
+ }
+
+ wrap = timestamp_wrap(hdr->ts, rx->ts_recv.last);
+
+ switch (wrap) {
+
+ case -1:
+ warning("audio: rtp timestamp wraps backwards"
+ " (delta = %d) -- discard\n",
+ (int32_t)(rx->ts_recv.last - hdr->ts));
+ discard = true;
+ break;
+
+ case 0:
+ break;
+
+ case 1:
+ ++rx->ts_recv.num_wraps;
+ break;
+
+ default:
+ break;
+ }
+
+ rx->ts_recv.last = hdr->ts;
+
+#if 0
+ re_printf("[time=%.3f] wrap=%d discard=%d\n",
+ aurx_calc_seconds(rx), wrap, discard);
+#endif
+
+ if (discard) {
+ ++a->rx.n_discard;
+ return;
+ }
+
out:
(void)aurx_stream_decode(&a->rx, mb);
}
@@ -746,6 +972,60 @@ static int add_telev_codec(struct audio *a)
}
+/*
+ * EBU ACIP (Audio Contribution over IP) Profile
+ *
+ * Ref: https://tech.ebu.ch/docs/tech/tech3368.pdf
+ */
+static int set_ebuacip_params(struct audio *au, uint32_t ptime)
+{
+ struct sdp_media *sdp = stream_sdpmedia(au->strm);
+ const struct config_avt *avt = &au->strm->cfg;
+ char str[64];
+ int jbvalue = 0;
+ int jb_id = 0;
+ int err = 0;
+
+ /* set ebuacip version fixed value 0 for now. */
+ err |= sdp_media_set_lattr(sdp, false, "ebuacip", "version %i", 0);
+
+ /* set jb option, only one in our case */
+ err |= sdp_media_set_lattr(sdp, false, "ebuacip", "jb %i", jb_id);
+
+ /* define jb value in option */
+ if (0 == conf_get_str(conf_cur(), "ebuacip_jb_type",str,sizeof(str))) {
+
+ if (0 == str_cmp(str, "auto")) {
+
+ err |= sdp_media_set_lattr(sdp, false,
+ "ebuacip",
+ "jbdef %i auto %d-%d",
+ jb_id,
+ avt->jbuf_del.min * ptime,
+ avt->jbuf_del.max * ptime);
+ }
+ else if (0 == str_cmp(str, "fixed")) {
+
+ /* define jb value in option */
+ jbvalue = avt->jbuf_del.max * ptime;
+
+ err |= sdp_media_set_lattr(sdp, false,
+ "ebuacip",
+ "jbdef %i fixed %d",
+ jb_id, jbvalue);
+ }
+ }
+
+ /* set QOS recomendation use tos / 4 to set DSCP value */
+ err |= sdp_media_set_lattr(sdp, false, "ebuacip", "qosrec %u",
+ avt->rtp_tos / 4);
+
+ /* EBU ACIP FEC:: NOT SET IN BARESIP */
+
+ return err;
+}
+
+
int audio_alloc(struct audio **ap, const struct stream_param *stream_prm,
const struct config *cfg,
struct call *call, struct sdp_session *sdp_sess, int label,
@@ -774,6 +1054,9 @@ int audio_alloc(struct audio **ap, const struct stream_param *stream_prm,
tx = &a->tx;
rx = &a->rx;
+ tx->src_fmt = cfg->audio.src_fmt;
+ rx->play_fmt = cfg->audio.play_fmt;
+
err = stream_alloc(&a->strm, stream_prm, &cfg->avt, call, sdp_sess,
"audio", label,
mnat, mnat_sess, menc, menc_sess,
@@ -803,6 +1086,13 @@ int audio_alloc(struct audio **ap, const struct stream_param *stream_prm,
goto out;
}
+ if (cfg->sdp.ebuacip) {
+
+ err = set_ebuacip_params(a, ptime);
+ if (err)
+ goto out;
+ }
+
/* Audio codecs */
for (le = list_head(aucodecl); le; le = le->next) {
err = add_audio_codec(a, stream_sdpmedia(a->strm), le->data);
@@ -811,8 +1101,8 @@ int audio_alloc(struct audio **ap, const struct stream_param *stream_prm,
}
tx->mb = mbuf_alloc(STREAM_PRESZ + 4096);
- tx->sampv = mem_zalloc(AUDIO_SAMPSZ * 2, NULL);
- rx->sampv = mem_zalloc(AUDIO_SAMPSZ * 2, NULL);
+ tx->sampv = mem_zalloc(AUDIO_SAMPSZ * sizeof(int16_t), NULL);
+ rx->sampv = mem_zalloc(AUDIO_SAMPSZ * sizeof(int16_t), NULL);
if (!tx->mb || !tx->sampv || !rx->sampv) {
err = ENOMEM;
goto out;
@@ -829,7 +1119,7 @@ int audio_alloc(struct audio **ap, const struct stream_param *stream_prm,
auresamp_init(&tx->resamp);
str_ncpy(tx->device, a->cfg.src_dev, sizeof(tx->device));
tx->ptime = ptime;
- tx->ts = rand_u16();
+ tx->ts_ext = tx->ts_base = rand_u16();
tx->marker = true;
auresamp_init(&rx->resamp);
@@ -841,9 +1131,6 @@ int audio_alloc(struct audio **ap, const struct stream_param *stream_prm,
a->errh = errh;
a->arg = arg;
- if (a->cfg.txmode == AUDIO_MODE_TMR)
- tmr_init(&tx->u.tmr);
-
out:
if (err)
mem_deref(a);
@@ -859,46 +1146,46 @@ static void *tx_thread(void *arg)
{
struct audio *a = arg;
struct autx *tx = &a->tx;
- unsigned i;
-
- /* Enable Real-time mode for this thread, if available */
- if (a->cfg.txmode == AUDIO_MODE_THREAD_REALTIME)
- (void)realtime_enable(true, 1);
+ uint64_t ts = 0;
while (a->tx.u.thr.run) {
- for (i=0; i<16; i++) {
+ uint64_t now;
- if (aubuf_cur_size(tx->aubuf) < tx->psize)
- break;
+ sys_msleep(4);
- poll_aubuf_tx(a);
- }
+ if (!tx->aubuf_started)
+ continue;
- sys_msleep(5);
- }
+ if (!a->tx.u.thr.run)
+ break;
- return NULL;
-}
-#endif
+ now = tmr_jiffies();
+ if (!ts)
+ ts = now;
+ if (ts > now)
+ continue;
-static void timeout_tx(void *arg)
-{
- struct audio *a = arg;
- struct autx *tx = &a->tx;
- unsigned i;
+ /* Now is the time to send */
- tmr_start(&a->tx.u.tmr, 5, timeout_tx, a);
+ if (aubuf_cur_size(tx->aubuf) >= tx->psize) {
- for (i=0; i<16; i++) {
+ poll_aubuf_tx(a);
+ }
+ else {
+ ++tx->stats.aubuf_underrun;
- if (aubuf_cur_size(tx->aubuf) < tx->psize)
- break;
+ debug("audio: thread: tx aubuf underrun"
+ " (total %llu)\n", tx->stats.aubuf_underrun);
+ }
- poll_aubuf_tx(a);
+ ts += tx->ptime;
}
+
+ return NULL;
}
+#endif
static void aufilt_param_set(struct aufilt_prm *prm,
@@ -1079,13 +1366,18 @@ static int start_player(struct aurx *rx, struct audio *a)
prm.srate = srate_dsp;
prm.ch = channels_dsp;
prm.ptime = rx->ptime;
+ prm.fmt = rx->play_fmt;
if (!rx->aubuf) {
size_t psize;
+ size_t sz = aufmt_sample_size(rx->play_fmt);
- psize = 2 * calc_nsamp(prm.srate, prm.ch, prm.ptime);
+ psize = sz * calc_nsamp(prm.srate, prm.ch, prm.ptime);
- err = aubuf_alloc(&rx->aubuf, psize * 1, psize * 8);
+ rx->aubuf_maxsz = psize * 8;
+
+ err = aubuf_alloc(&rx->aubuf, psize * 1,
+ rx->aubuf_maxsz);
if (err)
return err;
}
@@ -1101,6 +1393,9 @@ static int start_player(struct aurx *rx, struct audio *a)
}
rx->auplay_prm = prm;
+
+ info("audio: player started with sample format %s\n",
+ aufmt_name(rx->play_fmt));
}
return 0;
@@ -1154,16 +1449,22 @@ static int start_source(struct autx *tx, struct audio *a)
if (!tx->ausrc && ausrc_find(baresip_ausrcl(), NULL)) {
struct ausrc_prm prm;
+ size_t sz;
prm.srate = srate_dsp;
prm.ch = channels_dsp;
prm.ptime = tx->ptime;
+ prm.fmt = tx->src_fmt;
+
+ sz = aufmt_sample_size(tx->src_fmt);
- tx->psize = 2 * calc_nsamp(prm.srate, prm.ch, prm.ptime);
+ tx->psize = sz * calc_nsamp(prm.srate, prm.ch, prm.ptime);
+
+ tx->aubuf_maxsz = tx->psize * 30;
if (!tx->aubuf) {
- err = aubuf_alloc(&tx->aubuf, tx->psize * 2,
- tx->psize * 30);
+ err = aubuf_alloc(&tx->aubuf, tx->psize,
+ tx->aubuf_maxsz);
if (err)
return err;
}
@@ -1181,7 +1482,6 @@ static int start_source(struct autx *tx, struct audio *a)
switch (a->cfg.txmode) {
#ifdef HAVE_PTHREAD
case AUDIO_MODE_THREAD:
- case AUDIO_MODE_THREAD_REALTIME:
if (!tx->u.thr.run) {
tx->u.thr.run = true;
err = pthread_create(&tx->u.thr.tid, NULL,
@@ -1194,15 +1494,14 @@ static int start_source(struct autx *tx, struct audio *a)
break;
#endif
- case AUDIO_MODE_TMR:
- tmr_start(&tx->u.tmr, 1, timeout_tx, a);
- break;
-
default:
break;
}
tx->ausrc_prm = prm;
+
+ info("audio: source started with sample format %s\n",
+ aufmt_name(tx->src_fmt));
}
return 0;
@@ -1578,6 +1877,7 @@ int audio_debug(struct re_printf *pf, const struct audio *a)
{
const struct autx *tx;
const struct aurx *rx;
+ size_t sztx, szrx;
int err;
if (!a)
@@ -1586,21 +1886,60 @@ int audio_debug(struct re_printf *pf, const struct audio *a)
tx = &a->tx;
rx = &a->rx;
+ sztx = aufmt_sample_size(tx->src_fmt);
+ szrx = aufmt_sample_size(rx->play_fmt);
+
err = re_hprintf(pf, "\n--- Audio stream ---\n");
- err |= re_hprintf(pf, " tx: %H %H ptime=%ums\n",
+ err |= re_hprintf(pf, " tx: %H ptime=%ums\n",
aucodec_print, tx->ac,
- aubuf_debug, tx->aubuf,
tx->ptime);
+ err |= re_hprintf(pf, " aubuf: %H"
+ " (cur %.2fms, max %.2fms, or %llu, ur %llu)\n",
+ aubuf_debug, tx->aubuf,
+ calc_ptime(aubuf_cur_size(tx->aubuf)/sztx,
+ tx->ausrc_prm.srate,
+ tx->ausrc_prm.ch),
+ calc_ptime(tx->aubuf_maxsz/sztx,
+ tx->ausrc_prm.srate,
+ tx->ausrc_prm.ch),
+ tx->stats.aubuf_overrun,
+ tx->stats.aubuf_underrun);
+
+ err |= re_hprintf(pf, " time = %.3f sec\n",
+ autx_calc_seconds(tx));
- err |= re_hprintf(pf, " rx: %H %H ptime=%ums pt=%d\n",
+ err |= re_hprintf(pf,
+ " rx: %H\n"
+ " ptime=%ums pt=%d\n",
aucodec_print, rx->ac,
- aubuf_debug, rx->aubuf,
rx->ptime, rx->pt);
+ err |= re_hprintf(pf, " aubuf: %H"
+ " (cur %.2fms, max %.2fms, or %llu, ur %llu)\n",
+ aubuf_debug, rx->aubuf,
+ calc_ptime(aubuf_cur_size(rx->aubuf)/szrx,
+ rx->auplay_prm.srate,
+ rx->auplay_prm.ch),
+ calc_ptime(rx->aubuf_maxsz/szrx,
+ rx->auplay_prm.srate,
+ rx->auplay_prm.ch),
+ rx->stats.aubuf_overrun,
+ rx->stats.aubuf_underrun
+ );
+
+ err |= re_hprintf(pf, " n_discard:%llu\n",
+ rx->n_discard);
if (rx->level_set) {
err |= re_hprintf(pf, " level %.3f dBov\n",
rx->level_last);
}
+ if (rx->ts_recv.is_set) {
+ err |= re_hprintf(pf, " time = %.3f sec\n",
+ aurx_calc_seconds(rx));
+ }
+ else {
+ err |= re_hprintf(pf, " time = (not started)\n");
+ }
err |= re_hprintf(pf,
" %H"
diff --git a/src/cmd.c b/src/cmd.c
index 1aaf761..c9d1745 100644
--- a/src/cmd.c
+++ b/src/cmd.c
@@ -85,6 +85,14 @@ static int ctx_alloc(struct cmd_ctx **ctxp, const struct cmd *cmd)
}
+/**
+ * Find a command block
+ *
+ * @param commands Commands container
+ * @param cmdv Command vector
+ *
+ * @return Command block if found, otherwise NULL
+ */
struct cmds *cmds_find(const struct commands *commands,
const struct cmd *cmdv)
{
@@ -291,6 +299,17 @@ static int cmd_report(const struct cmd *cmd, struct re_printf *pf,
}
+/**
+ * Process long commands
+ *
+ * @param commands Commands container
+ * @param str Input string
+ * @param len Length of input string
+ * @param pf_resp Print function for response
+ * @param data Application data
+ *
+ * @return 0 if success, otherwise errorcode
+ */
int cmd_process_long(struct commands *commands, const char *str, size_t len,
struct re_printf *pf_resp, void *data)
{
@@ -455,6 +474,14 @@ void cmd_unregister(struct commands *commands, const struct cmd *cmdv)
}
+/**
+ * Find a long command
+ *
+ * @param commands Commands container
+ * @param name Name of command, excluding prefix
+ *
+ * @return Command if found, NULL if not found
+ */
const struct cmd *cmd_find_long(const struct commands *commands,
const char *name)
{
@@ -715,6 +742,13 @@ int cmd_print(struct re_printf *pf, const struct commands *commands)
}
+/**
+ * Initialize the commands subsystem.
+ *
+ * @param commandsp Pointer to allocated commands
+ *
+ * @return 0 if success, otherwise errorcode
+ */
int cmd_init(struct commands **commandsp)
{
struct commands *commands;
diff --git a/src/conf.c b/src/conf.c
index e2e89f4..2046216 100644
--- a/src/conf.c
+++ b/src/conf.c
@@ -369,7 +369,7 @@ int conf_modules(void)
*
* @return Config object
*
- * @note It is only available during init
+ * @note It is only available after init and before conf_close()
*/
struct conf *conf_cur(void)
{
diff --git a/src/config.c b/src/config.c
index eb83c67..ce748d3 100644
--- a/src/config.c
+++ b/src/config.c
@@ -56,6 +56,8 @@ static struct config core_config = {
false,
AUDIO_MODE_POLL,
false,
+ AUFMT_S16LE,
+ AUFMT_S16LE,
},
#ifdef USE_VIDEO
@@ -95,6 +97,11 @@ static struct config core_config = {
""
},
#endif
+
+ /* SDP */
+ {
+ false
+ },
};
@@ -134,11 +141,57 @@ static int dns_server_handler(const struct pl *pl, void *arg)
}
+static enum aufmt resolve_aufmt(const struct pl *fmt)
+{
+ if (0 == pl_strcasecmp(fmt, "s16")) return AUFMT_S16LE;
+ if (0 == pl_strcasecmp(fmt, "float")) return AUFMT_FLOAT;
+ if (0 == pl_strcasecmp(fmt, "s24_3le")) return AUFMT_S24_3LE;
+
+ /* XXX remove this after librem is fixed */
+ if (0 == pl_strcasecmp(fmt, "s16le")) return AUFMT_S16LE;
+
+ return (enum aufmt)-1;
+}
+
+
+static int conf_get_aufmt(const struct conf *conf, const char *name,
+ int *fmtp)
+{
+ struct pl pl;
+ int fmt;
+ int err;
+
+ err = conf_get(conf, name, &pl);
+ if (err)
+ return err;
+
+ fmt = resolve_aufmt(&pl);
+ if (fmt == -1) {
+ warning("config: %s: sample format not supported"
+ " (%r)\n", name, &pl);
+ return EINVAL;
+ }
+
+ *fmtp = fmt;
+
+ return 0;
+}
+
+
+/**
+ * Parse the core configuration file and update baresip core config
+ *
+ * @param cfg Baresip core config to update
+ * @param conf Configuration file to parse
+ *
+ * @return 0 if success, otherwise errorcode
+ */
int config_parse_conf(struct config *cfg, const struct conf *conf)
{
struct pl pollm, as, ap;
enum poll_method method;
struct vidsz size = {0, 0};
+ struct pl txmode;
uint32_t v;
int err = 0;
@@ -202,8 +255,22 @@ int config_parse_conf(struct config *cfg, const struct conf *conf)
0 == conf_get(conf, "audio_player", &ap))
cfg->audio.src_first = as.p < ap.p;
+ if (0 == conf_get(conf, "audio_txmode", &txmode)) {
+
+ if (0 == pl_strcasecmp(&txmode, "poll"))
+ cfg->audio.txmode = AUDIO_MODE_POLL;
+ else if (0 == pl_strcasecmp(&txmode, "thread"))
+ cfg->audio.txmode = AUDIO_MODE_THREAD;
+ else {
+ warning("unsupported audio txmode (%r)\n", &txmode);
+ }
+ }
+
(void)conf_get_bool(conf, "audio_level", &cfg->audio.level);
+ conf_get_aufmt(conf, "ausrc_format", &cfg->audio.src_fmt);
+ conf_get_aufmt(conf, "auplay_format", &cfg->audio.play_fmt);
+
#ifdef USE_VIDEO
/* Video */
(void)conf_get_csv(conf, "video_source",
@@ -254,10 +321,21 @@ int config_parse_conf(struct config *cfg, const struct conf *conf)
sizeof(cfg->bfcp.proto));
#endif
+ /* SDP */
+ (void)conf_get_bool(conf, "sdp_ebuacip", &cfg->sdp.ebuacip);
+
return err;
}
+/**
+ * Print the baresip core config
+ *
+ * @param pf Print function
+ * @param cfg Baresip core config
+ *
+ * @return 0 if success, otherwise errorcode
+ */
int config_print(struct re_printf *pf, const struct config *cfg)
{
int err;
@@ -459,7 +537,10 @@ static int core_config_template(struct re_printf *pf, const struct config *cfg)
"#auplay_srate\t\t48000\n"
"#ausrc_channels\t\t0\n"
"#auplay_channels\t\t0\n"
+ "#audio_txmode\t\tpoll\t\t# poll, thread\n"
"audio_level\t\tno\n"
+ "ausrc_format\t\ts16\t\t# s16, float, ..\n"
+ "auplay_format\t\ts16\t\t# s16, float, ..\n"
,
poll_method_name(poll_method_best()),
cfg->call.local_timeout,
@@ -468,7 +549,8 @@ static int core_config_template(struct re_printf *pf, const struct config *cfg)
default_audio_device(),
default_audio_device(),
cfg->audio.srate.min, cfg->audio.srate.max,
- cfg->audio.channels.min, cfg->audio.channels.max);
+ cfg->audio.channels.min, cfg->audio.channels.max
+ );
#ifdef USE_VIDEO
err |= re_hprintf(pf,
@@ -478,7 +560,7 @@ static int core_config_template(struct re_printf *pf, const struct config *cfg)
"video_size\t\t%dx%d\n"
"video_bitrate\t\t%u\n"
"video_fps\t\t%u\n"
- "video_fullscreen\t\tyes\n",
+ "video_fullscreen\tyes\n",
default_video_device(),
default_video_display(),
cfg->video.width, cfg->video.height,
@@ -572,6 +654,14 @@ static const char *detect_module_path(bool *valid)
}
+/**
+ * Write the baresip core config template to a file
+ *
+ * @param file Filename of output file
+ * @param cfg Baresip core config
+ *
+ * @return 0 if success, otherwise errorcode
+ */
int config_write_template(const char *file, const struct config *cfg)
{
FILE *f = NULL;
@@ -762,6 +852,7 @@ int config_write_template(const char *file, const struct config *cfg)
(void)re_fprintf(f, "#module_app\t\t" MOD_PRE "natbd"MOD_EXT"\n");
(void)re_fprintf(f, "#module_app\t\t" MOD_PRE "presence"MOD_EXT"\n");
(void)re_fprintf(f, "#module_app\t\t" MOD_PRE "syslog"MOD_EXT"\n");
+ (void)re_fprintf(f, "#module_app\t\t" MOD_PRE "mqtt" MOD_EXT "\n");
#ifdef USE_VIDEO
(void)re_fprintf(f, "module_app\t\t" MOD_PRE "vidloop"MOD_EXT"\n");
#endif
@@ -807,6 +898,11 @@ int config_write_template(const char *file, const struct config *cfg)
"ice_mode\t\tfull\t# {full,lite}\n");
(void)re_fprintf(f,
+ "\n# ZRTP\n"
+ "#zrtp_hash\t\tno # Disable SDP zrtp-hash "
+ "(not recommended)\n");
+
+ (void)re_fprintf(f,
"\n# Menu\n"
"#redial_attempts\t\t3 # Num or <inf>\n"
"#redial_delay\t\t5 # Delay in seconds\n");
@@ -818,6 +914,11 @@ int config_write_template(const char *file, const struct config *cfg)
}
+/**
+ * Get the baresip core config
+ *
+ * @return Core config
+ */
struct config *conf_config(void)
{
return &core_config;
diff --git a/src/core.h b/src/core.h
index 1e1aa1e..a390e0c 100644
--- a/src/core.h
+++ b/src/core.h
@@ -133,7 +133,6 @@ int audio_encoder_set(struct audio *a, const struct aucodec *ac,
int pt_tx, const char *params);
int audio_decoder_set(struct audio *a, const struct aucodec *ac,
int pt_rx, const char *params);
-struct stream *audio_strm(const struct audio *a);
int audio_send_digit(struct audio *a, char key);
void audio_sdp_attr_decode(struct audio *a);
int audio_print_rtpstat(struct re_printf *pf, const struct audio *au);
@@ -471,7 +470,72 @@ int video_encoder_set(struct video *v, struct vidcodec *vc,
int pt_tx, const char *params);
int video_decoder_set(struct video *v, struct vidcodec *vc, int pt_rx,
const char *fmtp);
-struct stream *video_strm(const struct video *v);
void video_update_picture(struct video *v);
void video_sdp_attr_decode(struct video *v);
int video_print(struct re_printf *pf, const struct video *v);
+
+
+/*
+ * Timestamp helpers
+ */
+
+
+/**
+ * This struct is used to keep track of timestamps for
+ * incoming RTP packets.
+ */
+struct timestamp_recv {
+ uint32_t first;
+ uint32_t last;
+ bool is_set;
+ unsigned num_wraps;
+};
+
+
+static inline uint64_t calc_extended_timestamp(uint32_t num_wraps, uint32_t ts)
+{
+ uint64_t ext_ts;
+
+ ext_ts = (uint64_t)num_wraps * 0x100000000ULL;
+ ext_ts += (uint64_t)ts;
+
+ return ext_ts;
+}
+
+
+static inline uint64_t timestamp_duration(const struct timestamp_recv *ts)
+{
+ uint64_t last_ext;
+
+ if (!ts || !ts->is_set)
+ return 0;
+
+ last_ext = calc_extended_timestamp(ts->num_wraps, ts->last);
+
+ return last_ext - ts->first;
+}
+
+
+/*
+ * -1 backwards wrap-around
+ * 0 no wrap-around
+ * 1 forward wrap-around
+ */
+static inline int timestamp_wrap(uint32_t ts_new, uint32_t ts_old)
+{
+ int32_t delta;
+
+ if (ts_new < ts_old) {
+
+ delta = (int32_t)ts_new - (int32_t)ts_old;
+
+ if (delta > 0)
+ return 1;
+ }
+ else if ((int32_t)(ts_old - ts_new) > 0) {
+
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/src/event.c b/src/event.c
new file mode 100644
index 0000000..b29ab8b
--- /dev/null
+++ b/src/event.c
@@ -0,0 +1,171 @@
+/**
+ * @file src/event.c Baresip event handling
+ *
+ * Copyright (C) 2017 Creytiv.com
+ */
+
+#include <re.h>
+#include <baresip.h>
+#include "core.h"
+
+
+static const char *event_class_name(enum ua_event ev)
+{
+ switch (ev) {
+
+ case UA_EVENT_REGISTERING:
+ case UA_EVENT_REGISTER_OK:
+ case UA_EVENT_REGISTER_FAIL:
+ case UA_EVENT_UNREGISTERING:
+ return "register";
+
+ case UA_EVENT_SHUTDOWN:
+ case UA_EVENT_EXIT:
+ return "application";
+
+ case UA_EVENT_CALL_INCOMING:
+ case UA_EVENT_CALL_RINGING:
+ case UA_EVENT_CALL_PROGRESS:
+ case UA_EVENT_CALL_ESTABLISHED:
+ case UA_EVENT_CALL_CLOSED:
+ case UA_EVENT_CALL_TRANSFER_FAILED:
+ case UA_EVENT_CALL_DTMF_START:
+ case UA_EVENT_CALL_DTMF_END:
+ case UA_EVENT_CALL_RTCP:
+ return "call";
+
+ default:
+ return "other";
+ }
+}
+
+
+static int add_rtcp_stats(struct odict *od_parent, const struct rtcp_stats *rs)
+{
+ struct odict *od = NULL, *tx = NULL, *rx = NULL;
+ int err = 0;
+
+ if (!od_parent || !rs)
+ return EINVAL;
+
+ err = odict_alloc(&od, 8);
+ err |= odict_alloc(&tx, 8);
+ err |= odict_alloc(&rx, 8);
+ if (err)
+ goto out;
+
+ err = odict_entry_add(tx, "sent", ODICT_INT, (int64_t)rs->tx.sent);
+ err |= odict_entry_add(tx, "lost", ODICT_INT, (int64_t)rs->tx.lost);
+ err |= odict_entry_add(tx, "jit", ODICT_INT, (int64_t)rs->tx.jit);
+ if (err)
+ goto out;
+
+ err = odict_entry_add(rx, "sent", ODICT_INT, (int64_t)rs->rx.sent);
+ err |= odict_entry_add(rx, "lost", ODICT_INT, (int64_t)rs->rx.lost);
+ err |= odict_entry_add(rx, "jit", ODICT_INT, (int64_t)rs->rx.jit);
+ if (err)
+ goto out;
+
+ err = odict_entry_add(od, "tx", ODICT_OBJECT, tx);
+ err |= odict_entry_add(od, "rx", ODICT_OBJECT, rx);
+ err |= odict_entry_add(od, "rtt", ODICT_INT, (int64_t)rs->rtt);
+ if (err)
+ goto out;
+
+ /* add object to the parent */
+ err = odict_entry_add(od_parent, "rtcp_stats", ODICT_OBJECT, od);
+ if (err)
+ goto out;
+
+ out:
+ mem_deref(od);
+
+ return err;
+}
+
+
+int event_encode_dict(struct odict *od, struct ua *ua, enum ua_event ev,
+ struct call *call, const char *prm)
+{
+ const char *event_str = uag_event_str(ev);
+ int err = 0;
+
+ if (!od)
+ return EINVAL;
+
+ err |= odict_entry_add(od, "type", ODICT_STRING, event_str);
+ err |= odict_entry_add(od, "class",
+ ODICT_STRING, event_class_name(ev));
+ err |= odict_entry_add(od, "accountaor", ODICT_STRING, ua_aor(ua));
+ if (err)
+ goto out;
+
+ if (call) {
+
+ const char *dir;
+
+ dir = call_is_outgoing(call) ? "outgoing" : "incoming";
+
+ err |= odict_entry_add(od, "direction", ODICT_STRING, dir);
+ err |= odict_entry_add(od, "peeruri",
+ ODICT_STRING, call_peeruri(call));
+ if (err)
+ goto out;
+ }
+
+ if (str_isset(prm)) {
+ err = odict_entry_add(od, "param", ODICT_STRING, prm);
+ if (err)
+ goto out;
+ }
+
+ if (ev == UA_EVENT_CALL_RTCP) {
+ struct stream *strm = NULL;
+
+ if (0 == str_casecmp(prm, "audio"))
+ strm = audio_strm(call_audio(call));
+#ifdef USE_VIDEO
+ else if (0 == str_casecmp(prm, "video"))
+ strm = video_strm(call_video(call));
+#endif
+
+ err = add_rtcp_stats(od, stream_rtcp_stats(strm));
+ if (err)
+ goto out;
+ }
+
+ out:
+
+ return err;
+}
+
+
+/**
+ * Get the name of the User-Agent event
+ *
+ * @param ev User-Agent event
+ *
+ * @return Name of the event
+ */
+const char *uag_event_str(enum ua_event ev)
+{
+ switch (ev) {
+
+ case UA_EVENT_REGISTERING: return "REGISTERING";
+ case UA_EVENT_REGISTER_OK: return "REGISTER_OK";
+ case UA_EVENT_REGISTER_FAIL: return "REGISTER_FAIL";
+ case UA_EVENT_UNREGISTERING: return "UNREGISTERING";
+ case UA_EVENT_SHUTDOWN: return "SHUTDOWN";
+ case UA_EVENT_EXIT: return "EXIT";
+ case UA_EVENT_CALL_INCOMING: return "CALL_INCOMING";
+ case UA_EVENT_CALL_RINGING: return "CALL_RINGING";
+ case UA_EVENT_CALL_PROGRESS: return "CALL_PROGRESS";
+ case UA_EVENT_CALL_ESTABLISHED: return "CALL_ESTABLISHED";
+ case UA_EVENT_CALL_CLOSED: return "CALL_CLOSED";
+ case UA_EVENT_CALL_TRANSFER_FAILED: return "TRANSFER_FAILED";
+ case UA_EVENT_CALL_DTMF_START: return "CALL_DTMF_START";
+ case UA_EVENT_CALL_DTMF_END: return "CALL_DTMF_END";
+ case UA_EVENT_CALL_RTCP: return "CALL_RTCP";
+ default: return "?";
+ }
+}
diff --git a/src/log.c b/src/log.c
index 6d38d2a..650689d 100644
--- a/src/log.c
+++ b/src/log.c
@@ -70,12 +70,12 @@ void vlog(enum log_level level, const char *fmt, va_list ap)
bool color = level == LEVEL_WARN || level == LEVEL_ERROR;
if (color)
- (void)re_fprintf(stderr, "\x1b[31m"); /* Red */
+ (void)re_fprintf(stdout, "\x1b[31m"); /* Red */
- (void)re_fprintf(stderr, "%s", buf);
+ (void)re_fprintf(stdout, "%s", buf);
if (color)
- (void)re_fprintf(stderr, "\x1b[;m");
+ (void)re_fprintf(stdout, "\x1b[;m");
}
le = lg.logl.head;
diff --git a/src/main.c b/src/main.c
index 40f7f9d..b789648 100644
--- a/src/main.c
+++ b/src/main.c
@@ -77,7 +77,7 @@ int main(int argc, char *argv[])
size_t i;
int err;
- (void)re_fprintf(stderr, "baresip v%s"
+ (void)re_fprintf(stdout, "baresip v%s"
" Copyright (C) 2010 - 2017"
" Alfred E. Heggestad et al.\n",
BARESIP_VERSION);
diff --git a/src/play.c b/src/play.c
index bd5d3de..aa0b59f 100644
--- a/src/play.c
+++ b/src/play.c
@@ -69,7 +69,7 @@ static void tmr_polling(void *arg)
/**
* NOTE: DSP cannot be destroyed inside handler
*/
-static void write_handler(int16_t *sampv, size_t sampc, void *arg)
+static void write_handler(void *sampv, size_t sampc, void *arg)
{
struct play *play = arg;
size_t sz = sampc * 2;
@@ -242,6 +242,7 @@ int play_tone(struct play **playp, struct player *player,
wprm.ch = ch;
wprm.srate = srate;
wprm.ptime = PTIME;
+ wprm.fmt = AUFMT_S16LE;
err = auplay_alloc(&play->auplay, baresip_auplayl(),
cfg->audio.alert_mod, &wprm,
diff --git a/src/srcs.mk b/src/srcs.mk
index 686ca7c..a35e0d8 100644
--- a/src/srcs.mk
+++ b/src/srcs.mk
@@ -17,6 +17,7 @@ SRCS += cmd.c
SRCS += conf.c
SRCS += config.c
SRCS += contact.c
+SRCS += event.c
SRCS += log.c
SRCS += menc.c
SRCS += message.c
diff --git a/src/stream.c b/src/stream.c
index d3d0bba..38db2d6 100644
--- a/src/stream.c
+++ b/src/stream.c
@@ -52,6 +52,12 @@ static void check_rtp_handler(void *arg)
debug("stream: last \"%s\" RTP packet: %d milliseconds\n",
sdp_media_name(strm->sdp), diff_ms);
+ /* check for large jumps in time */
+ if (diff_ms > (3600 * 1000)) {
+ strm->ts_last = 0;
+ return;
+ }
+
if (diff_ms > (int)strm->rtp_timeout_ms) {
info("stream: no %s RTP packets received for"
@@ -289,6 +295,8 @@ static void rtcp_handler(const struct sa *src, struct rtcp_msg *msg, void *arg)
if (s->cfg.rtp_stats)
call_set_xrtpstat(s->call);
+ ua_event(call_get_ua(s->call), UA_EVENT_CALL_RTCP, s->call,
+ "%s", sdp_media_name(stream_sdpmedia(s)));
break;
}
}
@@ -717,3 +725,9 @@ int stream_print(struct re_printf *pf, const struct stream *s)
s->metric_tx.cur_bitrate,
s->metric_rx.cur_bitrate);
}
+
+
+const struct rtcp_stats *stream_rtcp_stats(const struct stream *strm)
+{
+ return strm ? &strm->rtcp_stats : NULL;
+}
diff --git a/src/ua.c b/src/ua.c
index e6a019d..0467e35 100644
--- a/src/ua.c
+++ b/src/ua.c
@@ -1633,36 +1633,6 @@ struct tls *uag_tls(void)
/**
- * Get the name of the User-Agent event
- *
- * @param ev User-Agent event
- *
- * @return Name of the event
- */
-const char *uag_event_str(enum ua_event ev)
-{
- switch (ev) {
-
- case UA_EVENT_REGISTERING: return "REGISTERING";
- case UA_EVENT_REGISTER_OK: return "REGISTER_OK";
- case UA_EVENT_REGISTER_FAIL: return "REGISTER_FAIL";
- case UA_EVENT_UNREGISTERING: return "UNREGISTERING";
- case UA_EVENT_SHUTDOWN: return "SHUTDOWN";
- case UA_EVENT_EXIT: return "EXIT";
- case UA_EVENT_CALL_INCOMING: return "CALL_INCOMING";
- case UA_EVENT_CALL_RINGING: return "CALL_RINGING";
- case UA_EVENT_CALL_PROGRESS: return "CALL_PROGRESS";
- case UA_EVENT_CALL_ESTABLISHED: return "CALL_ESTABLISHED";
- case UA_EVENT_CALL_CLOSED: return "CALL_CLOSED";
- case UA_EVENT_CALL_TRANSFER_FAILED: return "TRANSFER_FAILED";
- case UA_EVENT_CALL_DTMF_START: return "CALL_DTMF_START";
- case UA_EVENT_CALL_DTMF_END: return "CALL_DTMF_END";
- default: return "?";
- }
-}
-
-
-/**
* Find the correct UA from the contact user
*
* @param cuser Contact username
diff --git a/src/video.c b/src/video.c
index 43f7c9d..f191215 100644
--- a/src/video.c
+++ b/src/video.c
@@ -97,7 +97,7 @@ struct vtx {
struct tmr tmr_rtp; /**< Timer for sending RTP */
unsigned skipc; /**< Number of frames skipped */
struct list filtl; /**< Filters in encoding order */
- char device[64]; /**< Source device name */
+ char device[128]; /**< Source device name */
int muted_frames; /**< # of muted frames sent */
uint32_t ts_offset; /**< Random timestamp offset */
bool picup; /**< Send picture update */
@@ -136,7 +136,7 @@ struct vrx {
struct tmr tmr_picup; /**< Picture update timer */
struct vidsz size; /**< Incoming video resolution */
enum vidorient orient; /**< Display orientation */
- char device[64]; /**< Display device name */
+ char device[128]; /**< Display device name */
int pt_rx; /**< Incoming RTP payload type */
int frames; /**< Number of frames received */
int efps; /**< Estimated frame-rate */
diff --git a/test/call.c b/test/call.c
index 0cd0394..34c5fd5 100644
--- a/test/call.c
+++ b/test/call.c
@@ -5,6 +5,7 @@
*/
#include <string.h>
#include <re.h>
+#include <rem.h>
#include <baresip.h>
#include "test.h"
@@ -721,7 +722,7 @@ int test_call_video(void)
#endif
-static void mock_sample_handler(const int16_t *sampv, size_t sampc, void *arg)
+static void mock_sample_handler(const void *sampv, size_t sampc, void *arg)
{
struct fixture *fix = arg;
bool got_aulevel;
@@ -819,3 +820,87 @@ int test_call_progress(void)
return err;
}
+
+
+static void float_sample_handler(const void *sampv, size_t sampc, void *arg)
+{
+ struct fixture *fix = arg;
+ (void)sampv;
+ (void)sampc;
+
+ if (sampc && fix->a.n_established && fix->b.n_established)
+ re_cancel();
+}
+
+
+static int test_media_base(enum audio_mode txmode)
+{
+ struct fixture fix, *f = &fix;
+ struct ausrc *ausrc = NULL;
+ struct auplay *auplay = NULL;
+ int err = 0;
+
+ fixture_init(f);
+
+ conf_config()->audio.txmode = txmode;
+
+ conf_config()->audio.src_fmt = AUFMT_FLOAT;
+ conf_config()->audio.play_fmt = AUFMT_FLOAT;
+
+ err = mock_ausrc_register(&ausrc);
+ TEST_ERR(err);
+ err = mock_auplay_register(&auplay, float_sample_handler, f);
+ TEST_ERR(err);
+
+ f->estab_action = ACTION_NOTHING;
+
+ f->behaviour = BEHAVIOUR_ANSWER;
+
+ /* Make a call from A to B */
+ err = ua_connect(f->a.ua, 0, NULL, f->buri, NULL, VIDMODE_OFF);
+ TEST_ERR(err);
+
+ /* run main-loop with timeout, wait for events */
+ err = re_main_timeout(5000);
+ TEST_ERR(err);
+ TEST_ERR(fix.err);
+
+ ASSERT_EQ(0, fix.a.n_incoming);
+ ASSERT_EQ(1, fix.a.n_established);
+ ASSERT_EQ(0, fix.a.n_closed);
+ ASSERT_EQ(0, fix.a.close_scode);
+
+ ASSERT_EQ(1, fix.b.n_incoming);
+ ASSERT_EQ(1, fix.b.n_established);
+ ASSERT_EQ(0, fix.b.n_closed);
+
+ out:
+ conf_config()->audio.src_fmt = AUFMT_S16LE;
+ conf_config()->audio.play_fmt = AUFMT_S16LE;
+
+ fixture_close(f);
+ mem_deref(auplay);
+ mem_deref(ausrc);
+
+ if (fix.err)
+ return fix.err;
+
+ return err;
+}
+
+
+int test_call_format_float(void)
+{
+ int err;
+
+ err = test_media_base(AUDIO_MODE_POLL);
+ ASSERT_EQ(0, err);
+
+ err = test_media_base(AUDIO_MODE_THREAD);
+ ASSERT_EQ(0, err);
+
+ conf_config()->audio.txmode = AUDIO_MODE_POLL;
+
+ out:
+ return err;
+}
diff --git a/test/main.c b/test/main.c
index 497d347..e917f2d 100644
--- a/test/main.c
+++ b/test/main.c
@@ -35,6 +35,7 @@ static const struct test tests[] = {
TEST(test_call_dtmf),
TEST(test_call_aulevel),
TEST(test_call_progress),
+ TEST(test_call_format_float),
#ifdef USE_VIDEO
TEST(test_call_video),
TEST(test_video),
diff --git a/test/mock/mock_auplay.c b/test/mock/mock_auplay.c
index 9857d9f..2a4ffcc 100644
--- a/test/mock/mock_auplay.c
+++ b/test/mock/mock_auplay.c
@@ -4,6 +4,7 @@
* Copyright (C) 2010 - 2016 Creytiv.com
*/
#include <re.h>
+#include <rem.h>
#include <baresip.h>
#include "../test.h"
@@ -13,7 +14,7 @@ struct auplay_st {
struct tmr tmr;
struct auplay_prm prm;
- int16_t *sampv;
+ void *sampv;
size_t sampc;
auplay_write_h *wh;
void *arg;
@@ -72,7 +73,7 @@ static int mock_auplay_alloc(struct auplay_st **stp, const struct auplay *ap,
st->sampc = prm->srate * prm->ch * prm->ptime / 1000;
- st->sampv = mem_zalloc(2 * st->sampc, NULL);
+ st->sampv = mem_zalloc(aufmt_sample_size(prm->fmt) * st->sampc, NULL);
if (!st->sampv) {
err = ENOMEM;
goto out;
diff --git a/test/mock/mock_ausrc.c b/test/mock/mock_ausrc.c
index 39512f1..070cbfc 100644
--- a/test/mock/mock_ausrc.c
+++ b/test/mock/mock_ausrc.c
@@ -4,6 +4,7 @@
* Copyright (C) 2010 - 2016 Creytiv.com
*/
#include <re.h>
+#include <rem.h>
#include <baresip.h>
#include "../test.h"
@@ -13,7 +14,7 @@ struct ausrc_st {
struct tmr tmr;
struct ausrc_prm prm;
- int16_t *sampv;
+ void *sampv;
size_t sampc;
ausrc_read_h *rh;
void *arg;
@@ -65,7 +66,7 @@ static int mock_ausrc_alloc(struct ausrc_st **stp, const struct ausrc *as,
st->sampc = prm->srate * prm->ch * prm->ptime / 1000;
- st->sampv = mem_zalloc(2 * st->sampc, NULL);
+ st->sampv = mem_zalloc(aufmt_sample_size(prm->fmt) * st->sampc, NULL);
if (!st->sampv) {
err = ENOMEM;
goto out;
diff --git a/test/play.c b/test/play.c
index 9e8c09c..82c8813 100644
--- a/test/play.c
+++ b/test/play.c
@@ -39,7 +39,7 @@ static struct mbuf *generate_tone(void)
}
-static void sample_handler(const int16_t *sampv, size_t sampc, void *arg)
+static void sample_handler(const void *sampv, size_t sampc, void *arg)
{
struct test *test = arg;
size_t bytec = sampc * 2;
diff --git a/test/test.h b/test/test.h
index 572e179..f276a8d 100644
--- a/test/test.h
+++ b/test/test.h
@@ -143,7 +143,7 @@ int mock_ausrc_register(struct ausrc **ausrcp);
struct auplay;
-typedef void (mock_sample_h)(const int16_t *sampv, size_t sampc, void *arg);
+typedef void (mock_sample_h)(const void *sampv, size_t sampc, void *arg);
int mock_auplay_register(struct auplay **auplayp,
mock_sample_h *sampleh, void *arg);
@@ -206,6 +206,7 @@ int test_call_dtmf(void);
int test_call_video(void);
int test_call_aulevel(void);
int test_call_progress(void);
+int test_call_format_float(void);
#ifdef USE_VIDEO
int test_video(void);