/* * Copyright (c) 1995 The Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the Network Research * Group at Lawrence Berkeley National Laboratory. * 4. Neither the name of the University nor of the Laboratory may be used * to endorse or promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ static const char rcsid[] = "@(#) $Header: session-vat.cc,v 1.9 96/04/04 03:46:39 van Exp $ (LBL)"; #include "config.h" #include #include "session.h" #include "source.h" #include "rtp.h" #include "ntp-time.h" /* * A class for backward compat with the old vat protocol * (vat-3.x and earlier). Vat now uses RTPv2. */ class VatSessionManager : public AudioSessionManager { public: virtual void send_bye(); virtual void send_report(); protected: virtual void transmit(pktbuf* pb); virtual void recv(CtrlHandler*); virtual void recv(DataHandler*); }; static class VatSessionMatcher : public Matcher { public: VatSessionMatcher() : Matcher("session") {} TclObject* match(const char* id) { if (strcmp(id, "audio/vat") == 0) return (new VatSessionManager); else return (0); } } vat_matcher; /* * this table maps vat format codes to RTP format codes. Invalid * codes are signaled by -1. The table includes the 2 'must be zero' * bits in the vat hdr to merge their check with the fmt code check. */ static const signed char rtp_format[128] = { RTP_PT_PCMU, RTP_PT_CELP, -1, RTP_PT_GSM, // 0 - 3 -1, -1, -1, -1, // 4 - 7 -1, -1, -1, -1, // 8 - 11 -1, -1, -1, -1, // 12 - 15 -1, -1, -1, -1, // 16 - 19 -1, -1, -1, -1, // 20 - 23 -1, -1, -1, -1, // 24 - 27 RTP_PT_LPC, RTP_PT_LPC, RTP_PT_DVI, -1, // 28 - 31 // remaining codes are not valid vat formats -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 32 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 48 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 64 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 80 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 96 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, //112 }; static timeval tzero; void VatSessionManager::transmit(pktbuf* pb) { mh_.msg_iov = pb->iov; Network* net = dh_.net(); /* * Smash the packet for RTPv1 compat. * (put the contents back because it gets looped back above) */ rtphdr rhs = *(rtphdr*)pb->iov[0].iov_base; // save pb->iov[0].iov_base = (caddr_t)pb->iov[0].iov_base + 4; pb->iov[0].iov_len -= 4; u_char* bp = (u_char*)pb->iov[0].iov_base; bp[0] = 0; int flags = ((u_char*)&rhs)[1]; int fmt = flags & 0x7f; if (fmt == RTP_PT_LPC) fmt = 28; else if (fmt == RTP_PT_DVI) /*XXX*/ fmt = 30; bp[1] = (flags & 0x80) | fmt; bp[2] = confid_ >> 8; bp[3] = confid_; *(u_int32_t*)&bp[4] = rhs.rh_ts; net->sendmsg(mh_); pb->iov[0].iov_len += 4; pb->iov[0].iov_base = (caddr_t)pb->iov[0].iov_base - 4; *(rtphdr*)pb->iov[0].iov_base = rhs; } /* * Send a vat report packet. */ void VatSessionManager::send_report() { SourceManager& sm = SourceManager::instance(); Source& s = *sm.localsrc(); s.lts_ctrl(unixtime()); u_char* bp = pktbuf_; bp[0] = 0; bp[1] = 1; bp[2] = confid_ >> 8; bp[3] = confid_; const char* name = s.sdes(RTCP_SDES_NAME); int n = strlen(name) + 1; memcpy(&bp[4], name, n); ch_.send(pktbuf_, n + 4); /* update the avg control msg size with our msg */ rtcp_avg_size_ += RTCP_SIZE_GAIN * (double(n + 32) - rtcp_avg_size_); /* * compute the time to the next report. The bandwidth * limit for rtcp traffic was set on startup from the * session bandwidth. It is the inverse of bandwidth * (ie., ms/byte) to avoid a divide below. */ int nsrc = sm.nsources(); double rint = rtcp_avg_size_ * double(nsrc) * rtcp_inv_bw_; if (rint < RTCP_MIN_RPT_TIME * 1000.) rint = RTCP_MIN_RPT_TIME * 1000.; rt_.msched(int(fmod(double(random()), rint) + rint * .5 + .5)); sm.CheckActiveSources(rint); } /* * Send a vat bye message. */ void VatSessionManager::send_bye() { u_int8_t* bp = pktbuf_; bp[0] = 0; bp[1] = 2; bp[2] = confid_ >> 8; bp[3] = confid_; ch_.send(pktbuf_, 4); } void VatSessionManager::recv(DataHandler* dh) { u_int32_t addr; u_char* bp = &pktbuf_[4]; int cc = dh->recv(bp, 2 * RTP_MTU - 4, addr); if (cc <= 8) { ++nrunt_; return; } int flags = ntohs(*(u_int16_t*)bp); if ((flags & 0xc000) != 0 || ntohs(*(u_int16_t*)(bp + 2)) != confid_) { ++badversion_; return; } int fmt = rtp_format[flags & 0x7f]; if (fmt < 0) { ++badfmt_; return; } u_int32_t ts = *(u_int32_t*)(bp + 4); SourceManager& sm = SourceManager::instance(); Source* s = sm.lookup(addr, addr, addr); timeval now = unixtime(); /* skip over vat header */ cc -= 8; bp += 8; rtphdr* rh = (rtphdr*)(bp - 12); s->np(1); s->nb(cc + sizeof(*rh)); if (s->checkseq(ntohl(ts) / FRAMESIZE)) /* this is a dup */ return; int cnt = (flags >> 8) & 0x3f; if (cnt > 0) { if (cc <= cnt * 4) { ++badoptions_; /*XXX*/ return; } s->ismixer(1); rh = (rtphdr*)((char*)rh + (cnt << 2)); while (--cnt >= 0) { u_int32_t csrc = *(u_int32_t*)bp; bp += 4; cc -= 4; Source* cs = sm.lookup(csrc, addr, addr); cs->lts_data(now); cs->sts_data(ts); cs->action(); } } else { s->lts_data(now); s->sts_data(ts); /* * because of bugs in the vat-2/3 'mixer' code, we * occasionally get packets from a 'mixer' that's not * really active. suppress marking a mixer active * unless we got a name from it. */ if (s->ismixer() == 0 || s->sdes(0)) s->action(); } /* fill in stuff decoders want */ flags = (flags & 0x80) | fmt; rh->rh_flags = ntohs(flags); rh->rh_ts = ts; if (flags & RTP_M) s->nf(1); /* * This is a data packet. If the source needs activation, * or the packet format has changed, deal with this. * Then, hand the packet off to the packet handler. * XXX might want to be careful about flip-flopping * here when format changes due to misordered packets * (easy solution -- keep rtp seqno of last fmt change). */ PacketHandler* h = s->handler(); if (h == 0) h = s->activate(fmt); else if (s->format() != fmt) h = s->change_format(fmt); int hlen = h->hdrlen(); cc -= hlen; if (cc < 0) { s->runt(1); return; } if (!s->mute()) h->recv(rh, bp + hlen, cc); } void VatSessionManager::recv(CtrlHandler* ch) { /* * handle in incoming vat session packet */ u_int8_t* bp = pktbuf_; u_int32_t addr; int cc = ch->recv(bp, 2 * RTP_MTU, addr); if (cc <= 0) return; if (cc < 4) { ++nrunt_; return; } if ((bp[0] & 0xc0) != 0 || ntohs(*(u_int16_t*)(bp + 2)) != confid_) { ++badversion_; return; } rtcp_avg_size_ += RTCP_SIZE_GAIN * (double(cc + 28) - rtcp_avg_size_); SourceManager& sm = SourceManager::instance(); Source* s = sm.lookup(addr, addr, addr); u_int8_t* ep = &bp[cc]; *ep = 0; int i; timeval now = unixtime(); switch (bp[1]) { case 1: /* ID */ s->sdes(RTCP_SDES_NAME, (char*)&bp[4]); if (s->lts_done().tv_sec && int(now.tv_sec - s->lts_done().tv_sec) > 2) { /* * the site said it was done but then came back - * reactivate it. (the 2 second delay is to * account for possible packet reordering.) */ s->lts_done(tzero); s->lost(0); } break; case 2: /* BYE */ s->lts_done(now); break; case 3: /* IDLIST */ i = bp[4]; bp += 8; while (--i >= 0 && bp < ep) { u_int32_t id = *(u_int32_t*)bp; bp += 4; /* * Due to a bug in vat-3, mixers are * identified with id 0. We conveniently * use this fact to filter out mixer * names, since the rest of vat assumes * that a mixer usually won't send their * name and uses this as a hint to * not display them in the UI. (In RTP, * if a mixer does send it's name, then * we go ahead and display it.) */ if (id != 0) { s = sm.lookup(id, addr, addr); s->sdes(RTCP_SDES_NAME, (char*)bp); s->lts_ctrl(now); } int n = strlen((char*)bp); n += 4 - (n & 3); bp += n; } break; } s->lts_ctrl(now); }