static int update_media(struct call *call) { const struct sdp_format *sc; struct le *le; int err = 0; /* media attributes */ audio_sdp_attr_decode(call->audio); #ifdef USE_VIDEO if (call->video) video_sdp_attr_decode(call->video); #endif /* Update each stream */ FOREACH_STREAM { stream_update(le->data); } if (call->acc->mnat && call->acc->mnat->updateh && call->mnats) err = call->acc->mnat->updateh(call->mnats); sc = sdp_media_rformat(stream_sdpmedia(audio_strm(call->audio)), NULL); if (sc) { struct aucodec *ac = sc->data; if (ac) { err = audio_decoder_set(call->audio, sc->data, sc->pt, sc->params); err |= audio_encoder_set(call->audio, sc->data, sc->pt, sc->params); } else { info("no common audio-codecs..\n"); } } else { info("audio stream is disabled..\n"); } #ifdef USE_VIDEO sc = sdp_media_rformat(stream_sdpmedia(video_strm(call->video)), NULL); if (sc) { err = video_encoder_set(call->video, sc->data, sc->pt, sc->params); if (err) { warning("call: video stream error: %m\n", err); } } else if (call->video) { info("video stream is disabled..\n"); } #endif return err; }
/** * Check if the current call has an active audio stream * * @param call Call object * * @return True if active stream, otherwise false */ bool call_has_audio(const struct call *call) { if (!call) return false; return sdp_media_has_media(stream_sdpmedia(audio_strm(call->audio))); }
static void check_telev(struct audio *a, struct autx *tx) { const struct sdp_format *fmt; bool marker = false; int err; tx->mb->pos = tx->mb->end = STREAM_PRESZ; err = telev_poll(a->telev, &marker, tx->mb); if (err) return; if (marker) tx->ts_tel = tx->ts; fmt = sdp_media_rformat(stream_sdpmedia(audio_strm(a)), telev_rtpfmt); if (!fmt) return; tx->mb->pos = STREAM_PRESZ; err = stream_send(a->strm, marker, fmt->pt, tx->ts_tel, tx->mb); if (err) { DEBUG_WARNING("telev: stream_send %m\n", err); } }
void audio_sdp_attr_decode(struct audio *a) { const char *attr; if (!a) return; /* This is probably only meaningful for audio data, but may be used with other media types if it makes sense. */ attr = sdp_media_rattr(stream_sdpmedia(a->strm), "ptime"); if (attr) { struct autx *tx = &a->tx; uint32_t ptime_tx = atoi(attr); if (ptime_tx && ptime_tx != a->tx.ptime) { info("audio: peer changed ptime_tx %u -> %u\n", a->tx.ptime, ptime_tx); tx->ptime = ptime_tx; if (tx->ac) { tx->psize = 2 * get_framesize(tx->ac, ptime_tx); } } } }
/** * Check if the current call has an active video stream * * @param call Call object * * @return True if active stream, otherwise false */ bool call_has_video(const struct call *call) { if (!call) return false; #ifdef USE_VIDEO return sdp_media_has_media(stream_sdpmedia(video_strm(call->video))); #else return false; #endif }
static bool have_common_audio_codecs(const struct call *call) { const struct sdp_format *sc; struct aucodec *ac; sc = sdp_media_rformat(stream_sdpmedia(audio_strm(call->audio)), NULL); if (!sc) return false; ac = sc->data; /* note: this will exclude telephone-event */ return ac != NULL; }
void audio_sdp_attr_decode(struct audio *a) { const char *attr; if (!a) return; /* This is probably only meaningful for audio data, but may be used with other media types if it makes sense. */ attr = sdp_media_rattr(stream_sdpmedia(a->strm), "ptime"); if (attr) audio_ptime_tx_set(a, atoi(attr)); }
static bool have_common_audio_codecs(const struct call *call) { const struct sdp_format *sc; struct aucodec *ac; sc = sdp_media_rformat(stream_sdpmedia(audio_strm(call->audio)), NULL); if (!sc) return false; ac = sc->data; return ac != NULL; }
static int add_telev_codec(struct audio *a) { struct sdp_media *m = stream_sdpmedia(audio_strm(a)); struct sdp_format *sf; int err; /* Use payload-type 101 if available, for CiscoGW interop */ err = sdp_format_add(&sf, m, false, (!sdp_media_lformat(m, 101)) ? "101" : NULL, telev_rtpfmt, TELEV_SRATE, 1, NULL, NULL, NULL, false, "0-15"); if (err) return err; a->rx.pt_tel = sf->pt; return err; }
/** * Use the next audio encoder in the local list of negotiated codecs * * @param audio Audio object */ void audio_encoder_cycle(struct audio *audio) { const struct sdp_format *rc = NULL; if (!audio) return; rc = sdp_media_format_cycle(stream_sdpmedia(audio_strm(audio))); if (!rc) { info("audio: encoder cycle: no remote codec found\n"); return; } (void)audio_encoder_set(audio, rc->data, rc->pt, rc->params); }
static int pt_handler(struct audio *a, uint8_t pt_old, uint8_t pt_new, void *user_data) { const struct sdp_format *lc; lc = sdp_media_lformat(stream_sdpmedia(a->strm), pt_new); if (!lc) return ENOENT; if (pt_old != (uint8_t)-1) { re_printf("Audio decoder changed payload" " %u -> %u\n", pt_old, pt_new); } a->rx.pt = pt_new; return audio_decoder_set(a, lc->data, lc->pt, lc->params, user_data); }
/* Handle incoming stream data from the network */ static void stream_recv_handler(const struct rtp_header *hdr, struct mbuf *mb, void *arg) { struct audio *a = arg; struct aurx *rx = &a->rx; int err; if (!mb) goto out; /* Telephone event? */ if (hdr->pt != rx->pt) { const struct sdp_format *fmt; fmt = sdp_media_lformat(stream_sdpmedia(a->strm), hdr->pt); if (fmt && !str_casecmp(fmt->name, "telephone-event")) { handle_telev(a, mb); return; } } /* Comfort Noise (CN) as of RFC 3389 */ if (PT_CN == hdr->pt) return; /* Audio payload-type changed? */ /* XXX: this logic should be moved to stream.c */ if (hdr->pt != rx->pt) { err = pt_handler(a, rx->pt, hdr->pt); if (err) return; } out: (void)aurx_stream_decode(&a->rx, mb); }
int call_accept(struct call *call, struct sipsess_sock *sess_sock, const struct sip_msg *msg) { bool got_offer; int err; if (!call || !msg) return EINVAL; call->outgoing = false; got_offer = (mbuf_get_left(msg->mb) > 0); err = pl_strdup(&call->peer_uri, &msg->from.auri); if (err) return err; if (pl_isset(&msg->from.dname)) { err = pl_strdup(&call->peer_name, &msg->from.dname); if (err) return err; } if (got_offer) { struct sdp_media *m; const struct sa *raddr; err = sdp_decode(call->sdp, msg->mb, true); if (err) return err; call->got_offer = true; /* * Each media description in the SDP answer MUST * use the same network type as the corresponding * media description in the offer. * * See RFC 6157 */ m = stream_sdpmedia(audio_strm(call->audio)); raddr = sdp_media_raddr(m); if (sa_af(raddr) != call->af) { info("call: incompatible address-family" " (local=%s, remote=%s)\n", net_af2name(call->af), net_af2name(sa_af(raddr))); sip_treply(NULL, uag_sip(), msg, 488, "Not Acceptable Here"); call_event_handler(call, CALL_EVENT_CLOSED, "Wrong address family"); return 0; } /* Check if we have any common audio codecs, after * the SDP offer has been parsed */ if (!have_common_audio_codecs(call)) { info("call: no common audio codecs - rejected\n"); sip_treply(NULL, uag_sip(), msg, 488, "Not Acceptable Here"); call_event_handler(call, CALL_EVENT_CLOSED, "No audio codecs"); return 0; } } err = sipsess_accept(&call->sess, sess_sock, msg, 180, "Ringing", ua_cuser(call->ua), "application/sdp", NULL, auth_handler, call->acc, true, sipsess_offer_handler, sipsess_answer_handler, sipsess_estab_handler, sipsess_info_handler, sipsess_refer_handler, sipsess_close_handler, call, "Allow: %s\r\n", uag_allowed_methods()); if (err) { warning("call: sipsess_accept: %m\n", err); return err; } set_state(call, STATE_INCOMING); /* New call */ tmr_start(&call->tmr_inv, LOCAL_TIMEOUT*1000, invite_timeout, call); if (!call->acc->mnat) call_event_handler(call, CALL_EVENT_INCOMING, call->peer_uri); return err; }
static void call_stream_start(struct call *call, bool active) { const struct sdp_format *sc; int err; /* Audio Stream */ sc = sdp_media_rformat(stream_sdpmedia(audio_strm(call->audio)), NULL); if (sc) { struct aucodec *ac = sc->data; if (ac) { err = audio_encoder_set(call->audio, sc->data, sc->pt, sc->params); err |= audio_decoder_set(call->audio, sc->data, sc->pt, sc->params); if (!err) { err = audio_start(call->audio); } if (err) { warning("call: audio stream error: %m\n", err); } } else { info("call: no common audio-codecs..\n"); } } else { info("call: audio stream is disabled..\n"); } #ifdef USE_VIDEO /* Video Stream */ sc = sdp_media_rformat(stream_sdpmedia(video_strm(call->video)), NULL); if (sc) { err = video_encoder_set(call->video, sc->data, sc->pt, sc->params); err |= video_decoder_set(call->video, sc->data, sc->pt, sc->rparams); if (!err) { err = video_start(call->video, call->peer_uri); } if (err) { warning("call: video stream error: %m\n", err); } } else if (call->video) { info("call: video stream is disabled..\n"); } if (call->bfcp) { err = bfcp_start(call->bfcp); if (err) { warning("call: could not start BFCP: %m\n", err); } } #endif if (active) { struct le *le; tmr_cancel(&call->tmr_inv); call->time_start = time(NULL); FOREACH_STREAM { stream_reset(le->data); } } }
int audio_alloc(struct audio **ap, const struct config *cfg, struct call *call, struct sdp_session *sdp_sess, int label, const struct mnat *mnat, struct mnat_sess *mnat_sess, const struct menc *menc, struct menc_sess *menc_sess, uint32_t ptime, const struct list *aucodecl, audio_event_h *eventh, audio_err_h *errh, void *arg) { struct audio *a; struct autx *tx; struct aurx *rx; struct le *le; int err; if (!ap || !cfg) return EINVAL; a = mem_zalloc(sizeof(*a), audio_destructor); if (!a) return ENOMEM; MAGIC_INIT(a); a->cfg = cfg->audio; tx = &a->tx; rx = &a->rx; err = stream_alloc(&a->strm, &cfg->avt, call, sdp_sess, "audio", label, mnat, mnat_sess, menc, menc_sess, stream_recv_handler, NULL, a); if (err) goto out; stream_set_bw(a->strm, AUDIO_BANDWIDTH); err = sdp_media_set_lattr(stream_sdpmedia(a->strm), true, "ptime", "%u", ptime); if (err) goto out; /* Audio codecs */ for (le = list_head(aucodecl); le; le = le->next) { err = add_audio_codec(a, stream_sdpmedia(a->strm), le->data); if (err) goto out; } tx->mb = mbuf_alloc(STREAM_PRESZ + 4096); tx->sampv = mem_zalloc(AUDIO_SAMPSZ * 2, NULL); rx->sampv = mem_zalloc(AUDIO_SAMPSZ * 2, NULL); if (!tx->mb || !tx->sampv || !rx->sampv) { err = ENOMEM; goto out; } err = telev_alloc(&a->telev, TELEV_PTIME); if (err) goto out; err = add_telev_codec(a); if (err) goto out; tx->ptime = ptime; tx->ts = 160; tx->marker = true; rx->pt = -1; rx->ptime = ptime; a->eventh = eventh; a->errh = errh; a->arg = arg; if (a->cfg.txmode == AUDIO_MODE_TMR) tmr_init(&tx->u.tmr); out: if (err) mem_deref(a); else *ap = a; return err; }