207 lines
4.5 KiB
C
207 lines
4.5 KiB
C
/**
|
|
* @file rtcp.c Real-time Transport Control Protocol
|
|
*
|
|
* Copyright (C) 2010 Creytiv.com
|
|
*/
|
|
#include <string.h>
|
|
#include <re_types.h>
|
|
#include <re_fmt.h>
|
|
#include <re_mem.h>
|
|
#include <re_mbuf.h>
|
|
#include <re_list.h>
|
|
#include <re_sa.h>
|
|
#include <re_rtp.h>
|
|
#include "rtcp.h"
|
|
|
|
|
|
static int rtcp_quick_send(struct rtp_sock *rs, enum rtcp_type type,
|
|
uint32_t count, ...)
|
|
{
|
|
struct mbuf *mb;
|
|
va_list ap;
|
|
int err;
|
|
|
|
mb = mbuf_alloc(32);
|
|
if (!mb)
|
|
return ENOMEM;
|
|
|
|
mb->pos = RTCP_HEADROOM;
|
|
|
|
va_start(ap, count);
|
|
err = rtcp_vencode(mb, type, count, ap);
|
|
va_end(ap);
|
|
|
|
mb->pos = RTCP_HEADROOM;
|
|
|
|
if (!err)
|
|
err = rtcp_send(rs, mb);
|
|
|
|
mem_deref(mb);
|
|
|
|
return err;
|
|
}
|
|
|
|
|
|
int rtcp_send_app(struct rtp_sock *rs, const char name[4],
|
|
const uint8_t *data, size_t len)
|
|
{
|
|
return rtcp_quick_send(rs, RTCP_APP, 0, rtp_sess_ssrc(rs),
|
|
name, data, len);
|
|
}
|
|
|
|
|
|
/**
|
|
* Send a Full INTRA-frame Request (FIR) packet
|
|
*
|
|
* @param rs RTP Socket
|
|
* @param ssrc Synchronization source identifier for the sender of this packet
|
|
*
|
|
* @return 0 for success, otherwise errorcode
|
|
*/
|
|
int rtcp_send_fir(struct rtp_sock *rs, uint32_t ssrc)
|
|
{
|
|
return rtcp_quick_send(rs, RTCP_FIR, 0, ssrc);
|
|
}
|
|
|
|
|
|
int rtcp_send_nack(struct rtp_sock *rs, uint16_t fsn, uint16_t blp)
|
|
{
|
|
return rtcp_quick_send(rs, RTCP_NACK, 0, rtp_sess_ssrc(rs), fsn, blp);
|
|
}
|
|
|
|
|
|
int rtcp_send_pli(struct rtp_sock *rs, uint32_t fb_ssrc)
|
|
{
|
|
return rtcp_quick_send(rs, RTCP_PSFB, RTCP_PSFB_PLI,
|
|
rtp_sess_ssrc(rs), fb_ssrc, NULL, NULL);
|
|
}
|
|
|
|
|
|
const char *rtcp_type_name(enum rtcp_type type)
|
|
{
|
|
switch (type) {
|
|
|
|
case RTCP_FIR: return "FIR";
|
|
case RTCP_NACK: return "NACK";
|
|
case RTCP_SR: return "SR";
|
|
case RTCP_RR: return "RR";
|
|
case RTCP_SDES: return "SDES";
|
|
case RTCP_BYE: return "BYE";
|
|
case RTCP_APP: return "APP";
|
|
case RTCP_RTPFB: return "RTPFB";
|
|
case RTCP_PSFB: return "PSFB";
|
|
case RTCP_XR: return "XR";
|
|
case RTCP_AVB: return "AVB";
|
|
default: return "?";
|
|
}
|
|
}
|
|
|
|
|
|
const char *rtcp_sdes_name(enum rtcp_sdes_type sdes)
|
|
{
|
|
switch (sdes) {
|
|
|
|
case RTCP_SDES_END: return "END";
|
|
case RTCP_SDES_CNAME: return "CNAME";
|
|
case RTCP_SDES_NAME: return "NAME";
|
|
case RTCP_SDES_EMAIL: return "EMAIL";
|
|
case RTCP_SDES_PHONE: return "PHONE";
|
|
case RTCP_SDES_LOC: return "LOC";
|
|
case RTCP_SDES_TOOL: return "TOOL";
|
|
case RTCP_SDES_NOTE: return "NOTE";
|
|
case RTCP_SDES_PRIV: return "PRIV";
|
|
default: return "?";
|
|
}
|
|
}
|
|
|
|
|
|
int rtcp_msg_print(struct re_printf *pf, const struct rtcp_msg *msg)
|
|
{
|
|
size_t i, j;
|
|
int err;
|
|
|
|
if (!msg)
|
|
return 0;
|
|
|
|
err = re_hprintf(pf, "%8s pad=%d count=%-2d pt=%-3d len=%u ",
|
|
rtcp_type_name((enum rtcp_type)msg->hdr.pt),
|
|
msg->hdr.p,
|
|
msg->hdr.count, msg->hdr.pt, msg->hdr.length);
|
|
if (err)
|
|
return err;
|
|
|
|
switch (msg->hdr.pt) {
|
|
|
|
case RTCP_SR:
|
|
err = re_hprintf(pf, "%08x %u %u %u %u %u",
|
|
msg->r.sr.ssrc,
|
|
msg->r.sr.ntp_sec,
|
|
msg->r.sr.ntp_frac,
|
|
msg->r.sr.rtp_ts,
|
|
msg->r.sr.psent,
|
|
msg->r.sr.osent);
|
|
for (i=0; i<msg->hdr.count && !err; i++) {
|
|
const struct rtcp_rr *rr = &msg->r.sr.rrv[i];
|
|
err = re_hprintf(pf, " {%08x %u %d %u %u %u %u}",
|
|
rr->ssrc, rr->fraction, rr->lost,
|
|
rr->last_seq, rr->jitter,
|
|
rr->lsr, rr->dlsr);
|
|
}
|
|
break;
|
|
|
|
case RTCP_RR:
|
|
err = re_hprintf(pf, "%08x", msg->r.rr.ssrc);
|
|
for (i=0; i<msg->hdr.count && !err; i++) {
|
|
const struct rtcp_rr *rr = &msg->r.rr.rrv[i];
|
|
err = re_hprintf(pf, " {0x%08x %u %d %u %u %u %u}",
|
|
rr->ssrc, rr->fraction, rr->lost,
|
|
rr->last_seq, rr->jitter,
|
|
rr->lsr, rr->dlsr);
|
|
}
|
|
break;
|
|
|
|
case RTCP_SDES:
|
|
for (i=0; i<msg->hdr.count; i++) {
|
|
const struct rtcp_sdes *sdes = &msg->r.sdesv[i];
|
|
|
|
err = re_hprintf(pf, " {0x%08x n=%u",
|
|
sdes->src, sdes->n);
|
|
for (j=0; j<sdes->n && !err; j++) {
|
|
const struct rtcp_sdes_item *item;
|
|
item = &sdes->itemv[j];
|
|
err = re_hprintf(pf, " <%s:%b>",
|
|
rtcp_sdes_name(item->type),
|
|
item->data, item->length);
|
|
}
|
|
err |= re_hprintf(pf, "}");
|
|
}
|
|
break;
|
|
|
|
case RTCP_BYE:
|
|
err = re_hprintf(pf, "%u srcs:", msg->hdr.count);
|
|
for (i=0; i<msg->hdr.count && !err; i++) {
|
|
err = re_hprintf(pf, " %08x",
|
|
msg->r.bye.srcv[i]);
|
|
}
|
|
err |= re_hprintf(pf, " '%s'", msg->r.bye.reason);
|
|
break;
|
|
|
|
case RTCP_FIR:
|
|
err = re_hprintf(pf, "ssrc=%08x", msg->r.fir.ssrc);
|
|
break;
|
|
|
|
case RTCP_NACK:
|
|
err = re_hprintf(pf, "ssrc=%08x fsn=%04x blp=%04x",
|
|
msg->r.nack.ssrc, msg->r.nack.fsn,
|
|
msg->r.nack.blp);
|
|
break;
|
|
|
|
default:
|
|
err = re_hprintf(pf, "<len=%u>", msg->hdr.length);
|
|
break;
|
|
}
|
|
|
|
err |= re_hprintf(pf, "\n");
|
|
|
|
return err;
|
|
}
|