diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/audio.c | 166 | ||||
-rw-r--r-- | src/config.c | 6 | ||||
-rw-r--r-- | src/core.h | 7 | ||||
-rw-r--r-- | src/stream.c | 67 | ||||
-rw-r--r-- | src/video.c | 5 |
5 files changed, 238 insertions, 13 deletions
diff --git a/src/audio.c b/src/audio.c index 820ff1b..1d42065 100644 --- a/src/audio.c +++ b/src/audio.c @@ -134,6 +134,8 @@ struct aurx { int16_t *sampv_rs; /**< Sample buffer for resampler */ uint32_t ptime; /**< Packet time for receiving */ int pt; /**< Payload type for incoming RTP */ + double level_last; + bool level_set; }; @@ -146,12 +148,18 @@ struct audio { struct telev *telev; /**< Telephony events */ struct config_audio cfg; /**< Audio configuration */ bool started; /**< Stream is started flag */ + bool level_enabled; /**< Audio level RTP ext. enabled */ + unsigned extmap_aulevel; /**< ID Range 1-14 inclusive */ audio_event_h *eventh; /**< Event handler */ audio_err_h *errh; /**< Audio error handler */ void *arg; /**< Handler argument */ }; +/* RFC 6464 */ +static const char *uri_aulevel = "urn:ietf:params:rtp-hdrext:ssrc-audio-level"; + + static void stop_tx(struct autx *tx, struct audio *a) { if (!tx || !a) @@ -307,6 +315,29 @@ static int add_audio_codec(struct audio *a, struct sdp_media *m, } +static int append_rtpext(struct audio *au, struct mbuf *mb, + int16_t *sampv, size_t sampc) +{ + uint8_t data[1]; + double level; + int err; + + /* audio level must be calculated from the audio samples that + * are actually sent on the network. */ + level = aulevel_calc_dbov(sampv, sampc); + + data[0] = (int)-level & 0x7f; + + err = rtpext_encode(mb, au->extmap_aulevel, 1, data); + if (err) { + warning("audio: rtpext_encode failed (%m)\n", err); + return err; + } + + return err; +} + + /** * Encoder audio and send via stream * @@ -323,12 +354,36 @@ static void encode_rtp_send(struct audio *a, struct autx *tx, size_t frame_size; /* number of samples per channel */ size_t sampc_rtp; size_t len; + size_t ext_len = 0; int err; if (!tx->ac || !tx->ac->ench) return; tx->mb->pos = tx->mb->end = STREAM_PRESZ; + + if (a->level_enabled) { + + /* skip the extension header */ + tx->mb->pos += RTPEXT_HDR_SIZE; + + err = append_rtpext(a, tx->mb, sampv, sampc); + if (err) + return; + + ext_len = tx->mb->pos - STREAM_PRESZ; + + /* write the Extension header at the beginning */ + tx->mb->pos = STREAM_PRESZ; + + err = rtpext_hdr_encode(tx->mb, ext_len - RTPEXT_HDR_SIZE); + if (err) + return; + + tx->mb->pos = STREAM_PRESZ + ext_len; + tx->mb->end = STREAM_PRESZ + ext_len; + } + len = mbuf_get_space(tx->mb); err = tx->ac->ench(tx->enc, mbuf_buf(tx->mb), &len, sampv, sampc); @@ -344,11 +399,12 @@ static void encode_rtp_send(struct audio *a, struct autx *tx, } tx->mb->pos = STREAM_PRESZ; - tx->mb->end = STREAM_PRESZ + len; + tx->mb->end = STREAM_PRESZ + ext_len + len; if (mbuf_get_left(tx->mb)) { + if (len) { - err = stream_send(a->strm, tx->marker, -1, + err = stream_send(a->strm, ext_len!=0, tx->marker, -1, tx->ts, tx->mb); if (err) goto out; @@ -438,7 +494,7 @@ static void check_telev(struct audio *a, struct autx *tx) return; tx->mb->pos = STREAM_PRESZ; - err = stream_send(a->strm, marker, fmt->pt, tx->ts_tel, tx->mb); + err = stream_send(a->strm, false, marker, fmt->pt, tx->ts_tel, tx->mb); if (err) { warning("audio: telev: stream_send %m\n", err); } @@ -617,10 +673,12 @@ static int aurx_stream_decode(struct aurx *rx, struct mbuf *mb) /* Handle incoming stream data from the network */ static void stream_recv_handler(const struct rtp_header *hdr, + struct rtpext *extv, size_t extc, struct mbuf *mb, void *arg) { struct audio *a = arg; struct aurx *rx = &a->rx; + size_t i; int err; if (!mb) @@ -651,6 +709,20 @@ static void stream_recv_handler(const struct rtp_header *hdr, return; } + /* RFC 5285 -- A General Mechanism for RTP Header Extensions */ + for (i=0; i<extc; i++) { + + if (extv[i].id == a->extmap_aulevel) { + + a->rx.level_last = -(double)(extv[i].data[0] & 0x7f); + a->rx.level_set = true; + } + else { + info("audio: rtp header ext ignored (id=%u)\n", + extv[i].id); + } + } + out: (void)aurx_stream_decode(&a->rx, mb); } @@ -718,6 +790,18 @@ int audio_alloc(struct audio **ap, const struct config *cfg, if (err) goto out; + if (cfg->audio.level && offerer) { + + a->extmap_aulevel = 1; + + err = sdp_media_set_lattr(stream_sdpmedia(a->strm), true, + "extmap", + "%u %s", + a->extmap_aulevel, uri_aulevel); + 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); @@ -1377,6 +1461,45 @@ bool audio_ismuted(const struct audio *a) } +static bool extmap_handler(const char *name, const char *value, void *arg) +{ + struct audio *au = arg; + struct sdp_extmap extmap; + int err; + (void)name; + + err = sdp_extmap_decode(&extmap, value); + if (err) { + warning("audio: sdp_extmap_decode error (%m)\n", err); + return false; + } + + if (0 == pl_strcasecmp(&extmap.name, uri_aulevel)) { + + if (extmap.id < RTPEXT_ID_MIN || extmap.id > RTPEXT_ID_MAX) { + warning("audio: extmap id out of range (%u)\n", + extmap.id); + return false; + } + + au->extmap_aulevel = extmap.id; + + err = sdp_media_set_lattr(stream_sdpmedia(au->strm), true, + "extmap", + "%u %s", + au->extmap_aulevel, + uri_aulevel); + if (err) + return false; + + au->level_enabled = true; + info("audio: client-to-mixer audio levels enabled\n"); + } + + return false; +} + + void audio_sdp_attr_decode(struct audio *a) { const char *attr; @@ -1404,6 +1527,39 @@ void audio_sdp_attr_decode(struct audio *a) } } } + + /* Client-to-Mixer Audio Level Indication */ + if (a->cfg.level) { + sdp_media_rattr_apply(stream_sdpmedia(a->strm), + "extmap", + extmap_handler, a); + } +} + + +/** + * Get the last value of the audio level from incoming RTP packets + * + * @param au Audio object + * @param levelp Pointer to where to write audio level value + * + * @return 0 if success, otherwise errorcode + */ +int audio_level_get(const struct audio *au, double *levelp) +{ + if (!au) + return EINVAL; + + if (!au->level_enabled) + return ENOTSUP; + + if (!au->rx.level_set) + return ENOENT; + + if (levelp) + *levelp = au->rx.level_last; + + return 0; } @@ -1440,6 +1596,10 @@ int audio_debug(struct re_printf *pf, const struct audio *a) aucodec_print, rx->ac, aubuf_debug, rx->aubuf, rx->ptime, rx->pt); + if (rx->level_set) { + err |= re_hprintf(pf, " level %.3f dBov\n", + rx->level_last); + } err |= re_hprintf(pf, " %H" diff --git a/src/config.c b/src/config.c index cd6430a..9f2de5d 100644 --- a/src/config.c +++ b/src/config.c @@ -50,6 +50,7 @@ static struct config core_config = { 0, false, AUDIO_MODE_POLL, + false, }, #ifdef USE_VIDEO @@ -195,6 +196,8 @@ int config_parse_conf(struct config *cfg, const struct conf *conf) 0 == conf_get(conf, "audio_player", &ap)) cfg->audio.src_first = as.p < ap.p; + (void)conf_get_bool(conf, "audio_level", &cfg->audio.level); + #ifdef USE_VIDEO /* Video */ (void)conf_get_csv(conf, "video_source", @@ -277,6 +280,7 @@ int config_print(struct re_printf *pf, const struct config *cfg) "ausrc_srate\t\t%u\n" "auplay_channels\t\t%u\n" "ausrc_channels\t\t%u\n" + "audio_level\t\t%s\n" "\n" #ifdef USE_VIDEO "# Video\n" @@ -320,6 +324,7 @@ int config_print(struct re_printf *pf, const struct config *cfg) range_print, &cfg->audio.channels, cfg->audio.srate_play, cfg->audio.srate_src, cfg->audio.channels_play, cfg->audio.channels_src, + cfg->audio.level ? "yes" : "no", #ifdef USE_VIDEO cfg->video.src_mod, cfg->video.src_dev, @@ -447,6 +452,7 @@ static int core_config_template(struct re_printf *pf, const struct config *cfg) "#auplay_srate\t\t48000\n" "#ausrc_channels\t\t0\n" "#auplay_channels\t\t0\n" + "audio_level\t\tno\n" , poll_method_name(poll_method_best()), cfg->call.local_timeout, @@ -328,8 +328,9 @@ struct rtp_header; enum {STREAM_PRESZ = 4+12}; /* same as RTP_HEADER_SIZE */ -typedef void (stream_rtp_h)(const struct rtp_header *hdr, struct mbuf *mb, - void *arg); +typedef void (stream_rtp_h)(const struct rtp_header *hdr, + struct rtpext *extv, size_t extc, + struct mbuf *mb, void *arg); typedef void (stream_rtcp_h)(struct rtcp_msg *msg, void *arg); typedef void (stream_error_h)(struct stream *strm, int err, void *arg); @@ -377,7 +378,7 @@ int stream_alloc(struct stream **sp, const struct config_avt *cfg, const char *cname, stream_rtp_h *rtph, stream_rtcp_h *rtcph, void *arg); struct sdp_media *stream_sdpmedia(const struct stream *s); -int stream_send(struct stream *s, bool marker, int pt, uint32_t ts, +int stream_send(struct stream *s, bool ext, bool marker, int pt, uint32_t ts, struct mbuf *mb); void stream_update(struct stream *s); void stream_update_encoder(struct stream *s, int pt_enc); diff --git a/src/stream.c b/src/stream.c index acd4766..b9a0d6a 100644 --- a/src/stream.c +++ b/src/stream.c @@ -148,6 +148,61 @@ static void stream_destructor(void *arg) } +static void handle_rtp(struct stream *s, const struct rtp_header *hdr, + struct mbuf *mb) +{ + struct rtpext extv[8]; + size_t extc = 0; + + /* RFC 5285 -- A General Mechanism for RTP Header Extensions */ + if (hdr->ext && hdr->x.len && mb) { + + const size_t pos = mb->pos; + const size_t end = mb->end; + const size_t ext_stop = mb->pos; + size_t ext_len; + size_t i; + int err; + + if (hdr->x.type != RTPEXT_TYPE_MAGIC) { + info("stream: unknown ext type ignored (0x%04x)\n", + hdr->x.type); + goto handler; + } + + ext_len = hdr->x.len*sizeof(uint32_t); + if (mb->pos < ext_len) { + warning("stream: corrupt rtp packet," + " not enough space for rtpext of %zu bytes\n", + ext_len); + return; + } + + mb->pos = mb->pos - ext_len; + mb->end = ext_stop; + + for (i=0; i<ARRAY_SIZE(extv) && mbuf_get_left(mb); i++) { + + err = rtpext_decode(&extv[i], mb); + if (err) { + warning("stream: rtpext_decode failed (%m)\n", + err); + return; + } + } + + extc = i; + + mb->pos = pos; + mb->end = end; + } + + handler: + s->rtph(hdr, extv, extc, mb, s->arg); + +} + + static void rtp_recv(const struct sa *src, const struct rtp_header *hdr, struct mbuf *mb, void *arg) { @@ -204,17 +259,17 @@ static void rtp_recv(const struct sa *src, const struct rtp_header *hdr, s->jbuf_started = true; if (lostcalc(s, hdr2.seq) > 0) - s->rtph(hdr, NULL, s->arg); + handle_rtp(s, hdr, NULL); - s->rtph(&hdr2, mb2, s->arg); + handle_rtp(s, &hdr2, mb2); mem_deref(mb2); } else { if (lostcalc(s, hdr->seq) > 0) - s->rtph(hdr, NULL, s->arg); + handle_rtp(s, hdr, NULL); - s->rtph(hdr, mb, s->arg); + handle_rtp(s, hdr, mb); } } @@ -419,7 +474,7 @@ static void stream_start_keepalive(struct stream *s) } -int stream_send(struct stream *s, bool marker, int pt, uint32_t ts, +int stream_send(struct stream *s, bool ext, bool marker, int pt, uint32_t ts, struct mbuf *mb) { int err = 0; @@ -438,7 +493,7 @@ int stream_send(struct stream *s, bool marker, int pt, uint32_t ts, pt = s->pt_enc; if (pt >= 0) { - err = rtp_send(s->rtp, sdp_media_raddr(s->sdp), false, + err = rtp_send(s->rtp, sdp_media_raddr(s->sdp), ext, marker, pt, ts, mb); if (err) s->metric_tx.n_err++; diff --git a/src/video.c b/src/video.c index 9077a0c..a73ea34 100644 --- a/src/video.c +++ b/src/video.c @@ -255,7 +255,7 @@ static void vidqueue_poll(struct vtx *vtx, uint64_t jfs, uint64_t prev_jfs) sent += mbuf_get_left(qent->mb); - stream_send(vtx->video->strm, qent->marker, qent->pt, + stream_send(vtx->video->strm, false, qent->marker, qent->pt, qent->ts, qent->mb); le = le->next; @@ -654,10 +654,13 @@ static int pt_handler(struct video *v, uint8_t pt_old, uint8_t pt_new) /* Handle incoming stream data from the network */ static void stream_recv_handler(const struct rtp_header *hdr, + struct rtpext *extv, size_t extc, struct mbuf *mb, void *arg) { struct video *v = arg; int err; + (void)extv; + (void)extc; if (!mb) goto out; |