/* * 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. */ static const char rcsid[] = "@(#) $Header: /cs/research/mice/starship/src/local/CVS_repository/vic/rtp/h261_play.cpp,v 1.1 1999/09/09 12:29:46 piers Exp $ (LBL)"; #include #ifdef WIN32 #include #include "config.h" #else #include #include #include #endif #ifdef USE_SHM extern "C" { #include #include #if defined(sparc) && !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 #ifdef WIN32 #include #else #include #endif #include "p64.h" #include "p64dump.h" #include "color.h" #include "renderer.h" #include "vw.h" #include "vic_tcl.h" void usage() { fprintf(stderr, "usage\n"); exit(1); } #ifndef USE_SHM int use_shm = 0; #else int use_shm = 1; static int noXShm(Display* , XErrorEvent*) { use_shm = 0; fprintf(stderr, "h261_play: 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, const char* ) { 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 P64Decoder* decoder; Renderer* renderer; FILE* in; int nflag = 0; int nframe = 0; timeval start; struct offset { off_t off; int bit; }; #define MAXFRAME 100000 offset offs[MAXFRAME]; 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); } void buildindex(FILE* f) { off_t off = 0; int n = 0; u_int bb; bb = getc(f); bb <<= 8; bb |= getc(f); int v = getc(f); bb <<= 4; bb |= v >> 4; v <<= 4; for (;;) { int bit; for (bit = 0; bit < 4; ++bit) { if ((bb & 0xfffff) == 0x00010) { offset* p = &offs[n++]; p->bit = bit; p->off = off; } bb <<= 1; v <<= 1; bb |= (v >> 8) & 1; } v = getc(f); if (feof(f)) break; for (; bit < 8; ++bit) { if ((bb & 0xfffff) == 0x00010) { offset* p = &offs[n++]; p->bit = bit; p->off = off; } bb <<= 1; v <<= 1; bb |= (v >> 8) & 1; } ++off; } /*XXX skip last frame, since we use offs[cfrm+1] below*/ nframe = n - 1; rewind(f); } int lflag; int cfrm; /*XXX*/ u_char crvec[768 * 576 / 64]; u_int now = 1; void render_frame(const u_char* frm, int w, int h) { /* * Go through all the timestamps and smash the time that * is about to wrap from the past into the future to the present, * so that the block gets updated just once. If we let timestamps * wrap without doing anything, the algorithm would still work, * but we'd end up rendering a stationary block on every call here * (until we surpassed now + 128 again). In other words, for * an unchanging block, this approach renders it 2 times out of 256, * rather than 128 out of 256. */ int wraptime = now ^ 0x80; u_char* ts = crvec; for (int k = sizeof(crvec); --k >= 0; ++ts) { if (*ts == wraptime) *ts = now; } YuvFrame f(now, (u_int8_t*)frm, crvec, w, h); renderer->consume(&f); now = (now + 1) & 0xff; decoder->mark(now); } int nextframe() { if (cfrm >= nframe-1) { if (!lflag) { fprintf(stderr, "h261_play: eof\n"); timeval end; gettimeofday(&end, 0); double v = end.tv_sec - start.tv_sec; v += 1e-6 * (end.tv_usec - start.tv_usec); fprintf(stderr, "h261_play: %d frames in %g seconds (%g fps)\n", nframe, v, (double)nframe / v); exit(0); } cfrm = 0; } if (cfrm == 0) gettimeofday(&start, 0); offset* p = &offs[cfrm++]; offset* nxt = &offs[cfrm]; if (fseek(in, p->off, 0) < 0) { perror("fseek"); exit(1); } int len = nxt->off - p->off + 1; int ebit = 8 - nxt->bit; if (ebit == 8) { len -= 1; ebit = 0; } u_char buffer[80*1024]; if (fread((char*)buffer, len, 1, in) == 0) { perror("fread"); exit(1); } P64Decoder* d = decoder; d->decode(buffer, len, p->bit, ebit, 0, 0, 0, 0, 0); d->sync(); if (nflag) return (TCL_OK); render_frame(d->frame(), d->width(), d->height()); return (TCL_OK); } 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 , Tcl_Interp*, int argc, char** argv) { Tcl& tcl = Tcl::instance(); if (argc == 2) { if (strcmp(argv[1], "frame") == 0) { char* bp = tcl.buffer(); sprintf(bp, "%d", cfrm); tcl.result(bp); return (TCL_OK); } if (strcmp(argv[1], "next") == 0) { nextframe(); return (TCL_OK); } } else if (argc == 3) { if (strcmp(argv[1], "renderer") == 0) { renderer = (Renderer*)TclObject::lookup(argv[2]); return (TCL_OK); } #ifdef notdef if (strcmp(argv[1], "dump") == 0) { dump_ppm(argv[2], decoder->yplane(), decoder->uplane(), decoder->vplane(), decoder->width(), decoder->height()); return (TCL_OK); } #endif } return (TCL_ERROR); } #ifndef WIN32 extern "C" { int TkPlatformInit(Tcl_Interp *interp) { extern void TkCreateXEventSource(void); Tcl_SetVar(interp, "tk_library", "", TCL_GLOBAL_ONLY); TkCreateXEventSource(); return (TCL_OK); } } #endif int main(int argc, const char **argv) { int op; int Dflag = 0; int osize = 2; const char* optstr = "bCc:Dd:lnpSstu:V:"; const char* display = disparg(argc, argv, optstr); Tcl::init("h261_play"); Tcl& tcl = Tcl::instance(); #ifdef WIN32 if (display == NULL) display = "localhost:0"; #endif tcl.evalf(display? "set argv \"-name h261_play -display %s\"" : "set argv \"-name h261_play\"", display); Tk_Window tk = 0; if (Tk_Init(tcl.interp()) == TCL_OK) tk = Tk_MainWindow(tcl.interp()); if (tk == 0) { fprintf(stderr, "h261_play: %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 'b': osize = 3; break; case 'C': tcl.add_option("showChanges", "true"); break; case 'c': tcl.add_option("dither", optarg); break; case 'D': ++Dflag; break; case 'd': break; case 'l': ++lflag; break; case 'n': ++nflag; break; case 'p': tcl.add_option("pause", "false"); 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': tcl.add_option("visual", optarg); break; } } #ifdef USE_SHM if (use_shm) checkXShm(Tk_Display(tk), display); #endif if (optind >= argc || argc <= 1) usage(); const char* infile = argv[optind]; if (Dflag) decoder = new P64Dumper; else decoder = new FullP64Decoder; decoder->marks(crvec); decoder->mark(now); #ifdef WIN32 in = fopen(infile, "rb"); #else in = fopen(infile, "r"); #endif if (in == 0) { perror(infile); exit(1); } /* * Check that the file begins with a picture header. */ u_char phdr[3]; if (fread(phdr, 3, 1, in) != 1 || phdr[0] != 0 || phdr[1] != 1 || (phdr[2] >> 4) != 0) { fprintf(stderr, "h261_play: %s not a raw H.261 bit stream\n", infile); exit(1); } rewind(in); fprintf(stderr, "h261_play: building frame index..."); buildindex(in); fprintf(stderr, "ready - %d frames\n", nframe); tcl.CreateCommand("decoder", cmd_decoder); tcl.evalf("h261_main %d %d", 88 << osize, 72 << osize); Tk_MainLoop(); exit(0); }