diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..14876e4 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/baresip-*.tar.gz diff --git a/README.md b/README.md deleted file mode 100644 index 5321eb6..0000000 --- a/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# baresip - -A modular SIP user-agent with support for audio and video, and many IETF standards such as SIP, RTP, STUN, TURN, and ICE for both, IPv4 and IPv6. \ No newline at end of file diff --git a/baresip-0.6.6-3871768.patch b/baresip-0.6.6-3871768.patch new file mode 100644 index 0000000..ef25c4b --- /dev/null +++ b/baresip-0.6.6-3871768.patch @@ -0,0 +1,1526 @@ +https://github.com/baresip/baresip/compare/v0.6.6...3871768 + +diff --git a/Makefile b/Makefile +index d41b12f..e17ecdb 100644 +--- a/Makefile ++++ b/Makefile +@@ -10,7 +10,7 @@ + # + + PROJECT := baresip +-VERSION := 0.6.6 ++VERSION := 1.0.0 + DESCR := "Baresip is a modular SIP User-Agent with audio and video support" + + # Verbose and silent build modes +@@ -221,13 +221,22 @@ $(BIN): $(APP_OBJS) + -L$(LIBRE_SO) -lre $(LIBS) -o $@ + + ++# ++# List of modules used by selftest ++# ++ifneq ($(STATIC),) ++TEST_MODULES := ++else ++TEST_MODULES := g711.so ++endif ++ + .PHONY: test + test: $(TEST_BIN) + ./$(TEST_BIN) + +-$(TEST_BIN): $(STATICLIB) $(TEST_OBJS) ++$(TEST_BIN): $(STATICLIB) $(TEST_OBJS) $(TEST_MODULES) + @echo " LD $@" +- $(HIDE)$(LD) $(LFLAGS) $(TEST_OBJS) \ ++ $(HIDE)$(LD) $(LFLAGS) $(APP_LFLAGS) $(TEST_OBJS) \ + -L$(LIBRE_SO) -L. \ + -l$(PROJECT) -lre $(LIBS) $(TEST_LIBS) -o $@ + +diff --git a/include/baresip.h b/include/baresip.h +index 7553e28..79985ee 100644 +--- a/include/baresip.h ++++ b/include/baresip.h +@@ -13,7 +13,7 @@ extern "C" { + + + /** Defines the Baresip version string */ +-#define BARESIP_VERSION "0.6.6" ++#define BARESIP_VERSION "1.0.0" + + + #ifndef NET_MAX_NS +@@ -59,6 +59,8 @@ struct account; + + int account_alloc(struct account **accp, const char *sipaddr); + int account_debug(struct re_printf *pf, const struct account *acc); ++int account_json_api(struct odict *odacc, struct odict *odcfg, ++ const struct account *acc); + int account_set_auth_user(struct account *acc, const char *user); + int account_set_auth_pass(struct account *acc, const char *pass); + int account_set_outbound(struct account *acc, const char *ob, unsigned ix); +@@ -71,6 +73,7 @@ int account_set_stun_port(struct account *acc, uint16_t port); + int account_set_mediaenc(struct account *acc, const char *mediaenc); + int account_set_medianat(struct account *acc, const char *medianat); + int account_set_audio_codecs(struct account *acc, const char *codecs); ++int account_set_video_codecs(struct account *acc, const char *codecs); + int account_set_mwi(struct account *acc, const char *value); + int account_set_call_transfer(struct account *acc, const char *value); + int account_auth(const struct account *acc, char **username, char **password, +@@ -146,6 +149,12 @@ enum call_event { + CALL_EVENT_MENC, + }; + ++/** Video mode */ ++enum vidmode { ++ VIDMODE_OFF = 0, /**< Video disabled */ ++ VIDMODE_ON, /**< Video enabled */ ++}; ++ + struct call; + + typedef void (call_event_h)(struct call *call, enum call_event ev, +@@ -153,7 +162,7 @@ typedef void (call_event_h)(struct call *call, enum call_event ev, + typedef void (call_dtmf_h)(struct call *call, char key, void *arg); + + int call_connect(struct call *call, const struct pl *paddr); +-int call_answer(struct call *call, uint16_t scode); ++int call_answer(struct call *call, uint16_t scode, enum vidmode vmode); + int call_progress(struct call *call); + void call_hangup(struct call *call, uint16_t scode, const char *reason); + int call_modify(struct call *call); +@@ -209,6 +218,7 @@ int custom_hdrs_apply(const struct list *hdrs, custom_hdrs_h *h, void *arg); + typedef int (confline_h)(const struct pl *addr, void *arg); + + int conf_configure(void); ++int conf_configure_buf(const uint8_t *buf, size_t sz); + int conf_modules(void); + void conf_path_set(const char *path); + int conf_path_get(char *path, size_t sz); +@@ -712,12 +722,6 @@ enum ua_event { + UA_EVENT_MAX, + }; + +-/** Video mode */ +-enum vidmode { +- VIDMODE_OFF = 0, /**< Video disabled */ +- VIDMODE_ON, /**< Video enabled */ +-}; +- + /** Defines the User-Agent event handler */ + typedef void (ua_event_h)(struct ua *ua, enum ua_event ev, + struct call *call, const char *prm, void *arg); +@@ -732,11 +736,12 @@ int ua_connect(struct ua *ua, struct call **callp, + enum vidmode vmode); + void ua_hangup(struct ua *ua, struct call *call, + uint16_t scode, const char *reason); +-int ua_answer(struct ua *ua, struct call *call); +-int ua_hold_answer(struct ua *ua, struct call *call); ++int ua_answer(struct ua *ua, struct call *call, enum vidmode vmode); ++int ua_hold_answer(struct ua *ua, struct call *call, enum vidmode vmode); + int ua_options_send(struct ua *ua, const char *uri, + options_resp_h *resph, void *arg); + int ua_debug(struct re_printf *pf, const struct ua *ua); ++int ua_state_json_api(struct odict *od, const struct ua *ua); + int ua_print_calls(struct re_printf *pf, const struct ua *ua); + int ua_print_status(struct re_printf *pf, const struct ua *ua); + int ua_print_supported(struct re_printf *pf, const struct ua *ua); +@@ -1219,6 +1224,7 @@ int video_encoder_set(struct video *v, struct vidcodec *vc, + int video_start_source(struct video *v, struct media_ctx **ctx); + int video_start_display(struct video *v, const char *peer); + void video_stop(struct video *v); ++void video_stop_display(struct video *v); + int video_set_fullscreen(struct video *v, bool fs); + void video_vidsrc_set_device(struct video *v, const char *dev); + int video_set_source(struct video *v, const char *name, const char *dev); +@@ -1230,6 +1236,7 @@ struct stream *video_strm(const struct video *v); + double video_timestamp_to_seconds(uint64_t timestamp); + uint64_t video_calc_timebase_timestamp(uint64_t rtp_ts); + const struct vidcodec *video_codec(const struct video *vid, bool tx); ++void video_sdp_attr_decode(struct video *v); + + + /* +@@ -1447,7 +1454,7 @@ static inline bool h264_is_keyframe(int type) + + + int module_preload(const char *module); +-int module_load(const char *name); ++int module_load(const char *path, const char *name); + void module_unload(const char *name); + void module_app_unload(void); + +diff --git a/mk/Doxyfile b/mk/Doxyfile +index 946b1f7..3bc3fb6 100644 +--- a/mk/Doxyfile ++++ b/mk/Doxyfile +@@ -4,7 +4,7 @@ + # Project related configuration options + #--------------------------------------------------------------------------- + PROJECT_NAME = baresip +-PROJECT_NUMBER = 0.6.6 ++PROJECT_NUMBER = 1.0.0 + OUTPUT_DIRECTORY = ../baresip-dox + CREATE_SUBDIRS = NO + OUTPUT_LANGUAGE = English +diff --git a/mk/modules.mk b/mk/modules.mk +index 59704ed..e35bb80 100644 +--- a/mk/modules.mk ++++ b/mk/modules.mk +@@ -164,6 +164,7 @@ USE_SNDIO := $(shell [ -f $(SYSROOT)/include/sndio.h ] || \ + USE_STDIO := $(shell [ -f $(SYSROOT)/include/termios.h ] && echo "yes") + HAVE_SPEEXDSP := $(shell \ + [ -f $(SYSROOT)/local/lib/libspeexdsp$(LIB_SUFFIX) ] || \ ++ [ -f $(SYSROOT)/lib64/libspeexdsp$(LIB_SUFFIX) ] || \ + [ -f $(SYSROOT)/lib/libspeexdsp$(LIB_SUFFIX) ] || \ + [ -f $(SYSROOT_ALT)/lib/libspeexdsp$(LIB_SUFFIX) ] && echo "yes") + ifeq ($(HAVE_SPEEXDSP),) +diff --git a/modules/aac/aac.c b/modules/aac/aac.c +index 1432aae..54f4066 100644 +--- a/modules/aac/aac.c ++++ b/modules/aac/aac.c +@@ -77,7 +77,7 @@ static struct aucodec aac = { + void aac_encode_fmtp(const struct aac_param *prm) + { + (void)re_snprintf(fmtp_local, sizeof(fmtp_local), +- "streamType=5" ++ "streamType=%d" + "; profile-level-id=%u" + "; config=%s" + "; mode=%s" +@@ -86,9 +86,10 @@ void aac_encode_fmtp(const struct aac_param *prm) + "; indexLength=%u" + "; indexDeltaLength=%u" + "; bitrate=%u", ++ AAC_STREAMTYPE_AUDIO, + prm->profile_level_id, prm->config, "AAC-hbr", +- prm->constantduration, SIZELENGTH, +- INDEXLENGTH, INDEXDELTALENGTH, ++ prm->constantduration, AAC_SIZELENGTH, ++ AAC_INDEXLENGTH, AAC_INDEXDELTALENGTH, + prm->bitrate); + } + +diff --git a/modules/aac/aac.h b/modules/aac/aac.h +index 66f9fc1..84ad51f 100644 +--- a/modules/aac/aac.h ++++ b/modules/aac/aac.h +@@ -18,18 +18,20 @@ struct aac_param { + }; + + +-enum { AU_HDR_LEN = 4, /* single access unit only!!! */ +- +- SIZELENGTH = 13, +- INDEXLENGTH = 3, +- INDEXDELTALENGTH = 3, +- +- HIGH_QUALITY_AUDIO_PROFILE = 16, /* L3 */ +- LOW_DELAY_AUDIO_PROFILE = 25, /* L4 */ +- ENHANCED_LOW_DELAY_AUDIO_PROFILE = 76, /* L1 */ +- HIGH_EFFICIENCY_AAC_PROFILE = 46, /* L4 */ +- HIGH_EFFICIENCY_AAC_V2_PROFILE = 49, /* L3 */ +- AAC_PROFILE = 41, /* L2 */ ++enum { ++ AU_HDR_LEN = 4, /* single access unit only!!! */ ++ ++ AAC_SIZELENGTH = 13, ++ AAC_INDEXLENGTH = 3, ++ AAC_INDEXDELTALENGTH = 3, ++ AAC_STREAMTYPE_AUDIO = 5, ++ ++ HIGH_QUALITY_AUDIO_PROFILE = 16, /* L3 */ ++ LOW_DELAY_AUDIO_PROFILE = 25, /* L4 */ ++ ENHANCED_LOW_DELAY_AUDIO_PROFILE = 76, /* L1 */ ++ HIGH_EFFICIENCY_AAC_PROFILE = 46, /* L4 */ ++ HIGH_EFFICIENCY_AAC_V2_PROFILE = 49, /* L3 */ ++ AAC_PROFILE = 41, /* L2 */ + }; + + extern uint32_t aac_samplerate, aac_channels, aac_aot; +diff --git a/modules/aac/decode.c b/modules/aac/decode.c +index f8c78a2..4c469f0 100644 +--- a/modules/aac/decode.c ++++ b/modules/aac/decode.c +@@ -54,7 +54,7 @@ static int hdr_decode(struct au_hdr *au_data, const uint8_t *p, + + bits = ntohs(*(uint16_t *)(void *)&p[2]); + +- au_data->size = bits >> ((sizeof(uint16_t) * 8) - SIZELENGTH); ++ au_data->size = bits >> ((sizeof(uint16_t) * 8) - AAC_SIZELENGTH); + + if (au_data->size == 0) { + warning("aac: decode: invalid access unit size (zero)\n", +diff --git a/modules/aac/encode.c b/modules/aac/encode.c +index 58e046e..0c9415a 100644 +--- a/modules/aac/encode.c ++++ b/modules/aac/encode.c +@@ -63,7 +63,7 @@ static void hdr_encode(uint8_t *p, uint16_t size) + * size + */ + *(uint16_t *)(void *)&p[2] = +- htons(size << ((sizeof(uint16_t) * 8) - SIZELENGTH)); ++ htons(size << ((sizeof(uint16_t) * 8) - AAC_SIZELENGTH)); + } + + +diff --git a/modules/aac/sdp.c b/modules/aac/sdp.c +index e0527a0..46d7684 100644 +--- a/modules/aac/sdp.c ++++ b/modules/aac/sdp.c +@@ -50,16 +50,16 @@ bool aac_fmtp_cmp(const char *lfmtp, const char *rfmtp, void *arg) + return false; + } + +- if (param_value(rfmtp, "streamType") != 5) ++ if (param_value(rfmtp, "streamType") != AAC_STREAMTYPE_AUDIO) + return false; + +- if (param_value(rfmtp, "sizeLength") != SIZELENGTH) ++ if (param_value(rfmtp, "sizeLength") != AAC_SIZELENGTH) + return false; + +- if (param_value(rfmtp, "indexLength") != INDEXLENGTH) ++ if (param_value(rfmtp, "indexLength") != AAC_INDEXLENGTH) + return false; + +- if (param_value(rfmtp, "indexDeltaLength") != INDEXDELTALENGTH) ++ if (param_value(rfmtp, "indexDeltaLength") != AAC_INDEXDELTALENGTH) + return false; + + if (param_value(rfmtp, "bitrate") < 8000 || +diff --git a/modules/avcodec/avcodec.c b/modules/avcodec/avcodec.c +index 232258e..9f5b475 100644 +--- a/modules/avcodec/avcodec.c ++++ b/modules/avcodec/avcodec.c +@@ -260,8 +260,9 @@ static int module_init(void) + } + } + +- if ((ret = av_hwdevice_ctx_create(&avcodec_hw_device_ctx, type, +- NULL, NULL, 0)) < 0) { ++ ret = av_hwdevice_ctx_create(&avcodec_hw_device_ctx, type, ++ NULL, NULL, 0); ++ if (ret < 0) { + warning("avcodec: Failed to create HW device (%s)\n", + av_err2str(ret)); + return ENOTSUP; +diff --git a/modules/avcodec/decode.c b/modules/avcodec/decode.c +index b368ef1..ba5bbbf 100644 +--- a/modules/avcodec/decode.c ++++ b/modules/avcodec/decode.c +@@ -132,6 +132,11 @@ static int init_decoder(struct viddec_state *st, const char *name) + + st->ctx = avcodec_alloc_context3(st->codec); + ++ /* TODO: If avcodec_h264dec is h264_mediacodec, extradata needs to ++ added to context that contains Sequence Parameter Set (SPS) and ++ Picture Parameter Set (PPS), before avcodec_open2() is called. ++ */ ++ + st->pict = av_frame_alloc(); + + if (!st->ctx || !st->pict) +diff --git a/modules/avcodec/encode.c b/modules/avcodec/encode.c +index b4eeaa3..74c656f 100644 +--- a/modules/avcodec/encode.c ++++ b/modules/avcodec/encode.c +@@ -292,8 +292,9 @@ static int open_encoder(struct videnc_state *st, + + /* set hw_frames_ctx for encoder's AVCodecContext */ + +- if ((err = set_hwframe_ctx(st->ctx, avcodec_hw_device_ctx, +- size->w, size->h)) < 0) { ++ err = set_hwframe_ctx(st->ctx, avcodec_hw_device_ctx, ++ size->w, size->h); ++ if (err < 0) { + + warning("avcodec: encode: Failed to set" + " hwframe context.\n"); +@@ -320,7 +321,7 @@ static int open_encoder(struct videnc_state *st, + + + static int decode_sdpparam_h264(struct videnc_state *st, const struct pl *name, +- const struct pl *val) ++ const struct pl *val) + { + if (0 == pl_strcasecmp(name, "packetization-mode")) { + st->u.h264.packetization_mode = pl_u32(val); +diff --git a/modules/avcodec/sdp.c b/modules/avcodec/sdp.c +index 0605b05..c662318 100644 +--- a/modules/avcodec/sdp.c ++++ b/modules/avcodec/sdp.c +@@ -34,7 +34,7 @@ int avcodec_h264_fmtp_enc(struct mbuf *mb, const struct sdp_format *fmt, + { + struct vidcodec *vc = arg; + const uint8_t profile_idc = 0x42; /* baseline profile */ +- const uint8_t profile_iop = 0x80; ++ const uint8_t profile_iop = 0xe0; + (void)offer; + + if (!mb || !fmt || !vc) +diff --git a/modules/avformat/audio.c b/modules/avformat/audio.c +index 9e3264d..5e374e4 100644 +--- a/modules/avformat/audio.c ++++ b/modules/avformat/audio.c +@@ -74,7 +74,6 @@ int avformat_audio_alloc(struct ausrc_st **stp, const struct ausrc *as, + st->arg = arg; + st->prm = *prm; + +- /* todo: also lookup "dev" ? */ + if (ctx && *ctx && (*ctx)->id && !strcmp((*ctx)->id, "avformat")) { + st->shared = mem_ref(*ctx); + } +diff --git a/modules/b2bua/b2bua.c b/modules/b2bua/b2bua.c +index 0990888..3c278ba 100644 +--- a/modules/b2bua/b2bua.c ++++ b/modules/b2bua/b2bua.c +@@ -62,7 +62,8 @@ static void call_event_handler(struct call *call, enum call_event ev, + case CALL_EVENT_ESTABLISHED: + debug("b2bua: CALL_ESTABLISHED: peer_uri=%s\n", + call_peeruri(call)); +- call_answer(call2, 200); ++ call_answer(call2, 200, ++ call_has_video(call) ? VIDMODE_ON : VIDMODE_OFF); + break; + + case CALL_EVENT_CLOSED: +diff --git a/modules/debug_cmd/debug_cmd.c b/modules/debug_cmd/debug_cmd.c +index 41ff754..f6290b7 100644 +--- a/modules/debug_cmd/debug_cmd.c ++++ b/modules/debug_cmd/debug_cmd.c +@@ -82,6 +82,45 @@ static int cmd_ua_debug(struct re_printf *pf, void *unused) + } + + ++/** ++ * Returns all the User-Agents and their general codec state. ++ * Formatted as JSON, for use with TCP / MQTT API interface. ++ * JSON object with 'cuser' as the key. ++ * ++ * @return All User-Agents available, NULL if none ++ */ ++static int cmd_api_uastate(struct re_printf *pf, void *unused) ++{ ++ struct odict *od = NULL; ++ struct le *le; ++ int err; ++ (void)unused; ++ ++ err = odict_alloc(&od, 8); ++ if (err) ++ return err; ++ ++ for (le = list_head(uag_list()); le && !err; le = le->next) { ++ const struct ua *ua = le->data; ++ struct odict *odua; ++ ++ err = odict_alloc(&odua, 8); ++ ++ err |= ua_state_json_api(odua, ua); ++ err |= odict_entry_add(od, ua_aor(ua), ODICT_OBJECT, odua); ++ mem_deref(odua); ++ } ++ ++ err |= json_encode_odict(pf, od); ++ if (err) ++ warning("debug: failed to encode json (%m)\n", err); ++ ++ mem_deref(od); ++ ++ return re_hprintf(pf, "\n"); ++} ++ ++ + static int cmd_play_file(struct re_printf *pf, void *arg) + { + static struct play *g_play; +@@ -187,6 +226,7 @@ static const struct cmd debugcmdv[] = { + {"timers", 0, 0, "Timer debug", tmr_status }, + {"uastat", 'u', 0, "UA debug", cmd_ua_debug }, + {"uuid", 0, 0, "Print UUID", print_uuid }, ++{"apistate", 0, 0, "User Agent state", cmd_api_uastate }, + }; + + +diff --git a/modules/echo/echo.c b/modules/echo/echo.c +index 50f8b6a..d6e22a2 100644 +--- a/modules/echo/echo.c ++++ b/modules/echo/echo.c +@@ -83,7 +83,7 @@ static int new_session(struct ua *ua, struct call *call) + call_dtmf_handler, sess); + + list_append(&sessionl, &sess->le, sess); +- err = ua_answer(ua, call); ++ err = ua_answer(ua, call, VIDMODE_ON); + + if (err) + mem_deref(sess); +diff --git a/modules/gtk/gtk_mod.c b/modules/gtk/gtk_mod.c +index 501054e..e606c50 100644 +--- a/modules/gtk/gtk_mod.c ++++ b/modules/gtk/gtk_mod.c +@@ -700,7 +700,7 @@ static void mqueue_handler(int id, void *data, void *arg) + + case MQ_ANSWER: + call = data; +- err = ua_answer(ua, call); ++ err = ua_answer(ua, call, VIDMODE_ON); + if (err) { + gdk_threads_enter(); + warning_dialog("Call failed", +diff --git a/modules/gzrtp/stream.cpp b/modules/gzrtp/stream.cpp +index 1afa95a..074dd5d 100644 +--- a/modules/gzrtp/stream.cpp ++++ b/modules/gzrtp/stream.cpp +@@ -321,7 +321,7 @@ void Stream::stop() + + int Stream::sdp_encode(struct sdp_media *sdpm) + { +- // TODO: signaling hash ++ // NOTE: signaling hash + return 0; + } + +@@ -331,7 +331,7 @@ 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 ++ // NOTE: signaling hash + + return 0; + } +diff --git a/modules/ice/ice.c b/modules/ice/ice.c +index c7cf984..1d1d874 100644 +--- a/modules/ice/ice.c ++++ b/modules/ice/ice.c +@@ -13,12 +13,8 @@ + * Interactive Connectivity Establishment (ICE) for media NAT traversal + * + * This module enables ICE for NAT traversal. You can enable ICE +- * in your accounts file with the parameter ;medianat=ice. The following +- * options can be configured: ++ * in your accounts file with the parameter ;medianat=ice. + * +- \verbatim +- ice_debug {yes,no} # Enable ICE debugging/tracing +- \endverbatim + */ + + +@@ -32,10 +28,10 @@ struct mnat_sess { + struct sa srv; + struct stun_dns *dnsq; + struct sdp_session *sdp; ++ struct tmr tmr_async; + char lufrag[8]; + char lpwd[32]; + uint64_t tiebrk; +- enum ice_mode mode; + bool turn; + bool offerer; + char *user; +@@ -67,13 +63,6 @@ struct mnat_media { + }; + + +-static struct { +- bool debug; +-} ice = { +- false +-}; +- +- + static void gather_handler(int err, uint16_t scode, const char *reason, + void *arg); + +@@ -263,9 +252,6 @@ static int start_gathering(struct mnat_media *m, + unsigned i; + int err = 0; + +- if (m->sess->mode != ICE_MODE_FULL) +- return EINVAL; +- + /* for each component */ + for (i=0; i<2; i++) { + struct comp *comp = &m->compv[i]; +@@ -318,6 +304,7 @@ static void session_destructor(void *arg) + { + struct mnat_sess *sess = arg; + ++ tmr_cancel(&sess->tmr_async); + list_flush(&sess->medial); + mem_deref(sess->dnsq); + mem_deref(sess->user); +@@ -420,29 +407,12 @@ static int media_start(struct mnat_sess *sess, struct mnat_media *m) + + net_if_apply(if_handler, m); + +- switch (sess->mode) { +- +- default: +- case ICE_MODE_FULL: +- if (sess->turn) { +- err = icem_gather_relay(m, +- sess->user, sess->pass); +- } +- else { +- err = icem_gather_srflx(m); +- } +- break; +- +- case ICE_MODE_LITE: +- err = icem_lite_set_default_candidates(m->icem); +- if (err) { +- warning("ice: could not set" +- " default candidates (%m)\n", err); +- return err; +- } +- +- gather_handler(0, 0, NULL, m); +- break; ++ if (sess->turn) { ++ err = icem_gather_relay(m, ++ sess->user, sess->pass); ++ } ++ else { ++ err = icem_gather_srflx(m); + } + + return err; +@@ -478,6 +448,22 @@ static void dns_handler(int err, const struct sa *srv, void *arg) + } + + ++static void tmr_async_handler(void *arg) ++{ ++ struct mnat_sess *sess = arg; ++ struct le *le; ++ ++ for (le = sess->medial.head; le; le = le->next) { ++ ++ struct mnat_media *m = le->data; ++ ++ net_if_apply(if_handler, m); ++ ++ call_gather_handler(0, m, 0, ""); ++ } ++} ++ ++ + static int session_alloc(struct mnat_sess **sessp, + const struct mnat *mnat, struct dnsc *dnsc, + int af, const struct stun_uri *srv, +@@ -486,39 +472,37 @@ static int session_alloc(struct mnat_sess **sessp, + mnat_estab_h *estabh, void *arg) + { + struct mnat_sess *sess; +- const char *usage; ++ const char *usage = NULL; + int err = 0; ++ (void)mnat; + +- if (!sessp || !dnsc || !srv || !ss || !estabh) ++ if (!sessp || !dnsc || !ss || !estabh) + return EINVAL; + +- info("ice: new session with %s-server at %s (username=%s)\n", +- srv->scheme == STUN_SCHEME_TURN ? "TURN" : "STUN", +- srv->host, user); ++ if (srv) { ++ info("ice: new session with %s-server at %s (username=%s)\n", ++ srv->scheme == STUN_SCHEME_TURN ? "TURN" : "STUN", ++ srv->host, user); + +- switch (srv->scheme) { ++ switch (srv->scheme) { + +- case STUN_SCHEME_STUN: +- usage = stun_usage_binding; +- break; ++ case STUN_SCHEME_STUN: ++ usage = stun_usage_binding; ++ break; + +- case STUN_SCHEME_TURN: +- usage = stun_usage_relay; +- break; ++ case STUN_SCHEME_TURN: ++ usage = stun_usage_relay; ++ break; + +- default: +- return ENOTSUP; ++ default: ++ return ENOTSUP; ++ } + } + + sess = mem_zalloc(sizeof(*sess), session_destructor); + if (!sess) + return ENOMEM; + +- if (0 == str_casecmp(mnat->id, "ice")) +- sess->mode = ICE_MODE_FULL; +- else if (0 == str_casecmp(mnat->id, "ice-lite")) +- sess->mode = ICE_MODE_LITE; +- + sess->sdp = mem_ref(ss); + sess->estabh = estabh; + sess->arg = arg; +@@ -535,11 +519,6 @@ static int session_alloc(struct mnat_sess **sessp, + sess->tiebrk = rand_u64(); + sess->offerer = offerer; + +- if (ICE_MODE_LITE == sess->mode) { +- err |= sdp_session_set_lattr(ss, true, +- ice_attr_lite, NULL); +- } +- + err |= sdp_session_set_lattr(ss, true, + ice_attr_ufrag, sess->lufrag); + err |= sdp_session_set_lattr(ss, true, +@@ -547,11 +526,17 @@ static int session_alloc(struct mnat_sess **sessp, + if (err) + goto out; + +- sess->turn = (srv->scheme == STUN_SCHEME_TURN); ++ if (srv) { ++ sess->turn = (srv->scheme == STUN_SCHEME_TURN); + +- err = stun_server_discover(&sess->dnsq, dnsc, usage, stun_proto_udp, +- af, srv->host, srv->port, +- dns_handler, sess); ++ err = stun_server_discover(&sess->dnsq, dnsc, ++ usage, stun_proto_udp, ++ af, srv->host, srv->port, ++ dns_handler, sess); ++ } ++ else { ++ tmr_start(&sess->tmr_async, 1, tmr_async_handler, sess); ++ } + + out: + if (err) +@@ -654,6 +639,21 @@ static bool all_gathered(const struct mnat_sess *sess) + } + + ++static bool all_completed(const struct mnat_sess *sess) ++{ ++ struct le *le; ++ ++ /* Check all conncheck flags */ ++ LIST_FOREACH(&sess->medial, le) { ++ struct mnat_media *mx = le->data; ++ if (!mx->complete) ++ return false; ++ } ++ ++ return true; ++} ++ ++ + static void gather_handler(int err, uint16_t scode, const char *reason, + void *arg) + { +@@ -693,7 +693,7 @@ static void conncheck_handler(int err, bool update, void *arg) + { + struct mnat_media *m = arg; + struct mnat_sess *sess = m->sess; +- struct le *le; ++ bool sess_complete = false; + + info("ice: %s: connectivity check is complete (update=%d)\n", + sdp_media_name(m->sdpm), update); +@@ -720,29 +720,24 @@ static void conncheck_handler(int err, bool update, void *arg) + cand1 = icem_selected_rcand(m->icem, 1); + cand2 = icem_selected_rcand(m->icem, 2); + ++ sess_complete = all_completed(sess); ++ + if (m->connh) { + m->connh(icem_lcand_addr(cand1), + icem_lcand_addr(cand2), + m->arg); + } +- +- /* Check all conncheck flags */ +- LIST_FOREACH(&sess->medial, le) { +- struct mnat_media *mx = le->data; +- if (!mx->complete) +- return; +- } + } + + /* call estab-handler and send re-invite */ +- if (sess->send_reinvite && update) { ++ if (sess_complete && sess->send_reinvite && update) { + + info("ice: %s: sending Re-INVITE with updated" + " default candidates\n", + sdp_media_name(m->sdpm)); + +- sess->estabh(0, 0, NULL, sess->arg); + sess->send_reinvite = false; ++ sess->estabh(0, 0, NULL, sess->arg); + } + } + +@@ -780,16 +775,14 @@ static int ice_start(struct mnat_sess *sess) + if (sdp_media_has_media(m->sdpm)) { + m->complete = false; + +- if (sess->mode == ICE_MODE_FULL) { +- err = icem_conncheck_start(m->icem); +- if (err) +- return err; ++ err = icem_conncheck_start(m->icem); ++ if (err) ++ return err; + +- /* set the pair states +- -- first media stream only */ +- if (sess->medial.head == le) { +- ice_candpair_set_states(m->icem); +- } ++ /* set the pair states ++ -- first media stream only */ ++ if (sess->medial.head == le) { ++ ice_candpair_set_states(m->icem); + } + } + else { +@@ -831,14 +824,14 @@ static int media_alloc(struct mnat_media **mp, struct mnat_sess *sess, + else + role = ICE_ROLE_CONTROLLED; + +- err = icem_alloc(&m->icem, sess->mode, role, ++ err = icem_alloc(&m->icem, ICE_MODE_FULL, role, + IPPROTO_UDP, ICE_LAYER, + sess->tiebrk, sess->lufrag, sess->lpwd, + conncheck_handler, m); + if (err) + goto out; + +- icem_conf(m->icem)->debug = ice.debug; ++ icem_conf(m->icem)->debug = LEVEL_DEBUG==log_level_get(); + icem_conf(m->icem)->rc = 4; + + icem_set_conf(m->icem, icem_conf(m->icem)); +@@ -967,22 +960,10 @@ static struct mnat mnat_ice = { + .updateh = update, + }; + +-static struct mnat mnat_icelite = { +- .id = "ice-lite", +- .ftag = "+sip.ice", +- .wait_connected = true, +- .sessh = session_alloc, +- .mediah = media_alloc, +- .updateh = update, +-}; +- + + static int module_init(void) + { +- conf_get_bool(conf_cur(), "ice_debug", &ice.debug); +- + mnat_register(baresip_mnatl(), &mnat_ice); +- mnat_register(baresip_mnatl(), &mnat_icelite); + + return 0; + } +@@ -990,7 +971,6 @@ static int module_init(void) + + static int module_close(void) + { +- mnat_unregister(&mnat_icelite); + mnat_unregister(&mnat_ice); + + return 0; +diff --git a/modules/menu/menu.c b/modules/menu/menu.c +index ddab8f6..78244dc 100644 +--- a/modules/menu/menu.c ++++ b/modules/menu/menu.c +@@ -296,7 +296,7 @@ static int cmd_answer(struct re_printf *pf, void *unused) + /* Stop any ongoing ring-tones */ + menu.play = mem_deref(menu.play); + +- ua_hold_answer(ua, NULL); ++ ua_hold_answer(ua, NULL, VIDMODE_ON); + + return err; + } +diff --git a/modules/winwave/src.c b/modules/winwave/src.c +index 9a776c6..53eba94 100644 +--- a/modules/winwave/src.c ++++ b/modules/winwave/src.c +@@ -87,7 +87,6 @@ static void CALLBACK waveInCallback(HWAVEOUT hwo, + struct ausrc_st *st = (struct ausrc_st *)dwInstance; + WAVEHDR *wh = (WAVEHDR *)dwParam1; + struct auframe af; +- MMTIME mmtime; + + (void)hwo; + (void)dwParam2; +@@ -109,12 +108,10 @@ static void CALLBACK waveInCallback(HWAVEOUT hwo, + if (st->inuse < (READ_BUFFERS-1)) + add_wave_in(st); + +- waveInGetPosition(st->wavein, &mmtime, sizeof(mmtime)); +- + af.fmt = st->fmt; + af.sampv = (void *)wh->lpData; + af.sampc = wh->dwBytesRecorded/st->sampsz; +- af.timestamp = mmtime.u.ms * 1000; ++ af.timestamp = tmr_jiffies_usec(); + + st->rh(&af, st->arg); + +diff --git a/src/account.c b/src/account.c +index 4cf3b6c..6a92222 100644 +--- a/src/account.c ++++ b/src/account.c +@@ -442,7 +442,7 @@ int account_alloc(struct account **accp, const char *sipaddr) + if (acc->mnatid) { + acc->mnat = mnat_find(baresip_mnatl(), acc->mnatid); + if (!acc->mnat) { +- warning("account: medianat not found: `%s'\n", ++ warning("account: medianat not found: '%s'\n", + acc->mnatid); + } + } +@@ -450,7 +450,7 @@ int account_alloc(struct account **accp, const char *sipaddr) + if (acc->mencid) { + acc->menc = menc_find(baresip_mencl(), acc->mencid); + if (!acc->menc) { +- warning("account: mediaenc not found: `%s'\n", ++ warning("account: mediaenc not found: '%s'\n", + acc->mencid); + } + } +@@ -715,6 +715,34 @@ int account_set_audio_codecs(struct account *acc, const char *codecs) + + + /** ++ * Sets video codecs ++ * ++ * @param acc User-Agent account ++ * @param codecs Comma separated list of video codecs (NULL to disable) ++ * ++ * @return 0 if success, otherwise errorcode ++ */ ++int account_set_video_codecs(struct account *acc, const char *codecs) ++{ ++ char buf[256]; ++ struct pl pl; ++ ++ if (!acc) ++ return EINVAL; ++ ++ list_clear(&acc->vidcodecl); ++ ++ if (codecs) { ++ re_snprintf(buf, sizeof(buf), ";video_codecs=%s", codecs); ++ pl_set_str(&pl, buf); ++ return video_codecs_decode(acc, &pl); ++ } ++ ++ return 0; ++} ++ ++ ++/** + * Sets the displayed name. Pass null in dname to disable display name + * + * @param acc User-Agent account +@@ -1241,10 +1269,72 @@ int account_debug(struct re_printf *pf, const struct account *acc) + } + err |= re_hprintf(pf, "\n"); + } +- err |= re_hprintf(pf, " call_transfer: %s\n", ++ err |= re_hprintf(pf, " call_transfer:%s\n", + account_call_transfer(acc)); +- err |= re_hprintf(pf, " extra: %s\n", ++ err |= re_hprintf(pf, " extra: %s\n", + acc->extra ? acc->extra : "none"); + + return err; + } ++ ++ ++/** ++ * Print the account information in JSON ++ * ++ * @param od Account dict ++ * @param odcfg Configuration dict ++ * @param acc User-Agent account ++ * ++ * @return 0 if success, otherwise errorcode ++ */ ++int account_json_api(struct odict *od, struct odict *odcfg, ++ const struct account *acc) ++{ ++ int err = 0; ++ struct odict *obn = NULL; ++ const char *stunhost = ""; ++ ++ if (!acc) ++ return 0; ++ ++ /* account */ ++ err |= odict_entry_add(od, "aor", ODICT_STRING, acc->aor); ++ if (acc->dispname) { ++ err |= odict_entry_add(od, "display_name", ODICT_STRING, ++ acc->dispname); ++ } ++ ++ /* config */ ++ if (acc->sipnat) { ++ err |= odict_entry_add(odcfg, "sip_nat", ODICT_STRING, ++ acc->sipnat); ++ } ++ ++ err |= odict_alloc(&obn, 8); ++ for (size_t i=0; ioutboundv); i++) { ++ if (acc->outboundv[i]) { ++ err |= odict_entry_add(obn, "outbound", ODICT_STRING, ++ acc->outboundv[i]); ++ } ++ } ++ err |= odict_entry_add(odcfg, "sip_nat_outbound", ODICT_ARRAY, obn); ++ ++ stunhost = account_stun_host(acc) ? account_stun_host(acc) : ""; ++ err |= odict_entry_add(odcfg, "stun_host", ODICT_STRING, stunhost); ++ err |= odict_entry_add(odcfg, "stun_port", ODICT_INT, ++ account_stun_port(acc)); ++ if (acc->stun_user) { ++ err |= odict_entry_add(odcfg, "stun_user", ODICT_STRING, ++ acc->stun_user); ++ } ++ ++ err |= odict_entry_add(odcfg, "answer_mode", ODICT_STRING, ++ answermode_str(acc->answermode)); ++ err |= odict_entry_add(odcfg, "call_transfer", ODICT_BOOL, acc->refer); ++ ++ err |= odict_entry_add(odcfg, "packet_time", ODICT_INT, ++ account_ptime(acc)); ++ ++ mem_deref(obn); ++ return err; ++} +diff --git a/src/audio.c b/src/audio.c +index 6d057ca..90225d2 100644 +--- a/src/audio.c ++++ b/src/audio.c +@@ -162,7 +162,6 @@ struct aurx { + size_t last_sampc; + + struct { +- uint64_t aubuf_current_jb; + uint64_t aubuf_overrun; + uint64_t aubuf_underrun; + uint64_t n_discard; +@@ -202,10 +201,29 @@ static const char *uri_aulevel = "urn:ietf:params:rtp-hdrext:ssrc-audio-level"; + */ + uint64_t audio_jb_current_value(const struct audio *au) + { ++ const struct aurx *rx; ++ + if (!au) + return 0; + +- return au->rx.stats.aubuf_current_jb; ++ rx = &au->rx; ++ ++ if (rx->aubuf) { ++ uint64_t b_p_ms; /* bytes per ms */ ++ ++ b_p_ms = aufmt_sample_size(rx->play_fmt) * ++ rx->auplay_prm.srate * rx->auplay_prm.ch / 1000; ++ ++ if (b_p_ms) { ++ uint64_t val; ++ ++ val = aubuf_cur_size(rx->aubuf) / b_p_ms; ++ ++ return val; ++ } ++ } ++ ++ return 0; + } + + +@@ -627,7 +645,6 @@ 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); +- uint64_t b_p_ms = 0; /* bytes per ms */ + + if (rx->aubuf_started && aubuf_cur_size(rx->aubuf) < num_bytes) { + +@@ -638,16 +655,6 @@ static void auplay_write_handler(void *sampv, size_t sampc, void *arg) + rx->stats.aubuf_underrun); + #endif + } +- b_p_ms = aufmt_sample_size(rx->play_fmt)* +- rx->auplay_prm.srate *rx->auplay_prm.ch / 1000; +- +- if (b_p_ms) { +- size_t val = aubuf_cur_size(rx->aubuf) / b_p_ms; +- +- if (rx->stats.aubuf_current_jb != val) { +- rx->stats.aubuf_current_jb = val; +- } +- } + + aubuf_read(rx->aubuf, sampv, num_bytes); + } +diff --git a/src/baresip.c b/src/baresip.c +index a437e7e..231302b 100644 +--- a/src/baresip.c ++++ b/src/baresip.c +@@ -49,9 +49,13 @@ static int cmd_quit(struct re_printf *pf, void *unused) + static int insmod_handler(struct re_printf *pf, void *arg) + { + const struct cmd_arg *carg = arg; ++ char path[256]; + int err; + +- err = module_load(carg->prm); ++ if (conf_get_str(conf_cur(), "module_path", path, sizeof(path))) ++ str_ncpy(path, ".", sizeof(path)); ++ ++ err = module_load(path, carg->prm); + if (err) { + return re_hprintf(pf, "insmod: ERROR: could not load module" + " '%s': %m\n", carg->prm, err); +diff --git a/src/call.c b/src/call.c +index 1fdae44..a4d59a8 100644 +--- a/src/call.c ++++ b/src/call.c +@@ -1067,10 +1067,11 @@ int call_progress(struct call *call) + * + * @param call Call to answer + * @param scode Status code ++ * @param vmode Wanted video mode + * + * @return 0 if success, otherwise errorcode + */ +-int call_answer(struct call *call, uint16_t scode) ++int call_answer(struct call *call, uint16_t scode, enum vidmode vmode) + { + struct mbuf *desc; + int err; +@@ -1084,6 +1085,9 @@ int call_answer(struct call *call, uint16_t scode) + return 0; + } + ++ if (vmode == VIDMODE_OFF) ++ call->video = mem_deref(call->video); ++ + info("call: answering call from %s with %u\n", call->peer_uri, scode); + + if (call->got_offer) { +@@ -1405,6 +1409,8 @@ static int sipsess_offer_handler(struct mbuf **descp, + + if (got_offer) { + ++ call->got_offer = true; ++ + /* Decode SDP Offer */ + err = sdp_decode(call->sdp, msg->mb, true); + if (err) { +@@ -1432,6 +1438,8 @@ static int sipsess_answer_handler(const struct sip_msg *msg, void *arg) + + debug("call: got SDP answer (%zu bytes)\n", mbuf_get_left(msg->mb)); + ++ call->got_offer = false; ++ + if (msg_ctype_cmp(&msg->ctyp, "multipart", "mixed")) + (void)sdp_decode_multipart(&msg->ctyp.params, msg->mb); + +diff --git a/src/conf.c b/src/conf.c +index bfe59ba..0f654f4 100644 +--- a/src/conf.c ++++ b/src/conf.c +@@ -372,6 +372,31 @@ int conf_configure(void) + + + /** ++ * Configure the system from a buffer ++ * ++ * @param buf Buffer with config ++ * @param sz Size of buffer ++ * ++ * @return 0 if success, otherwise errorcode ++ */ ++int conf_configure_buf(const uint8_t *buf, size_t sz) ++{ ++ int err; ++ ++ if (!buf || !sz) ++ return EINVAL; ++ ++ conf_obj = mem_deref(conf_obj); ++ ++ err = conf_alloc_buf(&conf_obj, buf, sz); ++ if (err) ++ return err; ++ ++ return 0; ++} ++ ++ ++/** + * Load all modules from config file + * + * @return 0 if success, otherwise errorcode +diff --git a/src/core.h b/src/core.h +index 2c30b92..5164491 100644 +--- a/src/core.h ++++ b/src/core.h +@@ -54,7 +54,7 @@ struct account { + + /* parameters: */ + enum answermode answermode; /**< Answermode for incoming calls */ +- struct le acv[8]; /**< List elements for aucodecl */ ++ struct le acv[16]; /**< List elements for aucodecl */ + struct list aucodecl; /**< List of preferred audio-codecs */ + char *auth_user; /**< Authentication username */ + char *auth_pass; /**< Authentication password */ +@@ -220,6 +220,7 @@ int reg_register(struct reg *reg, const char *reg_uri, + void reg_unregister(struct reg *reg); + bool reg_isok(const struct reg *reg); + int reg_debug(struct re_printf *pf, const struct reg *reg); ++int reg_json_api(struct odict *od, const struct reg *reg); + int reg_status(struct re_printf *pf, const struct reg *reg); + int reg_af(const struct reg *reg); + +@@ -379,7 +380,6 @@ bool video_is_started(const struct video *v); + int video_decoder_set(struct video *v, struct vidcodec *vc, int pt_rx, + const char *fmtp); + 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); + + +diff --git a/src/module.c b/src/module.c +index c1a20b6..5a00620 100644 +--- a/src/module.c ++++ b/src/module.c +@@ -210,6 +210,7 @@ int module_preload(const char *module) + /** + * Load a module by name or by filename + * ++ * @param path Module path + * @param name Module name incl/excl extension, excluding module path + * + * @return 0 if success, otherwise errorcode +@@ -217,10 +218,10 @@ int module_preload(const char *module) + * example: "foo" + * example: "foo.so" + */ +-int module_load(const char *name) ++int module_load(const char *path, const char *name) + { + char filename[256]; +- struct pl path, pl_name; ++ struct pl pl_path, pl_name; + int err; + + if (!str_isset(name)) +@@ -228,12 +229,10 @@ int module_load(const char *name) + + append_extension(filename, sizeof(filename), name); + ++ pl_set_str(&pl_path, path); + pl_set_str(&pl_name, filename); + +- if (conf_get(conf_cur(), "module_path", &path)) +- pl_set_str(&path, "."); +- +- err = load_module(NULL, &path, &pl_name); ++ err = load_module(NULL, &pl_path, &pl_name); + + return err; + } +diff --git a/src/net.c b/src/net.c +index d5d3ba6..82412e2 100644 +--- a/src/net.c ++++ b/src/net.c +@@ -256,7 +256,6 @@ bool net_check(struct network *net) + &laddr6, &net->laddr6); + } + #endif +- debug("net: check for IP changes: change=%d\n", change); + + return change; + } +diff --git a/src/reg.c b/src/reg.c +index 1be4892..26fcc7f 100644 +--- a/src/reg.c ++++ b/src/reg.c +@@ -242,6 +242,14 @@ static const char *print_scode(uint16_t scode) + } + + ++/** ++ * Print the registration debug information ++ * ++ * @param pf Print function ++ * @param reg Registration object ++ * ++ * @return 0 if success, otherwise errorcode ++ */ + int reg_debug(struct re_printf *pf, const struct reg *reg) + { + int err = 0; +@@ -260,6 +268,34 @@ int reg_debug(struct re_printf *pf, const struct reg *reg) + } + + ++/** ++ * Print the registration information in JSON ++ * ++ * @param od Registration dict ++ * @param reg Registration object ++ * ++ * @return 0 if success, otherwise errorcode ++ */ ++int reg_json_api(struct odict *od, const struct reg *reg) ++{ ++ int err = 0; ++ ++ if (!reg) ++ return 0; ++ ++ err |= odict_entry_add(od, "id", ODICT_INT, (int64_t) reg->id); ++ err |= odict_entry_add(od, "state", ODICT_BOOL, reg_isok(reg)); ++ err |= odict_entry_add(od, "code", ODICT_INT, (int64_t) reg->scode); ++ if (reg->srv) ++ err |= odict_entry_add(od, "srv", ODICT_STRING, reg->srv); ++ ++ err |= odict_entry_add(od, "ipv", ODICT_STRING, ++ af_name(reg->af)); ++ ++ return err; ++} ++ ++ + int reg_status(struct re_printf *pf, const struct reg *reg) + { + if (!reg) +diff --git a/src/ua.c b/src/ua.c +index f4992fa..d261890 100644 +--- a/src/ua.c ++++ b/src/ua.c +@@ -56,7 +56,7 @@ static struct { + bool use_udp; /**< Use UDP transport */ + bool use_tcp; /**< Use TCP transport */ + bool use_tls; /**< Use TLS transport */ +- bool delayed_close; ++ bool delayed_close; /**< Module will close SIP stack */ + sip_msg_h *subh; /**< Subscribe handler */ + ua_exit_h *exith; /**< UA Exit handler */ + void *arg; /**< UA Exit handler argument */ +@@ -395,7 +395,7 @@ static void call_event_handler(struct call *call, enum call_event ev, + break; + + case ANSWERMODE_AUTO: +- (void)call_answer(call, 200); ++ (void)call_answer(call, 200, VIDMODE_ON); + break; + + case ANSWERMODE_MANUAL: +@@ -972,12 +972,13 @@ void ua_hangup(struct ua *ua, struct call *call, + /** + * Answer an incoming call + * +- * @param ua User-Agent +- * @param call Call to answer, or NULL for current call ++ * @param ua User-Agent ++ * @param call Call to answer, or NULL for current call ++ * @param vmode Wanted video mode + * + * @return 0 if success, otherwise errorcode + */ +-int ua_answer(struct ua *ua, struct call *call) ++int ua_answer(struct ua *ua, struct call *call, enum vidmode vmode) + { + if (!ua) + return EINVAL; +@@ -988,19 +989,20 @@ int ua_answer(struct ua *ua, struct call *call) + return ENOENT; + } + +- return call_answer(call, 200); ++ return call_answer(call, 200, vmode); + } + + + /** + * Put the current call on hold and answer the incoming call + * +- * @param ua User-Agent +- * @param call Call to answer, or NULL for current call ++ * @param ua User-Agent ++ * @param call Call to answer, or NULL for current call ++ * @param vmode Wanted video mode for the incoming call + * + * @return 0 if success, otherwise errorcode + */ +-int ua_hold_answer(struct ua *ua, struct call *call) ++int ua_hold_answer(struct ua *ua, struct call *call, enum vidmode vmode) + { + struct call *pcall; + int err; +@@ -1025,7 +1027,7 @@ int ua_hold_answer(struct ua *ua, struct call *call) + return err; + } + +- return ua_answer(ua, call); ++ return ua_answer(ua, call, vmode); + } + + +@@ -1237,6 +1239,66 @@ int ua_debug(struct re_printf *pf, const struct ua *ua) + } + + ++/** ++ * Print the user-agent information in JSON ++ * ++ * @param od User-Agent dict ++ * @param ua User-Agent object ++ * ++ * @return 0 if success, otherwise errorcode ++ */ ++int ua_state_json_api(struct odict *od, const struct ua *ua) ++{ ++ struct odict *reg = NULL; ++ struct odict *cfg = NULL; ++ struct le *le; ++ size_t i = 0; ++ int err = 0; ++ ++ if (!ua) ++ return 0; ++ ++ err |= odict_alloc(®, 8); ++ err |= odict_alloc(&cfg, 8); ++ ++ /* user-agent info */ ++ err |= odict_entry_add(od, "cuser", ODICT_STRING, ua->cuser); ++ err |= odict_entry_add(od, "selected_ua", ODICT_BOOL, ++ ua == uag_current()); ++ ++ /* account info */ ++ err |= account_json_api(od, cfg, ua->acc); ++ if (err) ++ warning("ua: failed to encode json account (%m)\n", err); ++ ++ /* registration info */ ++ for (le = list_head(&ua->regl); le; le = le->next) { ++ struct reg *regm = le->data; ++ err |= reg_json_api(reg, regm); ++ ++i; ++ } ++ if (i > 1) ++ warning("ua: multiple registrations for one account"); ++ ++ err |= odict_entry_add(reg, "interval", ODICT_INT, ++ (int64_t) ua->acc->regint); ++ err |= odict_entry_add(reg, "q_value", ODICT_DOUBLE, ua->acc->regq); ++ ++ if (err) ++ warning("ua: failed to encode json registration (%m)\n", err); ++ ++ /* package */ ++ err |= odict_entry_add(od, "settings", ODICT_OBJECT, cfg); ++ err |= odict_entry_add(od, "registration", ODICT_OBJECT, reg); ++ if (err) ++ warning("ua: failed to encode json package (%m)\n", err); ++ ++ mem_deref(cfg); ++ mem_deref(reg); ++ return err; ++} ++ ++ + /* One instance */ + + +diff --git a/src/video.c b/src/video.c +index d2da341..0f092e9 100644 +--- a/src/video.c ++++ b/src/video.c +@@ -1149,6 +1149,22 @@ void video_stop(struct video *v) + } + + ++/** ++ * Stop the video display ++ * ++ * @param v Video object ++ */ ++void video_stop_display(struct video *v) ++{ ++ if (!v) ++ return; ++ ++ debug("video: stopping video display ..\n"); ++ ++ v->vrx.vidisp = mem_deref(v->vrx.vidisp); ++} ++ ++ + bool video_is_started(const struct video *v) + { + return v ? v->started : false; +diff --git a/test/call.c b/test/call.c +index 79b24a9..e827830 100644 +--- a/test/call.c ++++ b/test/call.c +@@ -78,7 +78,9 @@ struct fixture { + f->estab_action = ACTION_RECANCEL; \ + f->exp_estab = 1; \ + f->exp_closed = 1; \ +- mock_aucodec_register(baresip_aucodecl()); \ ++ /* NOTE: See Makefile TEST_MODULES */ \ ++ err = module_load(".", "g711"); \ ++ TEST_ERR(err); \ + \ + err = ua_alloc(&f->a.ua, \ + "A ;regint=0" prm); \ +@@ -119,7 +121,7 @@ struct fixture { + mem_deref(f->b.ua); \ + mem_deref(f->a.ua); \ + \ +- mock_aucodec_unregister(); \ ++ module_unload("g711"); \ + \ + uag_event_unregister(event_handler); \ + \ +@@ -174,7 +176,7 @@ static void event_handler(struct ua *ua, enum ua_event ev, + switch (f->behaviour) { + + case BEHAVIOUR_ANSWER: +- err = ua_answer(ua, call); ++ err = ua_answer(ua, call, VIDMODE_ON); + if (err) { + warning("ua_answer failed (%m)\n", err); + goto out; +@@ -197,7 +199,7 @@ static void event_handler(struct ua *ua, enum ua_event ev, + + case BEHAVIOUR_GET_HDRS: + hdrs = call_get_custom_hdrs(call); +- err = ua_answer(ua, call); ++ err = ua_answer(ua, call, VIDMODE_ON); + if (err) { + warning("ua_answer failed (%m)\n", err); + goto out; diff --git a/baresip.spec b/baresip.spec new file mode 100644 index 0000000..ae89c40 --- /dev/null +++ b/baresip.spec @@ -0,0 +1,440 @@ +Summary: Modular SIP user-agent with audio and video support +Name: baresip +Version: 0.6.6 +Release: 1%{?dist} +License: BSD +URL: http://www.creytiv.com/baresip.html +Source0: https://github.com/baresip/baresip/archive/v%{version}/%{name}-%{version}.tar.gz +Patch0: baresip-0.6.6-3871768.patch +BuildRequires: make +BuildRequires: gcc +BuildRequires: libre-devel +BuildRequires: librem-devel +BuildRequires: openssl-devel +%if 0%{?fedora} || (0%{?rhel} && 0%{?rhel} > 7) +Recommends: %{name}-pulse%{?_isa} = %{version}-%{release} +%endif + +%description +A modular SIP user-agent with support for audio and video, and many IETF +standards such as SIP, RTP, STUN, TURN, and ICE for both, IPv4 and IPv6. + +Additional modules provide support for audio codecs like G.711, G.722, +G.726, GSM, L16, MPA, and Opus, audio drivers like ALSA, GStreamer, JACK +Audio Connection Kit, Portaudio, and PulseAudio, video codecs like VP8 or +VP9, video sources like Video4Linux and X11 grabber, video outputs like +SDL2 or X11, NAT traversal via STUN, TURN, ICE, NATBD, and NAT-PMP, media +encryption via SRTP or DTLS-SRTP, management features like embedded web- +server with HTTP interface, command-line console and interface, and MQTT. + +%package alsa +Summary: ALSA audio driver for baresip +BuildRequires: alsa-lib-devel +Requires: %{name}%{?_isa} = %{version}-%{release} + +%description alsa +Baresip is a modular SIP user-agent with audio and video support. + +This module provides the Advanced Linux Sound Architecture (ALSA) audio +driver. + +%package cairo +Summary: Video source driver for baresip to draw demo graphics +BuildRequires: pkgconfig(cairo) +Requires: %{name}%{?_isa} = %{version}-%{release} + +%description cairo +Baresip is a modular SIP user-agent with audio and video support. + +This module provides a video source driver to draw graphics for testing +and demo purposes into a frame buffer using the Cairo library. + +%package g722 +Summary: G.722 audio codec module for baresip +BuildRequires: spandsp-devel +Requires: %{name}%{?_isa} = %{version}-%{release} + +%description g722 +Baresip is a modular SIP user-agent with audio and video support. + +This module provides the G.722 audio codec, often used for HD voice. + +%package g726 +Summary: G.726 audio codec module for baresip +BuildRequires: spandsp-devel +Requires: %{name}%{?_isa} = %{version}-%{release} + +%description g726 +Baresip is a modular SIP user-agent with audio and video support. + +This module provides the G.726 audio codec. + +%package gsm +Summary: GSM audio codec module for baresip +BuildRequires: gsm-devel +Requires: %{name}%{?_isa} = %{version}-%{release} + +%description gsm +Baresip is a modular SIP user-agent with audio and video support. + +This module provides the GSM audio codec. + +%package gst +Summary: GStreamer audio source driver for baresip +BuildRequires: pkgconfig(gstreamer-1.0) +Requires: %{name}%{?_isa} = %{version}-%{release} + +%description gst +Baresip is a modular SIP user-agent with audio and video support. + +This module uses the GStreamer 1.0 framework to play external media and +provides them as an internal audio source. + +%package gst_video +Summary: Video codec support using GStreamer for baresip +BuildRequires: pkgconfig(gstreamer-1.0) +BuildRequires: pkgconfig(gstreamer-app-1.0) +Requires: %{name}%{?_isa} = %{version}-%{release} + +%description gst_video +Baresip is a modular SIP user-agent with audio and video support. + +This module implements video codecs using GStreamer 1.0 framework. + +%package gtk +Summary: GTK+ menu-based user interface module for baresip +BuildRequires: pkgconfig(gtk+-2.0) >= 2.22 +BuildRequires: pkgconfig(glib-2.0) >= 2.32 +Requires: %{name}%{?_isa} = %{version}-%{release} + +%description gtk +Baresip is a modular SIP user-agent with audio and video support. + +This module provides a GTK+ menu-based user interface. + +%package jack +Summary: JACK audio driver for baresip +BuildRequires: pkgconfig(jack) +Requires: %{name}%{?_isa} = %{version}-%{release} + +%description jack +Baresip is a modular SIP user-agent with audio and video support. + +This module provides the JACK Audio Connection Kit audio driver. + +# RHEL 8 doesn't have twolame-devel, see RHBZ#1843275 +%if 0%{?fedora} || (0%{?rhel} && 0%{?rhel} < 8) +%package mpa +Summary: MPA speech and audio codec module for baresip +BuildRequires: twolame-devel +BuildRequires: lame-devel +BuildRequires: mpg123-devel +%if 0%{?fedora} || (0%{?rhel} && 0%{?rhel} > 7) +BuildRequires: speexdsp-devel +%else +BuildRequires: speex-devel +%endif +Requires: %{name}%{?_isa} = %{version}-%{release} + +%description mpa +Baresip is a modular SIP user-agent with audio and video support. + +This module provides the MPA speech and audio codec. +%endif + +%package mqtt +Summary: MQTT management module for baresip +BuildRequires: mosquitto-devel +Requires: %{name}%{?_isa} = %{version}-%{release} + +%description mqtt +Baresip is a modular SIP user-agent with audio and video support. + +This module provides the Message Queue Telemetry Transport (MQTT) +management module. + +%package opus +Summary: Opus speech and audio codec module for baresip +BuildRequires: opus-devel +Requires: %{name}%{?_isa} = %{version}-%{release} + +%description opus +Baresip is a modular SIP user-agent with audio and video support. + +This module provides the Opus speech and audio codec module. + +%package plc +Summary: Packet Loss Concealment module for baresip +BuildRequires: spandsp-devel +Requires: %{name}%{?_isa} = %{version}-%{release} + +%description plc +Baresip is a modular SIP user-agent with audio and video support. + +This module provides the Packet Loss Concealment (PLC) module. + +%package portaudio +Summary: Portaudio audio driver for baresip +BuildRequires: portaudio-devel +Requires: %{name}%{?_isa} = %{version}-%{release} + +%description portaudio +Baresip is a modular SIP user-agent with audio and video support. + +This module provides the Portaudio audio driver. + +%package pulse +Summary: PulseAudio audio driver for baresip +BuildRequires: pkgconfig(libpulse-simple) +Requires: %{name}%{?_isa} = %{version}-%{release} + +%description pulse +Baresip is a modular SIP user-agent with audio and video support. + +This module provides the PulseAudio audio driver. + +%package rst + +Summary: Radio streamer audio/video source driver for baresip +BuildRequires: pkgconfig(cairo) +BuildRequires: pkgconfig(libmpg123) +Requires: %{name}%{?_isa} = %{version}-%{release} + +%description rst +Baresip is a modular SIP user-agent with audio and video support. + +This module uses mpg123 to play streaming media (MP3) and provide them as +an internal audio/video source. + +%package sdl +Summary: SDL2 video output driver for baresip +BuildRequires: SDL2-devel +Requires: %{name}%{?_isa} = %{version}-%{release} + +%description sdl +Baresip is a modular SIP user-agent with audio and video support. + +This module provides the Simple DirectMedia Layer 2.0 (SDL2) video output +driver. + +%package sndfile +Summary: Audio dumper module using libsndfile for baresip +BuildRequires: libsndfile-devel +Requires: %{name}%{?_isa} = %{version}-%{release} + +%description sndfile +Baresip is a modular SIP user-agent with audio and video support. + +This module provides an audio dumper to write WAV audio sample files +using libsndfile. + +%package speex_pp +Summary: Audio pre-processor module using libspeexdsp for baresip +%if 0%{?fedora} || (0%{?rhel} && 0%{?rhel} > 7) +BuildRequires: speexdsp-devel +%else +BuildRequires: speex-devel +%endif +Requires: %{name}%{?_isa} = %{version}-%{release} + +%description speex_pp +Baresip is a modular SIP user-agent with audio and video support. + +This module provides an audio pre-processor using libspeexdsp. + +%package vp8 +Summary: VP8 video codec module for baresip +BuildRequires: libvpx-devel +Requires: %{name}%{?_isa} = %{version}-%{release} + +%description vp8 +Baresip is a modular SIP user-agent with audio and video support. + +This module provides the VP8 video codec, which is compatible with the +WebRTC standard. + +%package vp9 +Summary: VP9 video codec module for baresip +BuildRequires: pkgconfig(vpx) >= 1.3.0 +Requires: %{name}%{?_isa} = %{version}-%{release} + +%description vp9 +Baresip is a modular SIP user-agent with audio and video support. + +This module provides the VP9 video codec, which is compatible with the +WebRTC standard. + +%package v4l2 +Summary: Video4Linux video source and codec modules for baresip +BuildRequires: libv4l-devel +Requires: %{name}%{?_isa} = %{version}-%{release} + +%description v4l2 +Baresip is a modular SIP user-agent with audio and video support. + +These modules provide the Video4Linux video source and codec, where +latter is for devices that support compressed formats such as H.264. + +%package x11 +Summary: X11 video output driver for baresip +BuildRequires: libX11-devel +BuildRequires: libXext-devel +Requires: %{name}%{?_isa} = %{version}-%{release} + +%description x11 +Baresip is a modular SIP user-agent with audio and video support. + +This module provides the X11 video output driver. + +%package x11grab +Summary: X11 grabber video source driver for baresip +BuildRequires: libX11-devel +BuildRequires: libXext-devel +Requires: %{name}%{?_isa} = %{version}-%{release} + +%description x11grab +Baresip is a modular SIP user-agent with audio and video support. + +This module provides the X11 grabber video source driver. + +%prep +%setup -q +%patch0 -p1 -b .3871768 + +%build +%make_build \ + SHELL='sh -x' \ + RELEASE=1 \ + PREFIX=%{_prefix} \ + MOD_PATH=%{_libdir}/%{name}/modules \ + EXTRA_CFLAGS="$RPM_OPT_FLAGS" \ + EXTRA_LFLAGS="$RPM_LD_FLAGS" + +%install +%make_install LIBDIR=%{_libdir} + +# Correct module permissions to add executable bit +chmod 755 $RPM_BUILD_ROOT%{_libdir}/%{name}/modules/*.so + +%files +%license docs/COPYING +%doc docs/ChangeLog docs/THANKS docs/examples +%{_bindir}/%{name} +%dir %{_libdir}/%{name}/ +%dir %{_libdir}/%{name}/modules/ +%{_libdir}/%{name}/modules/account.so +%{_libdir}/%{name}/modules/aubridge.so +%{_libdir}/%{name}/modules/aufile.so +%{_libdir}/%{name}/modules/auloop.so +%{_libdir}/%{name}/modules/b2bua.so +%{_libdir}/%{name}/modules/cons.so +%{_libdir}/%{name}/modules/contact.so +%{_libdir}/%{name}/modules/ctrl_tcp.so +%{_libdir}/%{name}/modules/debug_cmd.so +%{_libdir}/%{name}/modules/dtls_srtp.so +%{_libdir}/%{name}/modules/ebuacip.so +%{_libdir}/%{name}/modules/echo.so +%{_libdir}/%{name}/modules/evdev.so +%{_libdir}/%{name}/modules/fakevideo.so +%{_libdir}/%{name}/modules/g711.so +%{_libdir}/%{name}/modules/httpd.so +%{_libdir}/%{name}/modules/ice.so +%{_libdir}/%{name}/modules/l16.so +%{_libdir}/%{name}/modules/menu.so +%{_libdir}/%{name}/modules/mwi.so +%{_libdir}/%{name}/modules/natpmp.so +%{_libdir}/%{name}/modules/oss.so +%{_libdir}/%{name}/modules/presence.so +%{_libdir}/%{name}/modules/selfview.so +%{_libdir}/%{name}/modules/srtp.so +%{_libdir}/%{name}/modules/stdio.so +%{_libdir}/%{name}/modules/stun.so +%{_libdir}/%{name}/modules/syslog.so +%{_libdir}/%{name}/modules/turn.so +%{_libdir}/%{name}/modules/uuid.so +%{_libdir}/%{name}/modules/vidbridge.so +%{_libdir}/%{name}/modules/vidinfo.so +%{_libdir}/%{name}/modules/vidloop.so +%{_libdir}/%{name}/modules/vumeter.so +%{_datadir}/%{name}/ + +%files alsa +%{_libdir}/%{name}/modules/alsa.so + +%files cairo +%{_libdir}/%{name}/modules/cairo.so + +%files g722 +%{_libdir}/%{name}/modules/g722.so + +%files g726 +%{_libdir}/%{name}/modules/g726.so + +%files gsm +%{_libdir}/%{name}/modules/gsm.so + +%files gst +%{_libdir}/%{name}/modules/gst.so + +%files gst_video +%{_libdir}/%{name}/modules/gst_video.so + +%files gtk +%{_libdir}/%{name}/modules/gtk.so + +%files jack +%{_libdir}/%{name}/modules/jack.so + +%if 0%{?fedora} || (0%{?rhel} && 0%{?rhel} < 8) +%files mpa +%{_libdir}/%{name}/modules/mpa.so +%endif + +%files mqtt +%{_libdir}/%{name}/modules/mqtt.so + +%files opus +%{_libdir}/%{name}/modules/opus.so +%{_libdir}/%{name}/modules/opus_multistream.so + +%files plc +%{_libdir}/%{name}/modules/plc.so + +%files portaudio +%{_libdir}/%{name}/modules/portaudio.so + +%files pulse +%{_libdir}/%{name}/modules/pulse.so + +%files rst +%{_libdir}/%{name}/modules/rst.so + +%files sdl +%{_libdir}/%{name}/modules/sdl.so + +%files sndfile +%{_libdir}/%{name}/modules/sndfile.so + +%files speex_pp +%{_libdir}/%{name}/modules/speex_pp.so + +%files v4l2 +%{_libdir}/%{name}/modules/v4l2.so +%{_libdir}/%{name}/modules/v4l2_codec.so + +%files vp8 +%{_libdir}/%{name}/modules/vp8.so + +%files vp9 +%{_libdir}/%{name}/modules/vp9.so + +%files x11 +%{_libdir}/%{name}/modules/x11.so + +%files x11grab +%{_libdir}/%{name}/modules/x11grab.so + +%changelog +* Thu May 28 2020 Robert Scheck 0.6.6-1 +- Upgrade to 0.6.6 (#1843279) +- Initial spec file for Fedora and Red Hat Enterprise Linux diff --git a/sources b/sources new file mode 100644 index 0000000..5ebf227 --- /dev/null +++ b/sources @@ -0,0 +1 @@ +SHA512 (baresip-0.6.6.tar.gz) = b7d6b92e07aeb69c52364d28e7ab616a9365da366483b521c28f785001565ac5073357db07cdf3c0c0ada5d3f174ff6f31a8da1fdcf34b024df1f441ed64373d