Logo Search packages:      
Sourcecode: vic version File versions

vdd.cpp

/*
 * 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 <stdio.h>
#include <sys/param.h>
#include <netinet/in.h>
#ifdef USE_SHM
extern "C" {
#include <sys/ipc.h>
#include <sys/shm.h>
#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 <getopt.h>

#include <osfcn.h>

#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);
}

Generated by  Doxygen 1.6.0   Back to index