/* * Copyright (c) 1994 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. */ #ifndef lint static char rcsid[] = "@(#) $Header: /cs/research/mice/starship/src/local/CVS_repository/vic/rtp/vdd.cpp,v 1.1 1999/09/09 12:29:50 piers Exp $ (LBL)"; #endif #include #include #include #ifdef USE_SHM extern "C" { #include #include #if defined(sun) && !defined(__svr4__) int shmget(key_t, int, int); char *shmat(int, char*, int); int shmdt(char*); int shmctl(int, int, struct shmid_ds*); #endif } #endif #include #include "inet.h" #include "rtp.h" #include "p64.h" #include "p64dump.h" #include "color.h" #include "renderer.h" #include "vw.h" #include "vic_tcl.h" /* RTP v1 packet header */ struct rtp1hdr { u_char rh_chan; /* version/channel */ u_char rh_flags; /* O/S/content */ u_short rh_seqno; /* sequence number */ u_int32_t rh_ts; }; #define RTPCONT_H261 31 /* CCITT H.261 */ #define RTP_CONTENT_MASK 0x3f #define RTP_OPTION_BIT 0x80 int cnt = 0; int aflag = 0; int cflag = 0; int Dflag = 0; int iflag = 0; int nflag = 0; int qflag = 0; int sflag = 0; int vflag = 0; int dumpno = -1; u_int packets; u_int frames; u_int pictures; u_int32_t tzero; timeval start; u_int firstseq; u_int maxseq; u_int seqwraps; u_int32_t maxts; int lastSeqno = 0; #define H261_SLOTS 32 #define H261_SLOTMASK (H261_SLOTS - 1) /* * packet 'slots' for resequencing & reassembling gobs split * across packet boundaries. */ struct h261_pslot { int len; u_short flags; u_short seqno; u_char* bp; int filler; }; h261_pslot slot_[H261_SLOTS]; int fmt_ = -1; u_int seqno_; int packetsize_; u_char* wrkbuf; int wrkbuflen; int width; int height; Renderer* renderer; P64Decoder* decoder; FILE* in; void usage() { fprintf(stderr, "usage\n"); exit(1); } char* fmtts(u_int tsdelta) { static char tswrk[64]; register u_int sec = tsdelta >> 16; register u_int h = sec / 3600; register u_int m = (sec % 3600) / 60; register u_int s = sec % 60; register u_int frac = tsdelta & 0xffff; register float f = float(s) + float(frac) * (1./65536.); sprintf(tswrk, "%02d:%02d:%06.3f", h, m, f); return (tswrk); } int nextpacket(u_char* pktbuf, int cc) { if (packets == 0) gettimeofday(&start, 0); int sts = 0; u_int seqno; u_int32_t ts; register u_char* bp; u_int vers = *pktbuf >> 6; switch (vers) { case 1: { rtp1hdr* rh = (rtp1hdr*)pktbuf; cc -= sizeof(*rh); if ((rh->rh_chan & 0xc0) != 0x40 || (rh->rh_flags & RTP_CONTENT_MASK) != RTPCONT_H261) /* wrong version or content type */ return (sts); seqno = ntohs(rh->rh_seqno); ts = ntohl(rh->rh_ts); bp = (u_char*)(rh + 1); if (rh->rh_flags & RTP_OPTION_BIT) { /* skip over options */ register u_int optflags; do { optflags = bp[0]; register u_int optlen = bp[1] << 2; if (optlen >= cc) /* empty packet or bad optlen */ return (sts); bp += optlen; cc -= optlen; } while ((optflags & 0x80) == 0); } } break; case 2: { rtphdr* rh = (rtphdr*)pktbuf; int flags = ntohs(rh->rh_flags); if ((flags & 0x7f) != RTP_PT_H261) /* wrong content type */ return (sts); if ((flags & 0x0f00) != 0) /* XXX - can't deal with csrcs */ return (sts); seqno = ntohs(rh->rh_seqno); ts = ntohl(rh->rh_ts); cc -= sizeof(*rh); bp = (u_char*)(rh + 1); } break; default: /* unknown protocol version */ return (sts); } if (cc <= 2) /* wrong size */ return (sts); u_int flags = ntohs(*(u_short*)bp); bp += 2; cc -= 2; /* * if S is set, we have a start-of-GOB, but the RTPv1/H.261 * spec strips the start code. put it back. */ if (flags & 0x8000) { bp -= 2; cc += 2; bp[0] = 0; int sbit = (flags >> 12) & 7; if (sbit == 0) bp[1] = 1; else { bp[1] = 0; bp[2] |= 0x100 >> sbit; } } u_int fmt = flags & 7; int hascolor = flags & 0x80; flags >>= 8; u_int sbit = (flags >> 4) & 7; u_int ebit = flags & 7; if (packets == 0) { tzero = ts; maxts = ts; firstseq = seqno; maxseq = seqno; seqno_ = (seqno - 1) & 0xffff; } if (Dflag || vflag) { printf("packet %d bytes %d fmt %d%s sbit %d%s ebit %d%s", seqno, cc, fmt, (flags & 0x80)? " S":"", sbit, (flags & 0x08)? " E":"", ebit, hascolor? " color" : ""); if (vflag) { if (aflag == 0) { printf(" ts %s", fmtts(ts - tzero)); } else printf(" ts 0x%08x", ts); } if (((seqno_ + 1) & 0xffff) != seqno) printf(" *"); printf("\n"); } register int dt = int(ts - maxts); register int ds = (seqno - maxseq) & 0xffff; if (dt > 0 || (dt == 0 && ds && ds < 32768)) { maxts = ts; if (seqno < maxseq) /* keep track of seq no. wraps */ ++seqwraps; maxseq = seqno; } seqno_ = seqno; ++packets; /* * 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. */ if ((flags & 0x88) != 0x88) { /* * if the packet has a non-zero sbit but no SOG or * a non-zero ebit but no EOG, toss it. */ if ((sbit && ((flags & 0x80) == 0)) || (ebit && ((flags & 0x08) == 0))) return (sts); /* * 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. */ if (packetsize_ != cc) { if (packetsize_ == 0) { if ((flags & 0x80) == 0) return (sts); packetsize_ = cc; u_int len = cc * H261_SLOTS; u_char* wp = (u_char*)malloc(len); if (wp == 0) { fprintf(stderr, "vic: h261decoder: can't get %d bytes for reassembly buffer\n", len); packetsize_ = 0; return (sts); } for (int i = 0; i < H261_SLOTS; ++i) { slot_[i].seqno = ~0; slot_[i].bp = wp; wp += cc; } if (len > wrkbuflen) { if (wrkbuflen == 0) wrkbuf = (u_char*)malloc(len); else wrkbuf = (u_char*)realloc(wrkbuf, len); if (wrkbuf == 0) { fprintf(stderr, "vic: h261decoder: can't get %d bytes for work buffer\n", len); free(slot_[0].bp); packetsize_ = 0; wrkbuflen = 0; return (sts); } wrkbuflen = len; } } else if ((flags & 0x08) == 0 || cc > packetsize_) { static int msgcnt; if (++msgcnt < 20) fprintf(stderr, "vic: h261decoder: expected %d byte frags, got %d\n", packetsize_, cc); return (sts); } } register u_int l = seqno & H261_SLOTMASK; h261_pslot& slot = slot_[l]; if (slot.seqno == seqno) /* got a duplicate */ return (sts); slot.len = cc; slot.seqno = seqno; slot.flags = flags | (hascolor << 8); memcpy((char*)slot.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 (sts); /* scan backward for BOG */ register u_int pseq = seqno; register u_int f = pseq & H261_SLOTMASK; while ((slot_[f].flags & 0x80) == 0) { pseq = (pseq - 1) & 0xffff; f = pseq & H261_SLOTMASK; if (slot_[f].seqno != pseq) /* packet missing */ return (sts); } /* 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 (sts); } if (l < f) { /* packet wraps around buffer, have to copy it */ u_char* wp = wrkbuf; 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; cc = wp - bp; } else { bp = slot_[f].bp; cc = slot_[l].bp - bp + slot_[l].len; } hascolor = slot_[f].flags >> 8; sbit = (slot_[f].flags >> 4) & 7; ebit = slot_[l].flags & 7; } lastSeqno = seqno; (void)decoder->decode(bp, cc, sbit, ebit, 0, 0, 0, 0, 0); /* * This check only works if GOBs appear in order and * are not fragmented. XXX i.e., it doesn't work for RTPv2/H.261 */ int eop; if (decoder->seenMaxGob()) { eop = 1; decoder->sync(); } else eop = 0; ++frames; if (Dflag || vflag) printf("frame %d picture %d\n", frames, pictures); if (! nflag && (eop || iflag)) { Renderer* r = renderer; if (width != decoder->width() || height != decoder->height()) { width = decoder->width(); height = decoder->height(); r->resize(width, height, 1); } #ifdef notdef int x, y, w, h; decoder->bb(x, y, w, h); int off = y * decoder->width() + x; #endif /*XXX*/ u_char rvts[640 * 480 / 64]; memset(rvts, 1, sizeof(rvts)); r->render_frame(decoder->frame(), rvts, 0); sts = 1; } return (sts); } void finish(int sts) { if (!qflag) { timeval end; gettimeofday(&end, 0); int expected = (seqwraps << 16) + maxseq - firstseq + 1; int nlost = expected - packets; int plost = (nlost * 100) / expected; fprintf(stderr, "got %d packets of %d (%d%% lost), %d frames, %d pictures in %s\n", packets, expected, plost, frames, pictures, fmtts(maxts - tzero)); double s = end.tv_sec - start.tv_sec; s += 1e-6 * (end.tv_usec - start.tv_usec); fprintf(stderr, "running time %g sec (%g fps)\n", s, pictures / s); } exit(sts); } int nextrec() { if (cnt > 0 && --cnt == 0) finish(0);; if (feof(in)) finish(0);; struct ivs_record_hdr { u_short length; u_short delay; } irh; fread(&irh, sizeof(irh), 1, in); int length = ntohs(irh.length); if (length > 8000 || length <= 0) { printf("ridiculous packet length in file\n"); finish(1);; } u_char pktbuf[8192]; register int len = fread(pktbuf, 1, length, in); if (len != length) { if (len > 0) { fprintf(stderr, "-truncated read: expected %d, got %d\n", length, len); } finish(2);; } if (ntohs(irh.delay) & 0x8000) /* got a session packet (ignore for now) */ return (-1); return (nextpacket(pktbuf, length)); } #ifndef USE_SHM int use_shm = 0; #else int use_shm = 1; static int noXShm(Display* dpy, XErrorEvent*) { use_shm = 0; fprintf(stderr, "vdd: warning: not using shared memory\n"); return (0); } /* * XXX this is a hack to see if we can used shared memory. * if the X server says we can, and we\'re on the same * host as the X server, then we are golden. */ static void checkXShm(Display* dpy) { XShmSegmentInfo si; if ((si.shmid = shmget(IPC_PRIVATE, 512, IPC_CREAT|0777)) < 0) { perror("shmget"); exit(1); } si.readOnly = 1; XSync(dpy, 0); XSetErrorHandler(noXShm); XShmAttach(dpy, &si); XSync(dpy, 0); XSetErrorHandler(NULL); (void)shmctl(si.shmid, IPC_RMID, 0); } #endif void dump_ppm(char* file, const u_char* yp, const u_char* up, const u_char* vp, int w, int h) { FILE *f; if (file == 0) f = stdout; else { (void)unlink(file); f = fopen(file, "w"); if (f == 0) /*XXX*/ return; } fprintf(f, "P3\n%d %d\n255\n", w, h); for (int y = 0; y < h; ++y) { for (int x = 0; x < w; ++x) { double Y = (double)yp[w * y + x]; double U = up[(w / 2) * (y / 2) + x / 2] - 128.; double V = vp[(w / 2) * (y / 2) + x / 2] - 128.; int r = int(Y + 1.402 * V); int g = int(Y - 0.34414 * U - 0.71414 * V); int b = int(Y + 1.772 * U); #define LIM(x) ((x) >= 255 ? 255 : ((x) < 0 ? 0 : (x))) r = LIM(r); g = LIM(g); b = LIM(b); fprintf(f, "%d %d %d\n", r, g, b); } } if (f == stdout) fflush(stdout); else fclose(f); } extern "C" char *optarg; extern "C" int optind; extern "C" int opterr; const char* disparg(int argc, const char** argv, const char* optstr) { const char* display = 0; int op; while ((op = getopt(argc, (char**)argv, (char*)optstr)) != -1) { if (op == 'd') { display = optarg; break; } else if (op == '?') usage(); } optind = 1; return (display); } /* * decoder decode $frmno * decoder dump [file] * decoder next * decoder frame */ int cmd_decoder(ClientData cd, Tcl_Interp*, int argc, char** argv) { Tcl& tcl = Tcl::instance(); if (argc == 2) { if (strcmp(argv[1], "next") == 0) { while (nextrec() == 0) { } return (TCL_OK); } if (strcmp(argv[1], "frame") == 0) { char* bp = tcl.buffer(); sprintf(bp, "%d %d", frames, pictures); tcl.result(bp); return (TCL_OK); } if (strcmp(argv[1], "nextpacket") == 0) { nextrec(); return (TCL_OK); } if (strcmp(argv[1], "format") == 0) { tcl.result(decoder->width() == 352 ? "cif" : "qcif"); return (TCL_OK); } if (strcmp(argv[1], "dump") == 0) { #ifdef notyet dump_ppm(0, decoder->yplane(), decoder->uplane(), decoder->vplane(), decoder->width(), decoder->height()); return (TCL_OK); #endif } } else if (argc == 3) { if (strcmp(argv[1], "dump") == 0) { #ifdef notyet dump_ppm(argv[2], decoder->yplane(), decoder->uplane(), decoder->vplane(), decoder->width(), decoder->height()); return (TCL_OK); #endif } if (strcmp(argv[1], "target") == 0) { renderer = (Renderer*)TclObject::lookup(argv[2]); renderer->resize(decoder->width(), decoder->height(), 1); return (TCL_OK); } } return (TCL_ERROR); } char* parse_assignment(char* cp) { cp = strchr(cp, '='); if (cp != 0) { *cp = 0; return (cp + 1); } else return ("true"); } int main(int argc, const char **argv) { Tcl::init("vdd"); char clipvers[8]; int op; int osize = 2; const char* optstr = "abC:c:Dd:F:inSstu:vqX:"; const char* display = disparg(argc, argv, optstr); Tcl::init("vdd"); Tcl& tcl = Tcl::instance(); Tk_Window tk = Tk_CreateMainWindow(tcl.interp(), (char*)display, "vdd", "Vdd"); if (tk == 0) { fprintf(stderr, "vdd: %s\n", tcl.result()); exit(1); } tcl.tkmain(tk); EmbeddedTcl::init(); tcl.evalc("init_resources"); while ((op = getopt(argc, (char**)argv, optstr)) != -1) { switch (op) { default: usage(); case 'a': aflag = 1; break; case 'b': osize = 3; break; case 'd': break; case 'c': tcl.add_option("dither", optarg); break; case 'C': cnt = atoi(optarg); break; case 'F': dumpno = atoi(optarg); break; case 'D': ++Dflag; break; case 'i': ++iflag; break; case 'n': ++nflag; break; case 'q': qflag = 1; break; case 's': osize = 1; break; case 'S': use_shm = 0; break; case 't': osize = 0; break; case 'u': tcl.add_option("startupScript", optarg); break; case 'v': vflag = 1; break; case 'X': { const char* value = parse_assignment(optarg); tcl.add_option(optarg, value); } break; } } #ifdef USE_SHM if (use_shm) checkXShm(Tk_Display(tk)); #endif if (optind >= argc || argc <= 1) usage(); const char* infile = argv[optind]; if (Dflag) decoder = new P64Dumper; else decoder = new FullP64Decoder; /*XXX*/ in = fopen(infile, "r"); if (in == 0) { perror(infile); exit(1); } in = fopen(infile, "r"); if (in == 0) { perror(infile); exit(1); } /* * Check to see which of the 3 incompatible ivs_record * formats this file uses. * XXX seems we handle only the latest now */ op = fread(clipvers, 1, sizeof(clipvers), in); if (op != sizeof(clipvers)) { fprintf(stderr, "-%s: truncated read of %s (%d bytes)\n", argv[0], infile, op); exit(2); } if (strncmp(clipvers, "IVS CLIP", 8) == 0 || strncmp(clipvers, "RTPCLIP ", 8) == 0) { while ((op = getc(in)) != 0 && op != EOF) { } if (op == EOF) { fprintf(stderr, "-%s: unexpected eof in %s\n", argv[0], infile); exit(3); } } else { fseek(in, 0L, 0); } tcl.CreateCommand("decoder", cmd_decoder); tcl.evalf("vdd_main %d %d", 88 << osize, 72 << osize); Tk_MainLoop(); exit(0); }