diff --git a/CMakeLists.txt b/CMakeLists.txt index 5a8615f9b4..6a67a08b94 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -158,6 +158,7 @@ set(SRCS src/aureceiver.c src/ausrc.c src/baresip.c + src/bfcp.c src/bundle.c src/call.c src/cmd.c diff --git a/cmake/modules.cmake b/cmake/modules.cmake index 0ae510be77..1a35bce6f1 100644 --- a/cmake/modules.cmake +++ b/cmake/modules.cmake @@ -84,6 +84,7 @@ set(MODULES wincons winwave x11 + x11_grab CACHE STRING "List of modules like 'turn;pipewire;alsa'" ) diff --git a/include/baresip.h b/include/baresip.h index 5b223f4a17..58db58f4ba 100644 --- a/include/baresip.h +++ b/include/baresip.h @@ -227,6 +227,8 @@ void call_set_video_ldir(struct call *call, enum sdp_dir dir); int call_set_video_dir(struct call *call, enum sdp_dir dir); int call_update_media(struct call *call); int call_send_digit(struct call *call, char key); +int call_send_pfu(struct call *call, const char* content, + const char* label); bool call_has_audio(const struct call *call); bool call_has_video(const struct call *call); bool call_early_video_available(const struct call *call); @@ -253,6 +255,7 @@ const char *call_alerturi(const struct call *call); const char *call_diverteruri(const struct call *call); struct audio *call_audio(const struct call *call); struct video *call_video(const struct call *call); +struct video *call_slides(const struct call *call); struct list *call_streaml(const struct call *call); struct ua *call_get_ua(const struct call *call); bool call_is_onhold(const struct call *call); @@ -363,6 +366,7 @@ struct config_sip { enum tls_resume_mode tls_resume; /** TLS resumption mode */ uint8_t tos; /**< Type-of-Service for SIP */ uint32_t reg_filt; /**< Registrar filter transport mask*/ + bool media_control; /**< Media Control over SIP */ }; /** Call config */ @@ -446,6 +450,11 @@ struct config_net { bool use_getaddrinfo; /**< Use getaddrinfo for A/AAAA records */ }; +/** BFCP **/ +struct config_bfcp { + char proto[16]; /**< BFCP Transport */ + char floorctrl[16]; /**< BFCP floor control role */ +}; /** Core configuration */ struct config { @@ -457,9 +466,12 @@ struct config { struct config_audio audio; struct config_video video; + struct config_video slides; + struct config_avt avt; struct config_net net; + struct config_bfcp bfcp; }; int config_parse_conf(struct config *cfg, const struct conf *conf); @@ -857,6 +869,7 @@ enum ua_event { UA_EVENT_CALL_DTMF_END, UA_EVENT_CALL_RTPESTAB, UA_EVENT_CALL_RTCP, + UA_EVENT_CALL_VIDEO_DISP, UA_EVENT_CALL_MENC, UA_EVENT_VU_TX, UA_EVENT_VU_RX, @@ -1143,6 +1156,7 @@ struct vidisp_st; /** Video Display parameters */ struct vidisp_prm { bool fullscreen; /**< Enable fullscreen display */ + char content[64]; }; typedef void (vidisp_resize_h)(const struct vidsz *size, void *arg); @@ -1448,6 +1462,7 @@ const char *video_get_disp_dev(const struct video *v); int video_debug(struct re_printf *pf, const struct video *v); struct stream *video_strm(const struct video *v); const struct vidcodec *video_codec(const struct video *vid, bool tx); +void video_encode_refresh(struct video *v); void video_sdp_attr_decode(struct video *v); void video_req_keyframe(struct video *vid); @@ -1514,6 +1529,7 @@ const char *stream_peer(const struct stream *strm); int stream_bundle_init(struct stream *strm, bool offerer); int stream_debug(struct re_printf *pf, const struct stream *s); void stream_enable_rtp_timeout(struct stream *strm, uint32_t timeout_ms); +void print_rtp_stats(const struct stream *s); /** diff --git a/modules/avcodec/sdp.c b/modules/avcodec/sdp.c index 7ac676ee19..7f15c82dbf 100644 --- a/modules/avcodec/sdp.c +++ b/modules/avcodec/sdp.c @@ -39,6 +39,7 @@ int avcodec_h264_fmtp_enc(struct mbuf *mb, const struct sdp_format *fmt, uint8_t profile_idc = 0x42; /* baseline profile */ uint8_t profile_iop = 0xe0; uint8_t h264_level_idc = 0x1f; + char append[64] = ""; (void)offer; if (!mb || !fmt || !vc) @@ -46,6 +47,8 @@ int avcodec_h264_fmtp_enc(struct mbuf *mb, const struct sdp_format *fmt, conf_get_str(conf_cur(), "avcodec_profile_level_id", profile_level_id, sizeof(profile_level_id)); + conf_get_str(conf_cur(), "video_fmtp_append", + append, sizeof(append)); if (str_isset(profile_level_id)) { struct pl prof; @@ -67,10 +70,11 @@ int avcodec_h264_fmtp_enc(struct mbuf *mb, const struct sdp_format *fmt, out: return mbuf_printf(mb, "a=fmtp:%s" " %s" - ";profile-level-id=%02x%02x%02x" + ";profile-level-id=%02x%02x%02x%s" "\r\n", fmt->id, vc->variant, - profile_idc, profile_iop, h264_level_idc); + profile_idc, profile_iop, h264_level_idc, + append); } diff --git a/modules/ice/ice.c b/modules/ice/ice.c index 48aafd17c9..4ae983e259 100644 --- a/modules/ice/ice.c +++ b/modules/ice/ice.c @@ -239,7 +239,7 @@ static int cand_gather_relayed(struct mnat_media *m, struct comp *comp, err = turnc_alloc(&turnc, stun_conf(icem_stun(m->icem)), IPPROTO_UDP, comp->sock, layer, &m->sess->srv, username, password, - 60, turnc_handler, comp); + 60, NULL, turnc_handler, comp); if (err) return err; diff --git a/modules/menu/static_menu.c b/modules/menu/static_menu.c index 5aaf079d9c..146df36c1d 100644 --- a/modules/menu/static_menu.c +++ b/modules/menu/static_menu.c @@ -1331,7 +1331,7 @@ static int switch_video_source(struct re_printf *pf, void *arg) { const struct cmd_arg *carg = arg; struct pl pl_driver, pl_device; - struct config_video *vidcfg; + struct config_video *vidcfg, *slidescfg; struct config *cfg; struct video *v; const struct vidsrc *vs; @@ -1376,10 +1376,8 @@ static int switch_video_source(struct re_printf *pf, void *arg) return EINVAL; } - vidcfg = &cfg->video; - - str_ncpy(vidcfg->src_mod, driver, sizeof(vidcfg->src_mod)); - str_ncpy(vidcfg->src_dev, device, sizeof(vidcfg->src_dev)); + vidcfg= &cfg->video; + slidescfg = &cfg->slides; for (leu = list_head(uag_list()); leu; leu = leu->next) { struct ua *ua = leu->data; @@ -1388,8 +1386,11 @@ static int switch_video_source(struct re_printf *pf, void *arg) struct call *call = le->data; v = call_video(call); - - err = video_set_source(v, driver, device); + err = video_set_source(v, vidcfg->src_mod, + vidcfg->src_dev); + v = call_slides(call); + err |= video_set_source(v, slidescfg->src_mod, + slidescfg->src_dev); if (err) { (void)re_hprintf(pf, "failed to set video-source" diff --git a/modules/srtp/srtp.c b/modules/srtp/srtp.c index 53a1a13cfc..aa0ea92000 100644 --- a/modules/srtp/srtp.c +++ b/modules/srtp/srtp.c @@ -59,7 +59,7 @@ static const char aes_cm_128_hmac_sha1_80[] = "AES_CM_128_HMAC_SHA1_80"; static const char aes_128_gcm[] = "AEAD_AES_128_GCM"; static const char aes_256_gcm[] = "AEAD_AES_256_GCM"; -static const char *preferred_suite = aes_cm_128_hmac_sha1_80; +static const char *default_suite = aes_cm_128_hmac_sha1_80; static void destructor(void *arg) @@ -487,6 +487,7 @@ static int media_alloc(struct menc_media **stp, struct menc_sess *sess, int layer = 10; /* above zero */ int err = 0; bool mux = (rtpsock == rtcpsock); + char preferred_suite[64] = ""; (void)sess; (void)rtp; (void)raddr_rtp; @@ -537,6 +538,9 @@ static int media_alloc(struct menc_media **stp, struct menc_sess *sess, goto out; /* set our preferred crypto-suite */ + str_ncpy(preferred_suite, default_suite, sizeof(default_suite)); + conf_get_str(conf_cur(), "preferred_crypto_suite", + preferred_suite, sizeof(preferred_suite)); err |= str_dup(&st->crypto_suite, preferred_suite); if (err) goto out; diff --git a/modules/turn/turn.c b/modules/turn/turn.c index 119c2fb694..27dd78b6f7 100644 --- a/modules/turn/turn.c +++ b/modules/turn/turn.c @@ -49,6 +49,7 @@ struct mnat_media { struct tls_conn *tlsc; struct mbuf *mb; unsigned ix; + uint64_t token; } compv[COMPC]; }; @@ -196,16 +197,27 @@ static void turn_handler(int err, uint16_t scode, const char *reason, void *arg) { struct comp *comp = arg; + uint64_t token; struct mnat_media *m = comp->m; (void)mapped_addr; (void)msg; if (!err && !scode) { - const struct comp *other = &m->compv[comp->ix ^ 1]; + struct comp *other = &m->compv[comp->ix ^ 1]; - if (comp->ix == 0) - sdp_media_set_laddr(m->sdpm, relay_addr); + struct stun_attr *token_attr = stun_msg_attr(msg, STUN_ATTR_RSV_TOKEN); + if (token_attr) + token = token_attr->v.uint64; + + if (comp->ix == 0) { + sdp_media_set_laddr(m->sdpm, relay_addr); + err = turnc_alloc(&other->turnc, NULL, + IPPROTO_UDP, other->sock, LAYER, + &m->sess->srv, m->sess->user, m->sess->pass, + TURN_DEFAULT_LIFETIME, &token, + turn_handler, other); + } else sdp_media_set_laddr_rtcp(m->sdpm, relay_addr); @@ -238,7 +250,7 @@ static void tcp_estab_handler(void *arg) err = turnc_alloc(&comp->turnc, NULL, IPPROTO_TCP, comp->tc, 0, &m->sess->srv, m->sess->user, m->sess->pass, - TURN_DEFAULT_LIFETIME, turn_handler, comp); + TURN_DEFAULT_LIFETIME, NULL, turn_handler, comp); if (err) { m->sess->estabh(err, 0, NULL, m->sess->arg); } @@ -259,7 +271,7 @@ static int media_start(struct mnat_sess *sess, struct mnat_media *m) unsigned i; int err = 0; - for (i=0; icompv[i]; @@ -272,7 +284,7 @@ static int media_start(struct mnat_sess *sess, struct mnat_media *m) err |= turnc_alloc(&comp->turnc, NULL, IPPROTO_UDP, comp->sock, LAYER, &sess->srv, sess->user, sess->pass, - TURN_DEFAULT_LIFETIME, + TURN_DEFAULT_LIFETIME, NULL, turn_handler, comp); break; diff --git a/modules/x11/x11.c b/modules/x11/x11.c index 2538e8caf6..7ba9573641 100644 --- a/modules/x11/x11.c +++ b/modules/x11/x11.c @@ -45,6 +45,17 @@ struct vidisp_st { Atom XwinDeleted; int button_is_down; Time last_time; + char display[64]; + struct geometry { + struct resolution { + int w; + int h; + } res; + struct position { + int x; + int y; + } pos; + } geom; }; @@ -118,7 +129,8 @@ static int create_window(struct vidisp_st *st, const struct vidsz *sz) XSetWindowAttributes attr; #endif st->win = XCreateSimpleWindow(st->disp, DefaultRootWindow(st->disp), - 0, 0, sz->w, sz->h, 1, 0, 0); + st->geom.pos.x, st->geom.pos.y, + sz->w, sz->h, 0, 0, 0); if (!st->win) { warning("x11: failed to create X window\n"); return ENOMEM; @@ -272,6 +284,9 @@ static int alloc(struct vidisp_st **stp, const struct vidisp *vd, { struct vidisp_st *st; int err = 0; + char cfg_content[32] = "x11_"; + char cfg_geometry[64]; + struct pl x, y, w, h; (void)vd; (void)prm; (void)dev; @@ -284,7 +299,31 @@ static int alloc(struct vidisp_st **stp, const struct vidisp *vd, st->shm.shmaddr = (char *)-1; - st->disp = XOpenDisplay(NULL); + str_ncpy(st->display, "", str_len("")); + st->geom.res.w = st->geom.res.h = 0; + st->geom.pos.x = st->geom.pos.y = 0; + str_ncpy(cfg_content + str_len(cfg_content), + prm->content, str_len(prm->content)+1); + /* Example of expected lines in configuration file: + x11_main :0,1280x720+0+0 + x11_slides :0,1280x720+1280+0 */ + err = conf_get_csv(conf_cur(), cfg_content, + st->display, sizeof(st->display), + cfg_geometry, sizeof(cfg_geometry)); + + err = err | re_regex(cfg_geometry, + str_len(cfg_geometry), + "[0-9]*x[0-9]*\+[0-9]*\+[0-9]*", + &w, &h, &x, &y ); + if (!err) { + st->geom.res.w = pl_u32(&w); + st->geom.res.h = pl_u32(&h); + st->geom.pos.x = pl_u32(&x); + st->geom.pos.y = pl_u32(&y); + } + err = 0; + st->disp = XOpenDisplay(str_len(st->display)==0? + NULL:st->display); if (!st->disp) { warning("x11: could not open X display\n"); err = ENODEV; @@ -307,12 +346,16 @@ static int display(struct vidisp_st *st, const char *title, const struct vidframe *frame, uint64_t timestamp) { struct vidframe frame_rgb; + struct vidsz frame_size; int err = 0; (void)timestamp; if (!st->disp) return ENODEV; + frame_size.w = st->geom.res.w?st->geom.res.w:frame->size.w; + frame_size.h = st->geom.res.h?st->geom.res.h:frame->size.h; + /* * check for window delete - without blocking * the switch handles both the override redirect window @@ -365,29 +408,29 @@ static int display(struct vidisp_st *st, const char *title, } } - if (!vidsz_cmp(&st->size, &frame->size)) { + if (!vidsz_cmp(&st->size, &frame_size)) { char capt[256]; if (st->size.w && st->size.h) { info("x11: reset: %u x %u ---> %u x %u\n", st->size.w, st->size.h, - frame->size.w, frame->size.h); + frame_size.w, frame_size.h); } if (st->internal && !st->win) - err = create_window(st, &frame->size); + err = create_window(st, &frame_size); - err |= x11_reset(st, &frame->size); + err |= x11_reset(st, &frame_size); if (err) return err; if (title) { re_snprintf(capt, sizeof(capt), "%s - %u x %u", - title, frame->size.w, frame->size.h); + title, frame_size.w, frame_size.h); } else { re_snprintf(capt, sizeof(capt), "%u x %u", - frame->size.w, frame->size.h); + frame_size.w, frame_size.h); } XStoreName(st->disp, st->win, capt); @@ -395,7 +438,7 @@ static int display(struct vidisp_st *st, const char *title, /* Convert from YUV420P to RGB */ - vidframe_init_buf(&frame_rgb, st->pixfmt, &frame->size, + vidframe_init_buf(&frame_rgb, st->pixfmt, &frame_size, (uint8_t *)st->shm.shmaddr); vidconv(&frame_rgb, frame, 0); diff --git a/modules/x11grab/CMakeLists.txt b/modules/x11grab/CMakeLists.txt new file mode 100644 index 0000000000..37d4fd0734 --- /dev/null +++ b/modules/x11grab/CMakeLists.txt @@ -0,0 +1,12 @@ +project(x11grab) + +set(SRCS x11grab.c) + +if(STATIC) + add_library(${PROJECT_NAME} OBJECT ${SRCS}) +else() + add_library(${PROJECT_NAME} MODULE ${SRCS}) +endif() + +target_include_directories(${PROJECT_NAME} PRIVATE ${X11_INCLUDE_DIRS}) +target_link_libraries(${PROJECT_NAME} PRIVATE ${X11_LIBRARIES}) diff --git a/modules/x11grab/module.mk b/modules/x11grab/module.mk new file mode 100644 index 0000000000..b8f3ed808b --- /dev/null +++ b/modules/x11grab/module.mk @@ -0,0 +1,12 @@ +# +# module.mk +# +# Copyright (C) 2010 Alfred E. Heggestad +# + +MOD := x11grab +$(MOD)_SRCS += x11grab.c +$(MOD)_LFLAGS += -lX11 -lXext +$(MOD)_CFLAGS += -Wno-variadic-macros + +include mk/mod.mk diff --git a/modules/x11grab/x11grab.c b/modules/x11grab/x11grab.c new file mode 100644 index 0000000000..2f479b42b0 --- /dev/null +++ b/modules/x11grab/x11grab.c @@ -0,0 +1,220 @@ +/** + * @file x11grab.c X11 grabbing video-source + * + * Copyright (C) 2010 Alfred E. Heggestad + */ +#define _DEFAULT_SOURCE 1 +#define _BSD_SOURCE 1 +#include +#ifndef SOLARIS +#define _XOPEN_SOURCE 1 +#endif +#include +#include +#include +#include +#include +#include + + +/** + * @defgroup x11grab x11grab + * + * X11 window-grabbing video-source module + * + */ + + +struct vidsrc_st { + Display *disp; + XImage *image; + pthread_t thread; + bool run; + int fps; + struct vidsz size; + enum vidfmt pixfmt; + vidsrc_frame_h *frameh; + void *arg; +}; + + +static struct vidsrc *vidsrc; + + +static int x11grab_open(struct vidsrc_st *st, const struct vidsz *sz, + const char *dev) +{ + int x = 0, y = 0; + + st->disp = XOpenDisplay(dev); + if (!st->disp) { + warning("x11grab: error opening display '%s'\n", dev); + return ENODEV; + } + + st->image = XGetImage(st->disp, + RootWindow(st->disp, DefaultScreen(st->disp)), + x, y, sz->w, sz->h, AllPlanes, ZPixmap); + if (!st->image) { + warning("x11grab: error creating Ximage\n"); + return ENODEV; + } + + switch (st->image->bits_per_pixel) { + + case 32: + st->pixfmt = VID_FMT_RGB32; + break; + + default: + warning("x11grab: not supported: bpp=%d\n", + st->image->bits_per_pixel); + return ENOSYS; + } + + return 0; +} + + +static inline uint8_t *x11grab_read(struct vidsrc_st *st) +{ + const int x = 0, y = 0; + XImage *im; + + im = XGetSubImage(st->disp, + RootWindow(st->disp, DefaultScreen(st->disp)), + x, y, st->size.w, st->size.h, AllPlanes, ZPixmap, + st->image, 0, 0); + if (!im) + return NULL; + + return (uint8_t *)st->image->data; +} + + +static void call_frame_handler(struct vidsrc_st *st, uint8_t *buf, + uint64_t timestamp) +{ + struct vidframe frame; + + vidframe_init_buf(&frame, st->pixfmt, &st->size, buf); + + st->frameh(&frame, timestamp, st->arg); +} + + +static void *read_thread(void *arg) +{ + struct vidsrc_st *st = arg; + uint64_t ts = tmr_jiffies(); + uint8_t *buf; + + while (st->run) { + + uint64_t timestamp; + + if (tmr_jiffies() < ts) { + sys_msleep(4); + continue; + } + + buf = x11grab_read(st); + if (!buf) + continue; + + timestamp = ts * VIDEO_TIMEBASE / 1000; + + ts += (1000/st->fps); + + call_frame_handler(st, buf, timestamp); + } + + return NULL; +} + + +static void destructor(void *arg) +{ + struct vidsrc_st *st = arg; + + if (st->run) { + st->run = false; + pthread_join(st->thread, NULL); + } + + if (st->image) + XDestroyImage(st->image); + + if (st->disp) + XCloseDisplay(st->disp); +} + + +static int alloc(struct vidsrc_st **stp, const struct vidsrc *vs, + struct vidsrc_prm *prm, + const struct vidsz *size, const char *fmt, + const char *dev, vidsrc_frame_h *frameh, + vidsrc_packet_h *packeth, + vidsrc_error_h *errorh, void *arg) +{ + struct vidsrc_st *st; + int err; + + (void)fmt; + (void)packeth; + (void)errorh; + (void)vs; + + if (!stp || !prm || !size || !frameh) + return EINVAL; + + st = mem_zalloc(sizeof(*st), destructor); + if (!st) + return ENOMEM; + + st->size = *size; + st->fps = prm->fps; + st->frameh = frameh; + st->arg = arg; + + err = x11grab_open(st, size, dev); + if (err) + goto out; + + st->run = true; + err = pthread_create(&st->thread, NULL, read_thread, st); + if (err) { + st->run = false; + goto out; + } + + out: + if (err) + mem_deref(st); + else + *stp = st; + + return err; +} + + +static int x11grab_init(void) +{ + return vidsrc_register(&vidsrc, baresip_vidsrcl(), + "x11grab", alloc, NULL); +} + + +static int x11grab_close(void) +{ + vidsrc = mem_deref(vidsrc); + return 0; +} + + +EXPORT_SYM const struct mod_export DECL_EXPORTS(x11grab) = { + "x11grab", + "vidsrc", + x11grab_init, + x11grab_close +}; diff --git a/src/audio.c b/src/audio.c index ff0951ce2b..d91e73efad 100644 --- a/src/audio.c +++ b/src/audio.c @@ -1698,6 +1698,7 @@ int audio_debug(struct re_printf *pf, const struct audio *a) aurecv_print_pipeline, a->aur); err |= stream_debug(pf, a->strm); + print_rtp_stats(a->strm); return err; } diff --git a/src/bevent.c b/src/bevent.c index 22c45e1b34..5b4da5e077 100644 --- a/src/bevent.c +++ b/src/bevent.c @@ -514,9 +514,12 @@ int event_encode_dict(struct odict *od, struct ua *ua, enum ua_event ev, if (0 == str_casecmp(prm, "audio")) strm = audio_strm(call_audio(call)); - else if (0 == str_casecmp(prm, "video")) - strm = video_strm(call_video(call)); - + else if (NULL != strcasestr(prm, "video")){ + if (NULL != strcasestr(prm, "main")) + strm = video_strm(call_video(call)); + if (NULL != strcasestr(prm, "slides")) + strm = video_strm(call_slides(call)); + } err = add_rtcp_stats(od, stream_rtcp_stats(strm)); if (err) goto out; @@ -950,6 +953,7 @@ const char *uag_event_str(enum ua_event ev) case UA_EVENT_CALL_DTMF_END: return "CALL_DTMF_END"; case UA_EVENT_CALL_RTPESTAB: return "CALL_RTPESTAB"; case UA_EVENT_CALL_RTCP: return "CALL_RTCP"; + case UA_EVENT_CALL_VIDEO_DISP: return "VIDEO_DISP"; case UA_EVENT_CALL_MENC: return "CALL_MENC"; case UA_EVENT_VU_TX: return "VU_TX_REPORT"; case UA_EVENT_VU_RX: return "VU_RX_REPORT"; diff --git a/src/bfcp.c b/src/bfcp.c new file mode 100644 index 0000000000..a38b17225e --- /dev/null +++ b/src/bfcp.c @@ -0,0 +1,340 @@ +/** + * @file bfcp.c BFCP client + * + * Copyright (C) 2011 Creytiv.com + */ +#include +#include +#include +#include "core.h" + + +struct bfcp { + struct bfcp_conn *conn; + struct sdp_media *sdpm; + const struct mnat *mnat; + struct mnat_media *mnat_st; + bool active; + struct tmr tmr_hello; + + /* server */ + uint32_t lconfid; + uint16_t luserid; + + uint16_t lfloorid; + uint16_t lmstrm; +}; + + +static void destructor(void *arg) +{ + struct bfcp *bfcp = arg; + + tmr_cancel(&bfcp->tmr_hello); + + mem_deref(bfcp->mnat_st); + mem_deref(bfcp->sdpm); + mem_deref(bfcp->conn); +} + +static int stdout_handler(const char *p, size_t size, void *arg) +{ + (void)arg; + + if (1 != fwrite(p, size, 1, stdout)) + return ENOMEM; + + return 0; +} + + +static const char *bfcp_sdp_transp(enum bfcp_transp tp) +{ + switch (tp) { + + case BFCP_UDP: return "UDP/BFCP"; + case BFCP_TCP: return "TCP/BFCP"; + case BFCP_DTLS: return "UDP/TLS/BFCP"; + default: return NULL; + } +} + + +static enum bfcp_transp str2tp(const char *proto) +{ + if (0 == str_casecmp(proto, "udp")) + return BFCP_UDP; + if (0 == str_casecmp(proto, "tcp")) + return BFCP_TCP; + else if (0 == str_casecmp(proto, "dtls")) + return BFCP_DTLS; + else { + warning("unsupported BFCP protocol: %s\n", proto); + return -1; + } +} + + +static void bfcp_resp_handler(int err, const struct bfcp_msg *msg, void *arg) +{ + struct bfcp *bfcp = arg; + (void)bfcp; + + if (err) { + warning("bfcp: error response: %m\n", err); + return; + } + + info("bfcp: received BFCP response: '%s'\n", + bfcp_prim_name(msg->prim)); + + struct re_printf pf; + pf.vph = stdout_handler; + pf.arg = NULL; + bfcp_msg_print(&pf, msg); +} + + +void print_attr(const struct bfcp_attr *attr,void *dummy) +{ + struct re_printf pf; + pf.vph = stdout_handler; + pf.arg = NULL; + bfcp_attr_print(&pf, attr); + info("\n"); +} + + +static void bfcp_msg_handler(const struct bfcp_msg *msg, void *arg) +{ + struct bfcp *bfcp = arg; + + info("bfcp: received BFCP message '%s'\n", bfcp_prim_name(msg->prim)); + + struct re_printf pf; + struct bfcp_attr *attr; + pf.vph = stdout_handler; + pf.arg = NULL; + //bfcp_msg_print(&pf, msg); + struct bfcp_supprim supprim; + struct bfcp_supattr supattr; + enum bfcp_prim prim[] = { BFCP_FLOOR_REQUEST, + BFCP_FLOOR_RELEASE, + BFCP_FLOOR_REQUEST_QUERY, + BFCP_FLOOR_REQUEST_STATUS, + BFCP_HELLO, + BFCP_HELLO_ACK, + BFCP_GOODBYE, + BFCP_GOODBYE_ACK, + BFCP_ERROR }; + + enum bfcp_attrib attrib[] = { BFCP_BENEFICIARY_ID, + BFCP_FLOOR_ID, + BFCP_FLOOR_REQUEST_ID, + BFCP_PRIORITY, + BFCP_REQUEST_STATUS, + BFCP_ERROR_CODE, + BFCP_ERROR_INFO, + BFCP_PART_PROV_INFO, + BFCP_STATUS_INFO, + BFCP_SUPPORTED_ATTRS , + BFCP_SUPPORTED_PRIMS, + BFCP_USER_DISP_NAME, + BFCP_USER_URI , + BFCP_BENEFICIARY_INFO, + BFCP_FLOOR_REQ_INFO, + BFCP_REQUESTED_BY_INFO, + BFCP_FLOOR_REQ_STATUS, + BFCP_OVERALL_REQ_STATUS }; + + switch (msg->prim) { + + case BFCP_HELLO: + supprim.primv = prim; + supprim.primc = sizeof(prim)/sizeof(prim[0]); + supattr.attrv = attrib; + supattr.attrc = sizeof(attrib)/sizeof(attrib[0]); + (void)bfcp_reply(bfcp->conn, msg, + BFCP_HELLO_ACK, 2, + BFCP_SUPPORTED_ATTRS, 0, &supattr, + BFCP_SUPPORTED_PRIMS, 0, &supprim); + break; + + case BFCP_FLOOR_REQUEST: + attr = bfcp_msg_attr(msg, BFCP_FLOOR_ID); + uint16_t attr_val = attr->v.u16; + uint16_t floor_request_id = 1; + struct bfcp_reqstatus reqstatus; + reqstatus.status = BFCP_GRANTED; + reqstatus.qpos = 0; + (void)bfcp_reply(bfcp->conn, msg, + BFCP_FLOOR_REQUEST_STATUS, 1, + BFCP_FLOOR_REQ_INFO, 2, &floor_request_id, + BFCP_OVERALL_REQ_STATUS, 1, &floor_request_id, + BFCP_REQUEST_STATUS, 0, &reqstatus, + BFCP_FLOOR_REQ_STATUS, 0, &attr_val); + break; + + case BFCP_FLOOR_RELEASE: + case BFCP_HELLO_ACK: + case BFCP_GOODBYE: + case BFCP_GOODBYE_ACK: + break; + + default: + (void)bfcp_ereply(bfcp->conn, msg, BFCP_UNKNOWN_PRIM); + break; + } +} + +static void mnat_connected_handler(const struct sa *raddr1, + const struct sa *raddr2, void *arg) +{ + struct bfcp *bfcp = arg; + + info("BFCP mnat '%s' connected: raddr %J %J\n", + bfcp->mnat->id, raddr1, raddr2); +} + +int bfcp_alloc(struct bfcp **bfcpp, struct sdp_session *sdp_sess, + const struct config_bfcp *bfcp_cfg, bool offerer, + const struct mnat *mnat, struct mnat_sess *mnat_sess) +{ + struct bfcp *bfcp; + struct sa laddr; + enum bfcp_transp transp; + int err; + + if (!bfcpp || !sdp_sess) + return EINVAL; + + transp = str2tp(bfcp_cfg->proto); + + bfcp = mem_zalloc(sizeof(*bfcp), destructor); + if (!bfcp) + return ENOMEM; + + bfcp->active = offerer; + + sa_init(&laddr, AF_INET); + tmr_init(&bfcp->tmr_hello); + + err = bfcp_listen(&bfcp->conn, transp, &laddr, uag_tls(), + NULL, NULL, bfcp_msg_handler, NULL, bfcp); + if (err) + goto out; + + err = sdp_media_add(&bfcp->sdpm, sdp_sess, "application", + sa_port(&laddr), bfcp_sdp_transp(transp)); + if (err) + goto out; + + err = sdp_format_add(NULL, bfcp->sdpm, false, "*", NULL, + 0, 0, NULL, NULL, NULL, false, NULL); + if (err) + goto out; + + err |= sdp_media_set_lattr(bfcp->sdpm, true, "floorctrl", + str_isset(bfcp_cfg->floorctrl)? + bfcp_cfg->floorctrl:"c-s"); + err |= sdp_media_set_lattr(bfcp->sdpm, true, "setup", + bfcp->active ? "active" : "actpass"); + + if (bfcp->active) { + err |= sdp_media_set_lattr(bfcp->sdpm, true, + "connection", "new"); + } + else { + bfcp->lconfid = 1000 + (rand_u16() & 0xf); + bfcp->luserid = 1 + (rand_u16() & 0x7); + + err |= sdp_media_set_lattr(bfcp->sdpm, true, "confid", + "%u", bfcp->lconfid); + err |= sdp_media_set_lattr(bfcp->sdpm, true, "userid", + "%u", bfcp->luserid); + + bfcp->lfloorid = 1; + bfcp->lmstrm = 3; + err |= sdp_media_set_lattr(bfcp->sdpm, true, "floorid", + "%u mstrm %u", bfcp->lfloorid, + bfcp->lmstrm); + + err |= sdp_media_set_lattr(bfcp->sdpm, true, + "connection", "new"); + } + + if (err) + goto out; + + if (mnat) { + info("bfcp: enabled medianat '%s' on UDP socket\n", mnat->id); + err = mnat->mediah(&bfcp->mnat_st, mnat_sess, + bfcp_sock(bfcp->conn), NULL, bfcp->sdpm, + mnat_connected_handler, bfcp); + if (err) + goto out; + } + + + info("bfcp: %s BFCP agent protocol '%s' on port %d\n", + bfcp->active ? "Active" : "Passive", + bfcp_cfg->proto, sa_port(&laddr)); + + out: + if (err) + mem_deref(bfcp); + else + *bfcpp = bfcp; + + return err; +} + +int bfcp_send_hello(struct bfcp *bfcp) +{ + const struct sa *paddr; + uint32_t confid = 0; + uint16_t userid = 0; + int err = 0; + + tmr_start(&bfcp->tmr_hello, 10000,bfcp_send_hello, bfcp); + + paddr = sdp_media_raddr(bfcp->sdpm); + confid = 1; + if(sdp_media_rattr(bfcp->sdpm, "confid")) + confid = atoi(sdp_media_rattr(bfcp->sdpm, "confid")); + + userid = 1; + if(sdp_media_rattr(bfcp->sdpm, "userid")) + userid = atoi(sdp_media_rattr(bfcp->sdpm, "userid")); + + uint16_t floor_id = 1; + err = bfcp_request(bfcp->conn, paddr, BFCP_VER1, BFCP_HELLO, + confid, userid, bfcp_resp_handler, bfcp, 1, + BFCP_FLOOR_ID, 0, &floor_id); + + return err; + +} + +int bfcp_start(struct bfcp *bfcp) +{ + int err = 0; + char *floorctrl; + + if (!bfcp) + return EINVAL; + + if (!sdp_media_rport(bfcp->sdpm)) { + info("bfcp channel is disabled\n"); + return 0; + } + + floorctrl = sdp_media_rattr(bfcp->sdpm, "floorctrl"); + + if (floorctrl ) + if (str_str(floorctrl, "s")) { + bfcp_send_hello(bfcp); + } + + return err; +} diff --git a/src/call.c b/src/call.c index ccf455973d..e44f16d395 100644 --- a/src/call.c +++ b/src/call.c @@ -36,7 +36,9 @@ struct call { struct call *xcall; /**< Cross ref Transfer call */ struct list streaml; /**< List of mediastreams (struct stream) */ struct audio *audio; /**< Audio stream */ - struct video *video; /**< Video stream */ + struct video *video; /**< Video main stream */ + struct video *slides; /**< Video slides stream */ + struct bfcp *bfcp; /**< BFCP */ enum call_state state; /**< Call state */ int32_t adelay; /**< Auto answer delay in ms */ char *aluri; /**< Alert-Info URI */ @@ -53,6 +55,7 @@ struct call { struct tmr tmr_dtmf; /**< Timer for incoming DTMF events */ struct tmr tmr_answ; /**< Timer for delayed answer */ struct tmr tmr_reinv; /**< Timer for outgoing re-INVITES */ + struct tmr tmr_slides; /**< Timer for received slides stream */ time_t time_start; /**< Time when call started */ time_t time_conn; /**< Time when call initiated */ time_t time_stop; /**< Time when call stopped */ @@ -61,6 +64,9 @@ struct call { bool got_offer; /**< Got SDP Offer from Peer */ bool on_hold; /**< True if call is on hold (local) */ bool ans_queued; /**< True if an (auto) answer is queued */ + bool early_confirmed; /**< Early media confirmed by PRACK */ + bool pfu_disabled; /**< PFU requests disabled */ + bool slides_displayed; /**< True if slides are being displayed */ struct mnat_sess *mnats; /**< Media NAT session */ bool mnat_wait; /**< Waiting for MNAT to establish */ struct menc_sess *mencs; /**< Media encryption session state */ @@ -85,9 +91,22 @@ struct call { bool evstop; /**< UA events stopped flag */ }; +/** SIP info requests */ +enum sip_info_req { + DTMF = 0, + PICTURE_FAST_UPDATE, +}; + static int send_invite(struct call *call); +static int send_info(struct sipsess *sess, + const char* content_type, const char* content, + sip_resp_h *resph, + void *arg, const char *fmt, ...); static int send_dtmf_info(struct call *call, char key); +static void send_pfu_info_handler(int err, + const struct sip_msg *msg, + void *arg); static const char *state_name(enum call_state st) @@ -161,8 +180,10 @@ static void call_stream_stop(struct call *call) /* Video */ video_stop(call->video); + video_stop(call->slides); tmr_cancel(&call->tmr_inv); + tmr_cancel(&call->tmr_slides); } @@ -258,6 +279,9 @@ static int call_apply_sdp(struct call *call) if (call->video) video_sdp_attr_decode(call->video); + if (call->slides) + video_sdp_attr_decode(call->slides); + /* Update each stream */ FOREACH_STREAM { struct stream *strm = le->data; @@ -294,6 +318,11 @@ static int update_streams(struct call *call) else video_stop(call->video); + if (stream_is_ready(video_strm(call->slides))) + err |= video_update(call->slides, call->peer_uri); + else + video_stop(call->slides); + return err; } @@ -355,6 +384,8 @@ static void call_destructor(void *arg) mem_deref(call->diverter_uri); mem_deref(call->audio); mem_deref(call->video); + mem_deref(call->slides); + mem_deref(call->bfcp); mem_deref(call->sdp); mem_deref(call->mnats); mem_deref(call->mencs); @@ -450,6 +481,13 @@ static void menc_event_handler(enum menc_event event, warning("call: secure: could not" " start video: %m\n", err); } + stream_set_secure(video_strm(call->slides), true); + stream_start_rtcp(video_strm(call->slides)); + err = video_update(call->slides, call->peer_uri); + if (err) { + warning("call: secure: could not" + " start video: %m\n", err); + } } else { info("call: mediaenc: no match for stream (%s)\n", @@ -510,6 +548,10 @@ static void stream_mnatconn_handler(struct stream *strm, void *arg) case MEDIA_VIDEO: err = video_update(call->video, call->peer_uri); + if (err) { + err = video_update(call->slides, + call->peer_uri); + } if (err) { warning("call: mnatconn: could not" " start video: %m\n", err); @@ -520,11 +562,46 @@ static void stream_mnatconn_handler(struct stream *strm, void *arg) } +static void check_slides_stream(struct call *call) +{ + uint64_t rx_ts_last; + const uint64_t now = tmr_jiffies(); + int diff_ms; + + tmr_start(&call->tmr_slides, 1000, check_slides_stream, call); + + rx_ts_last = stream_rx_ts_last(video_strm(call->slides)); + if(rx_ts_last){ + diff_ms = (int)(now - rx_ts_last); + if (diff_ms > 1000 && call->slides_displayed){ + video_stop_display(call->slides); + call->slides_displayed = false; + ua_event(call->ua, UA_EVENT_CALL_VIDEO_DISP, NULL, + "VIDEO_SLIDES_STOP"); + video_start_display(call->slides, call->peer_uri); + } + if (diff_ms < 1000 && !call->slides_displayed){ + call->slides_displayed = true; + ua_event(call->ua, UA_EVENT_CALL_VIDEO_DISP, NULL, + "VIDEO_SLIDES_START"); + } + } +} + + static void stream_rtpestab_handler(struct stream *strm, void *arg) { struct call *call = arg; + char* content= NULL; MAGIC_CHECK(call); + content = sdp_media_rattr(stream_sdpmedia(strm), "content"); + + if(0==str_cmp(content, "slides")){ + tmr_start(&call->tmr_slides, 1000, check_slides_stream, call); + return; + } + ua_event(call->ua, UA_EVENT_CALL_RTPESTAB, call, "%s", sdp_media_name(stream_sdpmedia(strm))); } @@ -543,8 +620,23 @@ static void stream_rtcp_handler(struct stream *strm, if (call->config_avt.rtp_stats) call_set_xrtpstat(call); - ua_event(call->ua, UA_EVENT_CALL_RTCP, call, - "%s", sdp_media_name(stream_sdpmedia(strm))); + struct sdp_media *m ; + struct le *le; + int err; + char *content = NULL; + + content = sdp_media_rattr(stream_sdpmedia(strm), "content"); + + if( str_cmp(sdp_media_name(stream_sdpmedia(strm)), + "video") == 0) { + ua_event(call->ua, UA_EVENT_CALL_RTCP, call, + "%s-%s", sdp_media_name(stream_sdpmedia(strm)), + content?content:"unknown"); + } + else{ + ua_event(call->ua, UA_EVENT_CALL_RTCP, call, + "%s", sdp_media_name(stream_sdpmedia(strm))); + } break; case RTCP_APP: @@ -762,6 +854,28 @@ int call_streams_alloc(struct call *call) video_error_handler, call); if (err) return err; + + if (str_isset(call->cfg->bfcp.proto)) { + err = video_alloc(&call->slides, &call->streaml, + &strm_prm, + call->cfg, call->sdp, + acc->mnat, call->mnats, + acc->menc, call->mencs, + "slides", + account_vidcodecl(call->acc), + baresip_vidfiltl(), + !call->got_offer, + video_error_handler, call); + + if (err) + return err; + + err = bfcp_alloc(&call->bfcp, call->sdp, + &call->cfg->bfcp, !call->got_offer, + acc->mnat, call->mnats); + if (err) + return err; + } } FOREACH_STREAM { @@ -845,6 +959,7 @@ int call_alloc(struct call **callp, const struct config *cfg, struct list *lst, tmr_init(&call->tmr_inv); tmr_init(&call->tmr_answ); tmr_init(&call->tmr_reinv); + tmr_init(&call->tmr_slides); call->cfg = cfg; call->acc = mem_ref(acc); @@ -856,6 +971,8 @@ int call_alloc(struct call **callp, const struct config *cfg, struct list *lst, call->estadir = SDP_SENDRECV; call->estvdir = SDP_SENDRECV; call->use_rtp = prm->use_rtp; + call->pfu_disabled = false; + call->slides_displayed = false; call_decode_sip_autoanswer(call, msg); call_decode_diverter(call, msg); @@ -1266,6 +1383,8 @@ static bool call_need_modify(const struct call *call) int call_answer(struct call *call, uint16_t scode, enum vidmode vmode) { struct mbuf *desc; + char public_address[16] = ""; + struct sa pub_addr; int err; if (!call || !call->sess) @@ -1285,8 +1404,10 @@ int call_answer(struct call *call, uint16_t scode, enum vidmode vmode) return EAGAIN; } - if (vmode == VIDMODE_OFF) + if (vmode == VIDMODE_OFF){ call->video = mem_deref(call->video); + call->slides = mem_deref(call->slides); + } info("call: answering call on line %u from %s with %u\n", call->linenum, call->peer_uri, scode); @@ -1297,7 +1418,14 @@ int call_answer(struct call *call, uint16_t scode, enum vidmode vmode) ua_event(call->ua, UA_EVENT_CALL_LOCAL_SDP, call, "%s", !call->got_offer ? "offer" : "answer"); - err = sdp_encode(&desc, call->sdp, !call->got_offer); + sa_ntop(sdp_session_laddr(call->sdp), public_address, + sizeof(public_address) ); + + conf_get_str(conf_cur(), "public_address", public_address, sizeof(public_address)); + err = sa_set_str(&pub_addr, public_address, 0); + + sdp_session_set_laddr(call->sdp, &pub_addr); + err |= sdp_encode(&desc, call->sdp, !call->got_offer); if (err) return err; @@ -1349,7 +1477,8 @@ bool call_has_video(const struct call *call) if (!call) return false; - return sdp_media_has_media(stream_sdpmedia(video_strm(call->video))); + return sdp_media_has_media(stream_sdpmedia(video_strm(call->video))) || + sdp_media_has_media(stream_sdpmedia(video_strm(call->slides))) ; } @@ -1629,6 +1758,9 @@ int call_status(struct re_printf *pf, const struct call *call) if (call->video) err |= video_print(pf, call->video); + if (call->slides) + err |= video_print(pf, call->slides); + /* remove old junk */ err |= re_hprintf(pf, " "); @@ -1696,6 +1828,49 @@ int call_send_digit(struct call *call, char key) } +/** + * Send a picture_fast_update request to the peer + * + * @param call Call object + * + * @return 0 if success, otherwise errorcode + */ +int call_send_pfu(struct call *call, const char* content, const char* label) +{ + char media_strm[128] = ""; + int err = 0; + + if (!call) + return EINVAL; + + if (call->pfu_disabled) + return 1; + + if(0==str_cmp(content, "slides")){ + re_snprintf(media_strm, sizeof(media_strm), + "%s", label); + } + + err = send_info(call->sess, + "application/media_control+xml", + "" + "" + "" + "%s" + "" + "", + send_pfu_info_handler, + call, media_strm); + if (err) { + warning("call: picture_fast_update request failed (%m)\n", err); + } + + call->pfu_disabled = true; + + return err; +} + + /** * Get the User-Agent for the call * @@ -1848,6 +2023,7 @@ static void sipsess_estab_handler(const struct sip_msg *msg, void *arg) { struct call *call = arg; uint32_t wait; + char * content = NULL; (void)msg; MAGIC_CHECK(call); @@ -1868,7 +2044,9 @@ static void sipsess_estab_handler(const struct sip_msg *msg, void *arg) FOREACH_STREAM { struct stream *strm = le->data; - stream_enable_rtp_timeout(strm, call->rtp_timeout_ms); + content = sdp_media_rattr(stream_sdpmedia(strm), "content"); + if(0!=str_cmp(content, "slides")) + stream_enable_rtp_timeout(strm, call->rtp_timeout_ms); } } @@ -1887,6 +2065,19 @@ static void sipsess_estab_handler(const struct sip_msg *msg, void *arg) call_event_handler(call, CALL_EVENT_ESTABLISHED, "%s", call->peer_uri); } +static void call_handle_info_req(struct call *call, const struct sip_msg *req) +{ + struct pl body; + + pl_set_mbuf(&body, req->mb); + + /* Poor-mans XML parsing */ + if (0 == re_regex(body.p, body.l, "picture_fast_update")) { + debug("call: receive media control: fast_update=%d\n"); + video_encode_refresh(call->video); + } +} + static void dtmfend_handler(void *arg) { @@ -1897,8 +2088,9 @@ static void dtmfend_handler(void *arg) } -static void sipsess_send_info_handler(int err, const struct sip_msg *msg, - void *arg) +static void send_dtmf_info_handler(int err, + const struct sip_msg *msg, + void *arg) { (void)arg; @@ -1910,6 +2102,25 @@ static void sipsess_send_info_handler(int err, const struct sip_msg *msg, } +static void send_pfu_info_handler(int err, + const struct sip_msg *msg, + void *arg) +{ + struct call *call = arg; + struct config *cur_conf; + + if (err) + warning("call: sending PICTURE_FAST_UPDATE INFO failed (%m)", err); + else if (msg && msg->scode != 200) { + warning("call: sending PICTURE_FAST_UPDATE INFO failed (scode: %d)", + msg->scode); + } + + if (msg->scode == 200 || msg->scode == 408) + call->pfu_disabled = false; +} + + static void sipsess_info_handler(struct sip *sip, const struct sip_msg *msg, void *arg) { @@ -1949,6 +2160,11 @@ static void sipsess_info_handler(struct sip *sip, const struct sip_msg *msg, else if (!mbuf_get_left(msg->mb)) { (void)sip_reply(sip, msg, 200, "OK"); } + else if (msg_ctype_cmp(&msg->ctyp, + "application", "media_control+xml")) { + call_handle_info_req(call, msg); + (void)sip_reply(sip, msg, 200, "OK"); + } else { (void)sip_reply(sip, msg, 488, "Not Acceptable Here"); } @@ -2100,6 +2316,8 @@ static bool have_common_video_codecs(const struct call *call) struct vidcodec *vc; sc = sdp_media_rcodec(stream_sdpmedia(video_strm(call->video))); + if (!sc) + sc = sdp_media_rcodec(stream_sdpmedia(video_strm(call->slides))); if (!sc) return false; @@ -2164,7 +2382,8 @@ int call_accept(struct call *call, struct sipsess_sock *sess_sock, * See RFC 6157 */ if (!valid_addressfamily(call, audio_strm(call->audio)) || - !valid_addressfamily(call, video_strm(call->video))) { + !valid_addressfamily(call, video_strm(call->video)) || + !valid_addressfamily(call, video_strm(call->slides))){ sip_treply(NULL, uag_sip(), msg, 488, "Not Acceptable Here"); @@ -2440,9 +2659,37 @@ static int send_invite(struct call *call) } -static int send_dtmf_info(struct call *call, char key) +static int send_info(struct sipsess *sess, + const char* content_type, const char* content, + sip_resp_h *resph, + void *arg, const char *fmt, ...) { struct mbuf *body; + va_list ap; + body = mbuf_alloc(1024); + int err; + + va_start(ap, fmt); + (void) mbuf_printf(body, content, fmt, ap); + va_end(ap); + mbuf_set_pos(body, 0); + + err = sipsess_info(sess, content_type, body, + resph, arg); + + if (err) { + goto out; + } + + out: + mem_deref(body); + + return err; +} + + +static int send_dtmf_info(struct call *call, char key) +{ int err; if ((key < '0' || key > '9') && @@ -2452,20 +2699,14 @@ static int send_dtmf_info(struct call *call, char key) (key != '#')) return EINVAL; - body = mbuf_alloc(25); - mbuf_printf(body, "Signal=%c\r\nDuration=250\r\n", key); - mbuf_set_pos(body, 0); - - err = sipsess_info(call->sess, "application/dtmf-relay", body, - sipsess_send_info_handler, call); + err = send_info(call->sess, + "application/dtmf-relay", + "Signal=%c\r\nDuration=250\r\n", + send_dtmf_info_handler, + call, &key); if (err) { warning("call: sipsess_info for DTMF failed (%m)\n", err); - goto out; } - - out: - mem_deref(body); - return err; } @@ -2539,7 +2780,7 @@ struct audio *call_audio(const struct call *call) /** - * Get the video object for the current call + * Get the main video object for the current call * * @param call Call object * @@ -2551,6 +2792,19 @@ struct video *call_video(const struct call *call) } +/** + * Get the slides video object for the current call + * + * @param call Call object + * + * @return Video object + */ +struct video *call_slides(const struct call *call) +{ + return call ? call->slides : NULL; +} + + /** * Get the list of media streams for the current call * diff --git a/src/config.c b/src/config.c index 6d2eaf9c70..b5413fb9b1 100644 --- a/src/config.c +++ b/src/config.c @@ -71,7 +71,18 @@ static struct config core_config = { 101 }, - /** Video */ + /** Video main */ + { + "", "", + "", "", + 640, 480, + 1000000, + 30, + true, + VID_FMT_YUV420P, + }, + + /** Video slides */ { "", "", "", "", @@ -427,6 +438,8 @@ int config_parse_conf(struct config *cfg, const struct conf *conf) if (0 == conf_get(conf, "filter_registrar", &pl)) decode_sip_transports(&cfg->sip.reg_filt, &pl); + (void)conf_get_bool(conf, "sip_media_control", + &cfg->sip.media_control); /* Call */ (void)conf_get_u32(conf, "call_local_timeout", @@ -509,6 +522,28 @@ int config_parse_conf(struct config *cfg, const struct conf *conf) conf_get_vidfmt(conf, "videnc_format", &cfg->video.enc_fmt); + /* Slides */ + (void)conf_get_csv(conf, "slides_source", + cfg->slides.src_mod, + sizeof(cfg->slides.src_mod), + cfg->slides.src_dev, + sizeof(cfg->slides.src_dev)); + (void)conf_get_csv(conf, "slides_display", + cfg->slides.disp_mod, + sizeof(cfg->slides.disp_mod), + cfg->slides.disp_dev, + sizeof(cfg->slides.disp_dev)); + if (0 == conf_get_vidsz(conf, "slides_size", &size)) { + cfg->slides.width = size.w; + cfg->slides.height = size.h; + } + (void)conf_get_u32(conf, "slides_bitrate", &cfg->slides.bitrate); + (void)conf_get_float(conf, "slides_fps", &cfg->slides.fps); + (void)conf_get_bool(conf, "slides_fullscreen", + &cfg->slides.fullscreen); + + conf_get_vidfmt(conf, "slidenc_format", &cfg->slides.enc_fmt); + /* AVT - Audio/Video Transport */ if (0 == conf_get_u32(conf, "rtp_tos", &v)) cfg->avt.rtp_tos = v; @@ -575,6 +610,11 @@ int config_parse_conf(struct config *cfg, const struct conf *conf) warning("unsupported af (%r)\n", &pl); } } + /* BFCP */ + (void)conf_get_str(conf, "bfcp_proto", cfg->bfcp.proto, + sizeof(cfg->bfcp.proto)); + (void)conf_get_str(conf, "bfcp_floorctrl", cfg->bfcp.floorctrl, + sizeof(cfg->bfcp.floorctrl)); return err; } @@ -692,6 +732,26 @@ int config_print(struct re_printf *pf, const struct config *cfg) if (err) return err; + err = re_hprintf(pf, + "# Slides\n" + "slides_source\t\t%s,%s\n" + "#slides_source\t\tavformat,rtmp://127.0.0.1/app/foo\n" + "slide_display\t\t%s,%s\n" + "slides_size\t\t\"%ux%u\"\n" + "slides_bitrate\t\t%u\n" + "slides_fps\t\t%.2f\n" + "slides_fullscreen\t%s\n" + "slidenc_format\t\t%s\n" + "\n", + cfg->slides.src_mod, cfg->slides.src_dev, + cfg->slides.disp_mod, cfg->slides.disp_dev, + cfg->slides.width, cfg->slides.height, + cfg->slides.bitrate, cfg->slides.fps, + cfg->slides.fullscreen ? "yes" : "no", + vidfmt_name(cfg->slides.enc_fmt)); + if (err) + return err; + err = re_hprintf(pf, "# AVT\n" "rtp_tos\t\t\t%u\n" @@ -712,6 +772,13 @@ int config_print(struct re_printf *pf, const struct config *cfg) "net_af\t\t\t%s\n" "\n", + cfg->slides.src_mod, cfg->slides.src_dev, + cfg->slides.disp_mod, cfg->slides.disp_dev, + cfg->slides.width, cfg->slides.height, + cfg->slides.bitrate, cfg->slides.fps, + cfg->slides.fullscreen ? "yes" : "no", + vidfmt_name(cfg->slides.enc_fmt), + cfg->avt.rtp_tos, cfg->avt.rtpv_tos, range_print, &cfg->avt.rtp_ports, @@ -727,6 +794,9 @@ int config_print(struct re_printf *pf, const struct config *cfg) cfg->net.ifname, net_af_str(cfg->net.af) + + cfg->bfcp.proto, + cfg->bfcp.floorctrl ); return err; @@ -940,6 +1010,22 @@ static int core_config_template(struct re_printf *pf, const struct config *cfg) cfg->video.bitrate, cfg->video.fps, vidfmt_name(cfg->video.enc_fmt)); + err |= re_hprintf(pf, + "\n# Slides\n" + "#slides_source\t\t%s\n" + "#slides_display\t\t%s\n" + "slides_size\t\t%dx%d\n" + "slides_bitrate\t\t%u\n" + "slides_fps\t\t%.2f\n" + "slides_fullscreen\tno\n" + "slides_format\t\t%s\n" + , + default_video_device(), + default_video_display(), + cfg->slides.width, cfg->slides.height, + cfg->slides.bitrate, cfg->slides.fps, + vidfmt_name(cfg->slides.enc_fmt)); + err |= re_hprintf(pf, "\n# AVT - Audio/Video Transport\n" "rtp_tos\t\t\t184\n" @@ -975,6 +1061,11 @@ static int core_config_template(struct re_printf *pf, const struct config *cfg) cfg->avt.video.jbuf_del.max, default_interface_print, NULL); + err |= re_hprintf(pf, + "\n# BFCP\n" + "#bfcp_proto\t\tudp\n" + "#bfcp_floorctrl\t\ts-only\n"); + return err; } @@ -1204,6 +1295,7 @@ int config_write_template(const char *file, const struct config *cfg) #else (void)re_fprintf(f, "#module\t\t\t" "v4l2" MOD_EXT "\n"); #endif + (void)re_fprintf(f, "#module\t\t\t" "x11grab" MOD_EXT "\n"); (void)re_fprintf(f, "#module\t\t\t" "vidbridge" MOD_EXT "\n"); (void)re_fprintf(f, "\n# Video display modules\n"); @@ -1262,6 +1354,10 @@ int config_write_template(const char *file, const struct config *cfg) (void)re_fprintf(f, "# Module parameters\n"); (void)re_fprintf(f, "\n"); + (void)re_fprintf(f, "# SRTP parameters\n"); + (void)re_fprintf(f, "#preferred_crypto_suite\tAES_CM_128_HMAC_SHA1_80\n"); + (void)re_fprintf(f, "\n"); + (void)re_fprintf(f, "# DTLS SRTP parameters\n"); (void)re_fprintf(f, "#dtls_srtp_use_ec\tprime256v1\n"); (void)re_fprintf(f, "\n"); diff --git a/src/core.h b/src/core.h index abf38200ac..30eb480154 100644 --- a/src/core.h +++ b/src/core.h @@ -147,6 +147,15 @@ double aurecv_level(const struct audio_recv *ar); int aurecv_debug(struct re_printf *pf, const struct audio_recv *ar); int aurecv_print_pipeline(struct re_printf *pf, const struct audio_recv *ar); +/* + * BFCP + */ + +struct bfcp; +int bfcp_alloc(struct bfcp **bfcpp, struct sdp_session *sdp_sess, + const struct config_bfcp *bfcp_cfg, bool offerer, + const struct mnat *mnat, struct mnat_sess *mnat_sess); +int bfcp_start(struct bfcp *bfcp); /* * Call Control @@ -334,6 +343,8 @@ struct rtp_sock *stream_rtp_sock(const struct stream *strm); const struct sa *stream_raddr(const struct stream *strm); const char *stream_mid(const struct stream *strm); uint8_t stream_generate_extmap_id(struct stream *strm); +uint64_t stream_rx_ts_last(const struct stream *strm); + /* Send */ void stream_update_encoder(struct stream *s, int pt_enc); diff --git a/src/stream.c b/src/stream.c index 685cb5f1f9..72db75b98a 100644 --- a/src/stream.c +++ b/src/stream.c @@ -91,7 +91,7 @@ struct stream { }; -static void print_rtp_stats(const struct stream *s) +void print_rtp_stats(const struct stream *s) { uint32_t tx_n_packets = metric_n_packets(s->tx.metric); uint32_t rx_n_packets = metric_n_packets(rtprecv_metric(s->rx)); @@ -284,6 +284,9 @@ static void stream_close(struct stream *strm, int err) strm->terminated = true; stream_enable(strm, false); strm->errorh = NULL; + strm->rx.rtp_estab = false; + + jbuf_flush(strm->rx.jbuf); strm->rx = mem_deref(strm->rx); if (errorh) @@ -311,7 +314,7 @@ static void check_rtp_handler(void *arg) /* We are in sendrecv mode, check when the last RTP packet * was received. */ - if (sdp_media_dir(strm->sdp) == SDP_SENDRECV) { + if (sdp_media_dir(strm->sdp) & SDP_RECVONLY) { diff_ms = (int)(now - ts_last); @@ -1557,6 +1560,17 @@ const struct sa *stream_raddr(const struct stream *strm) } +uint64_t stream_rx_ts_last(const struct stream *strm) +{ + if (!strm) + return 0; + if (!strm->rx.ts_last) + return 0; + + return strm->rx.ts_last; +} + + enum media_type stream_type(const struct stream *strm) { return strm ? strm->type : (enum media_type)-1; diff --git a/src/video.c b/src/video.c index ddeb639025..fe713cae02 100644 --- a/src/video.c +++ b/src/video.c @@ -164,6 +164,7 @@ struct video { struct vrx vrx; /**< Receive/decoder direction */ struct tmr tmr; /**< Timer for frame-rate estimation */ char *peer; /**< Peer URI */ + char *content; /**< Content type */ bool nack_pli; /**< Send NACK/PLI to peer */ video_err_h *errh; /**< Error handler */ void *arg; /**< Error handler argument */ @@ -311,6 +312,7 @@ static void video_destructor(void *arg) tmr_cancel(&v->tmr); mem_deref(v->strm); mem_deref(v->peer); + mem_deref(v->content); } @@ -656,6 +658,12 @@ static void picup_tmr_handler(void *arg) static void send_fir(struct stream *s, bool pli) { + struct call *cur_call; + struct ua *local_ua ; + char *local_uri, *label; + char* content= NULL; + bool sip_media_control=false; + struct config *cur_conf; int err; if (pli) { @@ -664,14 +672,45 @@ static void send_fir(struct stream *s, bool pli) err = stream_ssrc_rx(s, &ssrc); if (!err) err = rtcp_send_pli(stream_rtp_sock(s), ssrc); + if (err) + warning("video: failed to send PLI %m\n", err); } - else - err = rtcp_send_fir(stream_rtp_sock(s), - rtp_sess_ssrc(stream_rtp_sock(s))); + else{ + cur_conf = conf_config(); + if (cur_conf) + sip_media_control = cur_conf->sip.media_control; + + if(sip_media_control) { + local_uri = stream_cname(s); + if (local_uri) + local_ua = uag_find_requri(local_uri); + + if (local_ua) + cur_call = ua_call(local_ua); + + if (cur_call){ + content = sdp_media_rattr(stream_sdpmedia(s), + "content"); + label = sdp_media_rattr(stream_sdpmedia(s), + "label"); + err = call_send_pfu(cur_call, content, + label); + } - if (err) { - warning("video: failed to send RTCP %s: %m\n", - pli ? "PLI" : "FIR", err); + if (err){ + info("video: failed to send " + "picture_fast_update\n"); + } + else + return; + } + /* Try to send RTCP FIR at last*/ + err = rtcp_send_fir( + stream_rtp_sock(s), + rtp_sess_ssrc(stream_rtp_sock(s))); + if (err) + warning("video: failed to send FIR %m\n", + err); } } @@ -1088,7 +1127,10 @@ int video_alloc(struct video **vp, struct list *streaml, MAGIC_INIT(v); - v->cfg = cfg->video; + if (NULL != strcasestr(content, "slides")) + v->cfg = cfg->slides; + else + v->cfg = cfg->video; err = vtx_alloc(&v->vtx, v); err |= vrx_alloc(&v->vrx, v); @@ -1133,6 +1175,7 @@ int video_alloc(struct video **vp, struct list *streaml, if (content) { err |= sdp_media_set_lattr(stream_sdpmedia(v->strm), true, "content", "%s", content); + err |= str_dup(&v->content, content); } if (err) @@ -1200,6 +1243,7 @@ static void vidisp_resize_handler(const struct vidsz *sz, void *arg) static int set_vidisp(struct vrx *vrx) { struct vidisp *vd; + char *content; int err; vrx->vidisp = mem_deref(vrx->vidisp); @@ -1212,6 +1256,8 @@ static int set_vidisp(struct vrx *vrx) if (!vd) return ENOENT; + str_ncpy(&vrx->vidisp_prm.content, vrx->video->content, + sizeof(vrx->video->content)); err = vd->alloch(&vrx->vidisp, vd, &vrx->vidisp_prm, vrx->device, vidisp_resize_handler, vrx); if (err) @@ -1789,6 +1835,7 @@ int video_debug(struct re_printf *pf, const struct video *v) err |= vrx_print_pipeline(pf, vrx); err |= stream_debug(pf, v->strm); + print_rtp_stats(v->strm); return err; }