summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAlfred E. Heggestad <alfred.heggestad@gmail.com>2017-06-02 22:07:34 +0200
committerGitHub <noreply@github.com>2017-06-02 22:07:34 +0200
commit9c2b349424c49cd94e18c788e6fe9527d04be5d1 (patch)
treec295c3ec91f5ee51ff8bb482d9865e125fe1128c /src
parent0338f6dca9ed181c2e182c7bf8185c23ec880adb (diff)
add RTP Header extension for Client-to-Mixer Audio Level Indication (#264)
* add RTP Header extension for Client-to-Mixer Audio Level Indication https://tools.ietf.org/html/rfc6464 requires libre from this commit or later: https://github.com/creytiv/re/commit/1544a1e375c76a80084b411d21b0431f95e9cdfb * fix warnings * fix warnings * minor cleanup
Diffstat (limited to 'src')
-rw-r--r--src/audio.c166
-rw-r--r--src/config.c6
-rw-r--r--src/core.h7
-rw-r--r--src/stream.c67
-rw-r--r--src/video.c5
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,
diff --git a/src/core.h b/src/core.h
index 5dc5e9d..9c0c2ba 100644
--- a/src/core.h
+++ b/src/core.h
@@ -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;