/*- * Copyright (c) 1993-1994 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 University of * California, Berkeley and the Network Research Group at * Lawrence Berkeley 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: /cs/research/mice/starship/src/local/CVS_repository/vic/codec/decoder-h261v1.cpp,v 1.2 1999/11/05 11:33:03 piers Exp $ (LBL)"; #include #include #include #include "inet.h" #include "rtp.h" #include "decoder.h" #include "p64.h" #include "pktbuf.h" class RTPv1H261Decoder : public Decoder { public: RTPv1H261Decoder(); ~RTPv1H261Decoder(); int colorhist(u_int* hist) const; virtual void recv(pktbuf*); void stats(char* wrk); protected: const u_char* reassemble(const u_char* bp, int& cc, u_int& seqno, u_int& flags); virtual void redraw(); u_short seqno_; /* last rtp seqno */ /* * Reassembly buffer 'slots' for resequencing & reassembling * gobs split across packet boundaries. */ #define H261_SLOTS 64 #define H261_SLOTMASK (H261_SLOTS - 1) static u_char* wrkbuf_; static int wrkbuflen_; int packetsize_; /* max size of gob fragments */ struct slot { int len; u_short flags; u_short seqno; u_char* bp; int pad; } slot_[H261_SLOTS]; u_char* slotbuf_; P64Decoder* codec_; }; static class RTPv1H261DecoderMatcher : public Matcher { public: RTPv1H261DecoderMatcher() : Matcher("decoder") {} TclObject* match(const char* id) { if (strcasecmp(id, "h261v1") == 0) return (new RTPv1H261Decoder()); else return (0); } } dm_h261v1; #define STAT_BAD_PSC 0 #define STAT_BAD_GOB 1 #define STAT_BAD_SYNTAX 2 #define STAT_BAD_FMT 3 #define STAT_BAD_SIZE 4 /* packet dropped for unsupported fmt */ #define STAT_BAD_BITOFF 5 /* unexpected non-zero sbits or ebits */ #define STAT_SIZE_CHANGE 6 /* packet dropped because frag size changed */ #define STAT_FMT_CHANGE 7 /* # times fmt changed */ u_char* RTPv1H261Decoder::wrkbuf_; int RTPv1H261Decoder::wrkbuflen_; RTPv1H261Decoder::RTPv1H261Decoder() : Decoder(2) { stat_[STAT_BAD_PSC].name = "Bad-PSC"; stat_[STAT_BAD_GOB].name = "Bad-GOB"; stat_[STAT_BAD_SYNTAX].name = "Bad-Syntax"; stat_[STAT_BAD_FMT].name = "Bad-fmt"; stat_[STAT_BAD_SIZE].name = "Bad-size"; stat_[STAT_BAD_BITOFF].name = "Bad-bitoff"; stat_[STAT_SIZE_CHANGE].name = "Size-change"; stat_[STAT_FMT_CHANGE].name = "Fmt-change"; nstat_ = 8; /* cause a resize trigger later */ inw_ = 0; inh_ = 0; decimation_ = 411; seqno_ = 0; packetsize_ = 0; for (int i = 0; i < H261_SLOTS; ++i) { slot_[i].len = 0; slot_[i].flags = 0; slot_[i].seqno = 0; slot_[i].bp = 0; } slotbuf_ = 0; codec_ = new FullP64Decoder(); resize(codec_->width(), codec_->height()); codec_->marks(rvts_); } RTPv1H261Decoder::~RTPv1H261Decoder() { delete slotbuf_; delete codec_; } void RTPv1H261Decoder::stats(char* wrk) { /* pull stats out of vic indepdendent P64Decoder */ setstat(STAT_BAD_PSC, codec_->bad_psc()); setstat(STAT_BAD_GOB, codec_->bad_GOBno()); setstat(STAT_BAD_SYNTAX, codec_->bad_bits()); setstat(STAT_BAD_FMT, codec_->bad_fmt()); Decoder::stats(wrk); } /* * Packets waiting for reassembly are kept in 'slots' * indexed by the low bits of the sequence number. * To try to avoid copying packets twice, we assume * that the packet source is breaking packets at some * fixed, max size (which we can determine from the size * of a packet with the SOG marker) and we allocate the buffer * for slots to be this size. As long as a packet doesn't * wrap, this should make the final packets contiguous. */ inline const u_char* RTPv1H261Decoder::reassemble(const u_char* bp, int& cc, u_int& rseqno, u_int& rflags) { register int flags = rflags; /* XXX if the packet is complete just render it */ if ((flags & 0x88) == 0x88) return (bp); register u_int seqno = rseqno; if (packetsize_ != cc) { if (packetsize_ == 0) { if ((flags & 0x80) == 0) return (0); packetsize_ = cc; u_int len = cc * H261_SLOTS; slotbuf_ = new u_char[len+4]; u_char* bp = slotbuf_ + 4; for (int i = 0; i < H261_SLOTS; ++i) { slot_[i].bp = bp; bp += cc; } if (len > wrkbuflen_) { delete wrkbuf_; wrkbuf_ = new u_char[len+4]; wrkbuflen_ = len; } } else if ((flags & 0x08) == 0 || cc > packetsize_) { count(STAT_SIZE_CHANGE); delete slotbuf_; slotbuf_ = 0; packetsize_ = 0; for (int i = 0; i < H261_SLOTS; ++i) { slot_[i].len = 0; slot_[i].flags = 0; slot_[i].seqno = 0; slot_[i].bp = 0; } return (0); } } register u_int l = seqno & H261_SLOTMASK; slot_[l].len = cc; slot_[l].seqno = seqno; slot_[l].flags = flags; memcpy((char*)slot_[l].bp, (char*)bp, cc); /* * Don't bother trying to reassemble packet unless it * has an EOG marker or we have the next packet. */ if ((flags & 0x08) == 0 && ((seqno + 1) & 0xffff) != slot_[(l + 1) & H261_SLOTMASK].seqno) return (0); /* scan backward for BOG */ register u_int pseq = seqno; register u_int f = l; while ((slot_[f].flags & 0x80) == 0) { pseq = (pseq - 1) & 0xffff; f = pseq & H261_SLOTMASK; if (slot_[f].seqno != pseq) /* packet missing */ return (0); } /* scan forward for EOG */ while ((slot_[l].flags & 0x08) == 0) { seqno = (seqno + 1) & 0xffff; l = seqno & H261_SLOTMASK; if (slot_[l].seqno != seqno) /* packet missing */ return (0); } if (l < f) { /* packet wraps around buffer, have to copy it */ u_char* wp = wrkbuf_ + 4; register u_int i = f - 1; do { i = (i + 1) & H261_SLOTMASK; register int slen = slot_[i].len; memcpy((char*)wp, (char*)slot_[i].bp, slen); wp += slen; } while (i != l); bp = wrkbuf_ + 4; cc = wp - bp; } else { bp = slot_[f].bp; cc = slot_[l].bp - bp + slot_[l].len; } /* * At this point, all we care about is ebit and sbit. */ flags = slot_[f].flags & 0x70; flags |= slot_[l].flags & 0x07; rflags = flags; rseqno = seqno; return (bp); } void RTPv1H261Decoder::recv(pktbuf* pb) { rtphdr* rh = (rtphdr*)pb->dp; u_int8_t* vh = (u_int8_t*)(rh + 1); /* * if the packet has a non-zero sbit but no SOG or * a non-zero ebit but no EOG, toss it. * if the packet contains both start & end gobs we can * just render it. Otherwise we wait until we get the * start and end markers and all the packets in between. */ u_int v = ntohs(*(u_int16_t*)vh); u_int flags = v >> 8; if (((flags & 0x70) != 0 && (flags & 0x80) == 0) || ((flags & 0x07) != 0 && (flags & 0x08) == 0)) { count(STAT_BAD_BITOFF); return; } int cc = pb->len - (sizeof(*rh) + 2); const u_int8_t* bp = vh + 2; u_int seqno = ntohs(rh->rh_seqno); bp = reassemble(bp, cc, seqno, flags); if (bp == 0) return; int sbit = (flags >> 4) & 7; int ebit = flags & 7; /* XXX tack on the start code */ /*XXX*/ cc += 2; bp -= 2; ((u_char*)bp)[0] = 0; if (sbit == 0) ((u_char*)bp)[1] = 1; else { ((u_char*)bp)[1] = 0; ((u_char*)bp)[2] |= 0x100 >> sbit; } codec_->mark(now_); (void)codec_->decode(bp, cc, sbit, ebit, 0, 0, 0, 0, 0); /* * If the stream changes format, issue a resize. */ if (codec_->width() != inw_) { count(STAT_FMT_CHANGE); resize(codec_->width(), codec_->height()); codec_->marks(rvts_); } /*XXX*/ if (ntohs(rh->rh_flags) & RTP_M) { codec_->sync(); ndblk_ = codec_->ndblk(); render_frame(codec_->frame()); codec_->resetndblk(); } pb->release(); } int RTPv1H261Decoder::colorhist(u_int* hist) const { const u_char* frm = codec_->frame(); int w = inw_; int h = inh_; int s = w * h; colorhist_411_556(hist, frm, frm + s, frm + s + (s >> 2), w, h); return (1); } void RTPv1H261Decoder::redraw() { if (codec_ != 0) Decoder::redraw(codec_->frame()); }