/* * Copyright (c) 1993-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/video/grabber-vl.cpp,v 1.1 1999/09/09 12:52:56 piers Exp $ (LBL)"; #endif #include #include #include #include #include #include #include #include #include #include "grabber.h" #include "crdef.h" #include "module.h" #include "device-input.h" #include "vic_tcl.h" class vlDevice; class VLGrabber : public Grabber { public: VLGrabber(vlDevice&); virtual ~VLGrabber(); virtual int command(int argc, const char*const* argv); virtual void fps(int); virtual void start(); virtual void stop(); protected: void startup_vl(); void shutdown_vl(); virtual void saveblks(const u_char*); void suppress(const u_char*); virtual int grab(); virtual void setsize(int xsize, int ysize); u_char* next_frame(); int closest_rate(int fps) const; VLServer vlServer_; VLPath vlPath_; VLBuffer transferBuf_; VLNode src_; VLNode drn_; int decimate_; int current_decimate_; int current_fps_; int current_port_; int is_pal_; int port_; vlDevice& device_; }; class VLCIFGrabber : public VLGrabber { public: VLCIFGrabber(vlDevice&); protected: virtual void setsize(int xsize, int ysize); void saveblk(const u_char* in, u_char* yp, u_char* up, u_char* vp, int stride, int is); virtual void saveblks(const u_char* in); }; class VL411Grabber : public VLCIFGrabber { public: VL411Grabber(vlDevice&); protected: virtual void setsize(int xsize, int ysize); }; class vlDevice : public InputDevice { public: vlDevice(const char* name, VLDev, VLNodeInfo* ports, int nport); virtual int command(int argc, const char*const* argv); inline int device() const { return (device_); } int lookup_port(const char*) const; protected: VLDev device_; VLNodeInfo* ports_; int nport_; }; static class vlBuilder { public: vlBuilder(); static VLNodeInfo* findports(VLNodeInfo* node, int& n); } vlb; vlDevice::vlDevice(const char* name, VLDev device, VLNodeInfo* ports, int nport) : InputDevice(name), device_(device), ports_(ports), nport_(nport) { /* smash "ev1" insto galileo for better familiarity */ if (nickname_[0] == 'e' && nickname_[1] == 'v') { nickname_ = "galileo"; } char* cp = new char[80 + nport * (VL_NAME_SIZE + 1)]; attributes_ = cp; strcpy(cp, "format { 411 422 } size { small cif } port { "); cp += strlen(cp); *cp++ = ' '; for (int i = 0; i < nport; ++i) { strcpy(cp, ports[i].name); cp += strlen(cp); *cp++ = ' '; } *cp++ = '}'; *cp = 0; } int vlDevice::lookup_port(const char* name) const { for (int i = 0; i < nport_; ++i) { if (strcasecmp(name, ports_[i].name) == 0) return (ports_[i].number); } abort(); } int vlDevice::command(int argc, const char*const* argv) { Tcl& tcl = Tcl::instance(); if (argc == 3) { if (strcmp(argv[1], "open") == 0) { TclObject* o = 0; if (strcmp(argv[2], "422") == 0) o = new VLGrabber(*this); else if (strcmp(argv[2], "411") == 0) o = new VL411Grabber(*this); else if (strcmp(argv[2], "cif") == 0) o = new VLCIFGrabber(*this); if (o != 0) tcl.result(o->name()); return (TCL_OK); } } return (InputDevice::command(argc, argv)); } vlBuilder::vlBuilder() { VLServer s = vlOpenVideo(""); if (s == 0) return; VLDevList devlist; if (vlGetDeviceList(s, &devlist) == 0) { for (int i = 0; i < devlist.numDevices; ++i) { VLDevice* p = &devlist.devices[i]; int n = p->numNodes; VLNodeInfo* ni = findports(p->nodes, n); if (n > 0) new vlDevice(p->name, p->dev, ni, n); } } vlCloseVideo(s); } /* * Lookup all video sources and smash spaces in names to dashes * to avoid tcl list problems. */ VLNodeInfo* vlBuilder::findports(VLNodeInfo* node, int& n) { VLNodeInfo* result = new VLNodeInfo[n]; int k = 0; for (int i = 0; i < n; ++i) { if (node[i].type == VL_SRC && node[i].kind == VL_VIDEO) { result[k] = node[i]; for (char* cp = result[k].name; *cp != 0; ++cp) if (isspace(*cp)) *cp = '-'; ++k; } } n = k; return (result); } u_char* VLGrabber::next_frame() { register u_char* p = 0; if (vlServer_ && transferBuf_) { /* * wait at most one frame time (30ms) for a frame to show up. */ VLInfoPtr info = 0; for (register int i = 3; --i >= 0; ) { info = vlGetLatestValid(vlServer_, transferBuf_); if (info != 0) break; sginap(1); } if (info) { p = (u_char*)vlGetActiveRegion(vlServer_, transferBuf_, info); if (p == NULL) vlPerror("vic: vlGetActiveRegion"); } } return (p); } /* 422 */ inline void saveblk(const u_char* in, u_char* yp, u_char* up, u_char* vp, int stride) { int is = stride << 2; int cs = stride >> 1; for (int i = 16; --i >= 0; ) { /* * Each iteration of this loop grabs 16 Ys & 8 U/Vs. */ register u_int y0, y1, u, v; u = in[0] << 24 | in[8] << 16 | in[16] << 8 | in[24]; v = in[2] << 24 | in[10] << 16 | in[18] << 8 | in[26]; y0 = in[1] << 24 | in[5] << 16 | in[9] << 8 | in[13]; y1 = in[17] << 24 | in[21] << 16 | in[25] << 8 | in[29]; ((u_int*)yp)[0] = y0; ((u_int*)yp)[1] = y1; *(u_int*)up = u; *(u_int*)vp = v; u = in[32+0] << 24 | in[32+8] << 16 | in[32+16] << 8 | in[32+24]; v = in[32+2] << 24 | in[32+10] << 16 | in[32+18] << 8 | in[32+26]; y0 = in[32+1] << 24 | in[32+5] << 16 | in[32+9] << 8 | in[32+13]; y1 = in[32+17] << 24 | in[32+21] << 16 | in[32+25] << 8 | in[32+29]; ((u_int*)yp)[2] = y0; ((u_int*)yp)[3] = y1; ((u_int*)up)[1] = u; ((u_int*)vp)[1] = v; in += is; yp += stride; up += cs; vp += cs; } } void VLGrabber::saveblks(const u_char* in) { const u_int8_t* crv = crvec_; u_int8_t* yp = frame_; u_int8_t* up = yp + framesize_; u_int off = framesize_ >> 1; int stride = 15 * outw_; for (int y = 0; y < blkh_; ++y) { for (int x = 0; x < blkw_; ++x) { int s = *crv++; if ((s & CR_SEND) != 0) saveblk(in, yp, up, up + off, outw_); in += 64; yp += 16; up += 8; } yp += stride; up += stride >> 1; in += stride << 2; } } /* * define these for REPLENISH macro used below */ #define DIFF4(in, frm, v) \ v += (in)[1] - (frm)[0]; \ v += (in)[5] - (frm)[1]; \ v += (in)[9] - (frm)[2]; \ v += (in)[13] - (frm)[3]; #define DIFFLINE(in, frm, left, center, right) \ DIFF4(in, frm, left); \ DIFF4(in + 1*16, frm + 1*4, center); \ DIFF4(in + 2*16, frm + 2*4, center); \ DIFF4(in + 3*16, frm + 3*4, right); \ if (right < 0) \ right = -right; \ if (left < 0) \ left = -left; \ if (center < 0) \ center = -center; void VLGrabber::suppress(const u_char* devbuf) { const u_char* start = frame_ + 16 * vstart_ * outw_ + 16 * hstart_; REPLENISH(devbuf, start, inw_ << 2, 4, hstart_, hstop_, vstart_, vstop_); } int VLGrabber::grab() { u_char* p = next_frame(); if (p == 0) return (0); suppress(p); saveblks(p); vlPutFree(vlServer_, transferBuf_); YuvFrame f(media_ts(), frame_, crvec_, outw_, outh_); return (target_->consume(&f)); } void VLGrabber::setsize(int w, int h) { set_size_422(w, h); } int VLGrabber::command(int argc, const char*const* argv) { if (argc == 3) { if (strcmp(argv[1], "decimate") == 0) { int d = atoi(argv[2]); Tcl& tcl = Tcl::instance(); if (d <= 0) { tcl.result("divide by zero"); return (TCL_ERROR); } decimate_ = d; startup_vl(); return (TCL_OK); } if (strcmp(argv[1], "port") == 0) { port_ = device_.lookup_port(argv[2]); startup_vl(); return (TCL_OK); } } return (Grabber::command(argc, argv)); } void VLGrabber::start() { startup_vl(); Grabber::start(); } void VLGrabber::stop() { shutdown_vl(); Grabber::stop(); } void VLGrabber::fps(int f) { Grabber::fps(f); startup_vl(); } void VLGrabber::shutdown_vl() { if (vlServer_) { if (vlPath_) { if (transferBuf_) { vlEndTransfer(vlServer_, vlPath_); vlDeregisterBuffer(vlServer_, vlPath_, drn_, transferBuf_); vlDestroyBuffer(vlServer_, transferBuf_); transferBuf_ = 0; } vlDestroyPath(vlServer_, vlPath_); vlPath_ = 0; } vlCloseVideo(vlServer_); vlServer_ = 0; } } static int vl_ntsc_rates[] = { 30, 25, 24, 20, 18, 15, 12, 10, 6, 5 }; static int vl_pal_rates[] = { 25, 20, 15, 10, 5 }; int VLGrabber::closest_rate(int fps) const { if (fps <= 5) return (5); int* rate = is_pal_ ? vl_pal_rates : vl_ntsc_rates; while (rate[1] > fps) ++rate; return (*rate); } void VLGrabber::startup_vl() { if (vlServer_ != 0 && decimate_ == current_decimate_ && fps_ == current_fps_ && port_ == current_port_) return; current_decimate_ = decimate_; current_fps_ = fps_; current_port_ = port_; shutdown_vl(); VLControlValue val; int xsize; int ysize; vlServer_ = vlOpenVideo(""); if (vlServer_ == 0) goto failed; src_ = vlGetNode(vlServer_, VL_SRC, VL_VIDEO, port_); drn_ = vlGetNode(vlServer_, VL_DRN, VL_MEM, VL_ANY); vlPath_ = vlCreatePath(vlServer_, device_.device(), src_, drn_); if (vlPath_ < 0) { vlPerror("vic: create path"); goto failed; } if (vlSetupPaths(vlServer_, (VLPathList)&vlPath_, 1, VL_SHARE, VL_SHARE) < 0) { vlPerror("vic: set up paths"); goto failed; } val.intVal = VL_PACKING_YVYU_422_8; if (vlSetControl(vlServer_, vlPath_, drn_, VL_PACKING, &val) < 0) { vlPerror("vic: set control packing"); goto failed; } vlGetControl(vlServer_, vlPath_, drn_, VL_SIZE, &val); xsize = val.xyVal.x; ysize = val.xyVal.y &~ 7; if (xsize != 640) /*XXX there must be a better way */ is_pal_ = 1; else is_pal_ = 0; xsize /= decimate_; ysize /= decimate_; val.fractVal.numerator = closest_rate(fps_); val.fractVal.denominator = 1; if (vlSetControl(vlServer_, vlPath_, drn_, VL_RATE, &val) < 0) vlPerror("vic: set rate"); val.intVal = decimate_ == 1? VL_CAPTURE_INTERLEAVED : VL_CAPTURE_EVEN_FIELDS; if (vlSetControl(vlServer_, vlPath_, drn_, VL_CAP_TYPE, &val) < 0) { vlPerror("vic: vl set captype"); goto failed; } if (decimate_ != 1) { val.fractVal.numerator = 1; val.fractVal.denominator = decimate_ >> 1; if (vlSetControl(vlServer_, vlPath_, drn_, VL_ZOOM, &val) < 0) vlPerror("vic: zoom"); } /* Create ring buffers to hold captured data of new size. */ transferBuf_ = vlCreateBuffer(vlServer_, vlPath_, drn_, 2); if (transferBuf_ == 0) { vlPerror("vic: create Buffer"); goto failed; } /* Tell VL to use newly created ring buffer. */ if (vlRegisterBuffer(vlServer_, vlPath_, drn_, transferBuf_) < 0) { vlPerror("vic: buffer registration"); goto failed; } /* * Try to get into frame sync for the first transfer to minimize * interlace artifacts in the captured picture. * it would be nice to use VL_TRANSFER_MODE_AUTOTRIGGER here so * we'd stay in frame sync but it seems to do nothing but make * videod hang with a galileo. */ #ifdef notdef VLTransferDescriptor xfer; xfer.mode = VL_TRANSFER_MODE_CONTINUOUS; xfer.trigger = VLFrameVerticalRetraceMask; xfer.delay = 0; xfer.count = 1; if (vlBeginTransfer(vlServer_, vlPath_, 1, &xfer) < 0) #endif if (vlBeginTransfer(vlServer_, vlPath_, 0, NULL) < 0) { vlPerror("vic: begin transfer"); goto failed; } #ifdef notdef /* * it would be nice to wait on frame available events from * videod rather than busy-waiting in next_frame() but the VL * interface doeson't expose the fd so you can't select on it. */ vlSelectEvents(vlServer_, vlPath_, VLNoEventsMask); #endif setsize(xsize, ysize); return; failed: shutdown_vl(); status_ = -1; } VLGrabber::VLGrabber(vlDevice& device) : device_(device), port_(VL_ANY) { frame_ = 0; framebase_ = 0; current_decimate_ = -1; current_fps_ = -1; current_port_ = -1; transferBuf_ = 0; Grabber::fps(2); decimate_ = 2; vlServer_ = 0; transferBuf_ = 0; vlPath_ = 0; } VLGrabber::~VLGrabber() { shutdown_vl(); } VLCIFGrabber::VLCIFGrabber(vlDevice& device) : VLGrabber(device) { } void VLCIFGrabber::setsize(int w, int h) { set_size_cif(w, h); } /* 411 */ inline void VLCIFGrabber::saveblk(const u_char* in, u_char* yp, u_char* up, u_char* vp, int stride, int is) { int cs = stride >> 1; for (int i = 8; --i >= 0; ) { register u_int y0, y1, u, v; u = in[0] << 24 | in[8] << 16 | in[16] << 8 | in[24]; v = in[2] << 24 | in[10] << 16 | in[18] << 8 | in[26]; y0 = in[1] << 24 | in[5] << 16 | in[9] << 8 | in[13]; y1 = in[17] << 24 | in[21] << 16 | in[25] << 8 | in[29]; ((u_int*)yp)[0] = y0; ((u_int*)yp)[1] = y1; ((u_int*)up)[0] = u; ((u_int*)vp)[0] = v; u = in[32+0] << 24 | in[32+8] << 16 | in[32+16] << 8 | in[32+24]; v = in[32+2] << 24 | in[32+10] << 16 | in[32+18] << 8 | in[32+26]; y0 = in[32+1] << 24 | in[32+5] << 16 | in[32+9] << 8 | in[32+13]; y1 = in[32+17] << 24 | in[32+21] << 16 | in[32+25] << 8 | in[32+29]; ((u_int*)yp)[2] = y0; ((u_int*)yp)[3] = y1; ((u_int*)up)[1] = u; ((u_int*)vp)[1] = v; in += is; yp += stride; up += cs; vp += cs; /* do the 2nd (y only instead of yuv) line */ y0 = in[1] << 24 | in[5] << 16 | in[9] << 8 | in[13]; y1 = in[17] << 24 | in[21] << 16 | in[25] << 8 | in[29]; ((u_int*)yp)[0] = y0; ((u_int*)yp)[1] = y1; y0 = in[32+1] << 24 | in[32+5] << 16 | in[32+9] << 8 | in[32+13]; y1 = in[32+17] << 24 | in[32+21] << 16 | in[32+25] << 8 | in[32+29]; ((u_int*)yp)[2] = y0; ((u_int*)yp)[3] = y1; in += is; yp += stride; } } void VLCIFGrabber::saveblks(const u_char* in) { int is = 4 * inw_; const u_int8_t* crv = crvec_; u_int8_t* lum = frame_; u_int8_t* chm = frame_ + framesize_; u_int off = framesize_ >> 2; crv += vstart_ * blkw_ + hstart_; lum += vstart_ * outw_ * 16 + hstart_ * 16; chm += vstart_ * (outw_ >> 1) * 8 + hstart_ * 8; int skip = hstart_ + (blkw_ - hstop_); for (int y = vstart_; y < vstop_; ++y) { const u_char* nin = in; for (int x = hstart_; x < hstop_; ++x) { int s = *crv++; if ((s & CR_SEND) != 0) saveblk(in, lum, chm, chm + off, outw_, is); in += 64; lum += 16; chm += 8; } crv += skip; lum += 15 * outw_ + skip * 16; chm += 7 * (outw_ >> 1) + skip * 8; in = nin + 16 * is; } } VL411Grabber::VL411Grabber(vlDevice& device) : VLCIFGrabber(device) { } void VL411Grabber::setsize(int w, int h) { set_size_411(w, h); }