| /** |
| * @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; |
| } |
| |
| |
| /** |
| * Send an RTCP Application-Defined (APP) packet |
| * |
| * @param rs RTP Socket |
| * @param name Ascii name (4 octets) |
| * @param data Application-dependent data |
| * @param len Number of bytes of data |
| * |
| * @return 0 for success, otherwise errorcode |
| */ |
| 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); |
| } |
| |
| |
| /** |
| * Send an RTCP NACK packet |
| * |
| * @param rs RTP Socket |
| * @param fsn First Sequence Number lost |
| * @param blp Bitmask of lost packets |
| * |
| * @return 0 for success, otherwise errorcode |
| */ |
| 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); |
| } |
| |
| |
| /** |
| * Send an RTCP Picture Loss Indication (PLI) packet |
| * |
| * @param rs RTP Socket |
| * @param fb_ssrc Feedback SSRC |
| * |
| * @return 0 for success, otherwise errorcode |
| */ |
| 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 "?"; |
| } |
| } |
| |
| |
| /** |
| * Print an RTCP Message |
| * |
| * @param pf Print handler for debug output |
| * @param msg RTCP Message |
| * |
| * @return 0 if success, otherwise errorcode |
| */ |
| 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, |
| (size_t)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_APP: |
| err = re_hprintf(pf, "src=%08x '%b' data=%zu", |
| msg->r.app.src, |
| msg->r.app.name, sizeof(msg->r.app.name), |
| msg->r.app.data_len); |
| 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; |
| |
| case RTCP_RTPFB: |
| err = re_hprintf(pf, "pkt=%08x med=%08x n=%u", |
| msg->r.fb.ssrc_packet, |
| msg->r.fb.ssrc_media, |
| msg->r.fb.n); |
| if (msg->hdr.count == RTCP_RTPFB_GNACK) { |
| err |= re_hprintf(pf, " GNACK"); |
| for (i=0; i<msg->r.fb.n; i++) { |
| err |= re_hprintf(pf, " {%04x %04x}", |
| msg->r.fb.fci.gnackv[i].pid, |
| msg->r.fb.fci.gnackv[i].blp); |
| } |
| } |
| break; |
| |
| case RTCP_PSFB: |
| err = re_hprintf(pf, "pkt=%08x med=%08x n=%u", |
| msg->r.fb.ssrc_packet, |
| msg->r.fb.ssrc_media, |
| msg->r.fb.n); |
| if (msg->hdr.count == RTCP_PSFB_SLI) { |
| err |= re_hprintf(pf, " SLI"); |
| for (i=0; i<msg->r.fb.n; i++) { |
| err |= re_hprintf(pf, " {%04x %04x %02x}", |
| msg->r.fb.fci.sliv[i].first, |
| msg->r.fb.fci.sliv[i].number, |
| msg->r.fb.fci.sliv[i].picid); |
| } |
| } |
| else if (msg->hdr.count == RTCP_PSFB_AFB) { |
| err |= re_hprintf(pf, " AFB %u bytes", |
| msg->r.fb.n * 4); |
| } |
| break; |
| |
| default: |
| err = re_hprintf(pf, "<len=%u>", msg->hdr.length); |
| break; |
| } |
| |
| err |= re_hprintf(pf, "\n"); |
| |
| return err; |
| } |