root/vic/branches/mpeg4/video/grabber.cpp @ 3887

Revision 3887, 11.4 KB (checked in by barz, 8 years ago)

software scaling and xvido extension support

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1/*
2 * Copyright (c) 1993-1994 Regents of the University of California.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 *    must display the following acknowledgement:
15 *      This product includes software developed by the University of
16 *      California, Berkeley and the Network Research Group at
17 *      Lawrence Berkeley Laboratory.
18 * 4. Neither the name of the University nor of the Laboratory may be used
19 *    to endorse or promote products derived from this software without
20 *    specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 */
34
35#ifndef lint
36static const char rcsid[] =
37    "@(#) $Header$ (LBL)";
38#endif
39
40#if defined(__osf__) || defined(__ultrix__)
41/*XXX they didn't get this one right */
42extern "C" {
43#include <sys/types.h>
44#include <sys/uio.h>
45}
46#else
47#include <sys/types.h>
48#ifndef WIN32
49#include <sys/uio.h>
50#endif
51#endif
52
53#include <stdio.h>
54#include <stdlib.h>
55#include <errno.h>
56
57#include "grabber.h"
58#include "vic_tcl.h"
59#include "crdef.h"
60
61#if defined(sun) && !defined(__svr4__)
62extern "C" int gettimeofday(struct timeval*, struct timezone*);
63#endif
64
65//SV-XXX: rearranged initialisation order to shut up gcc4
66Grabber::Grabber()
67        : vstart_(0), vstop_(0),
68          hstart_(0), hstop_(0),
69          threshold_(48),
70          framebase_(0), frame_(0), crvec_(0), ref_(0),
71          inw_(0), inh_(0), outw_(0), outh_(0),
72          target_(0), tx_(0), rover_(0),
73          running_(0), status_(0), delta_(0.)
74{
75        bps(128);
76        fps(1);
77
78        /*XXX*/
79        idle_low_ = 2;
80        idle_high_ = 2;
81
82        /* CCIR 601 */
83        ymin_ = 16;
84        ymax_ = 235;
85        contrast_ = 1.0;
86        for (int i = 0; i < 256; ++i)
87                ynorm_[i] = i;
88}
89
90Grabber::~Grabber()
91{
92        delete[] framebase_; //SV-XXX: Debian
93        delete[] crvec_; //SV-XXX: Debian
94        delete[] ref_; //SV-XXX: Debian
95}
96
97int Grabber::command(int argc, const char*const* argv)
98{
99        Tcl& tcl = Tcl::instance();
100        if (argc == 2) {
101                if (strcmp(argv[1], "status") == 0) {
102                        sprintf(tcl.buffer(), "%d", status_);
103                        tcl.result(tcl.buffer());
104                        return (TCL_OK);
105                }
106                if (strcmp(argv[1], "need-capwin") == 0) {
107                        tcl.result("0");
108                        return (TCL_OK);
109                }
110        }
111        if (argc == 3) {
112                if (strcmp(argv[1], "send") == 0) {
113                        if (atoi(argv[2])) {
114                                if (!running_) {
115                                        start();
116                                        running_ = 1;
117                                }
118                        } else {
119                                if (running_) {
120                                        stop();
121                                        running_ = 0;
122                                }
123                        }
124                        return (TCL_OK);
125                }
126                if (strcmp(argv[1], "threshold") == 0) {
127                        threshold_ = 8*atoi(argv[2]);
128                        return (TCL_OK);
129                }
130                if (strcmp(argv[1], "fps") == 0) {
131                        /*XXX assume value in range */
132                        fps(atoi(argv[2]));
133                        return (TCL_OK);
134                }
135                if (strcmp(argv[1], "bps") == 0) {
136                        /*XXX assume value in range */
137                        bps(atoi(argv[2]));
138                        return (TCL_OK);
139                }
140                if (strcmp(argv[1], "fillrate") == 0) {
141                        /*XXX assume value in range */
142                        fillrate(atoi(argv[2]));
143                        return (TCL_OK);
144                }
145                if (strcmp(argv[1], "target") == 0) {
146                        target_ = (Module*)TclObject::lookup(argv[2]);
147                        return (TCL_OK);
148                }
149                if (strcmp(argv[1], "transmitter") == 0) {
150                        tx_ = (Transmitter*)TclObject::lookup(argv[2]);
151                        return (TCL_OK);
152                }
153                /*XXX*/
154                if (strcmp(argv[1], "decimate") == 0 ||
155                    strcmp(argv[1], "port") == 0 ||
156                    strcmp(argv[1], "type") == 0 ||
157                        /* windows only */
158                        strcmp(argv[1], "useconfig") ==0)
159                        /* ignore */
160                        return (TCL_OK);
161        }
162        return (TclObject::command(argc, argv));
163}
164
165void Grabber::contrast(double c)
166{
167        /* map min to ccir-601 black (16) & max to ccir-601 white (235) */
168        u_int min = ymin_, max = ymax_;
169        double dmin = min, dmax = max;
170        double dslope = 219. / (dmax - dmin) * c;
171        double dy = 16.;
172        int i;
173        for (i = 0; i < min; ++i)
174                ynorm_[i] = u_char(dy);
175        for ( ; i < max; ++i) {
176                ynorm_[i] = u_char(dy);
177                if (dy < 235.)
178                        dy += dslope;
179        }
180        for ( ; i < 256; ++i)
181                ynorm_[i] = u_char(dy);
182
183        contrast_ = c;
184}
185
186void Grabber::fps(int v)
187{
188        fps_ = v;
189        frametime_ = 1e6 / double(v);
190}
191
192void Grabber::bps(int kbps)
193{
194        bps_ = 1000 * kbps;
195}
196
197/*
198 * Advance the frame clock and return the amount of time we
199 * need to wait before sending the next frame.  We compute
200 * this time according to the desired bit and frame rates,
201 * favoring the more restrictive metric.  If we're more than
202 * 200ms behind (e.g., the cpu is saturated or we've been
203 * suspended), give up and reset the frame clock.
204 */
205double Grabber::tick(int n)
206{
207        double frametime = 8e6 * double(n) / double(bps_);
208        if (frametime < frametime_) {
209                if (frametime * 2. < frametime_)
210                        delta_ += (frametime - delta_) * .25;
211                else
212                        delta_ = frametime;
213                frametime = frametime_;
214        } else
215                delta_ = frametime;
216
217        frameclock_ += frametime;
218        double now = gettimeofday();
219        double delta = frameclock_ - now;
220        if (delta < -0.2e6) {
221                delta = frametime;
222                frameclock_ = now;
223        } else if (delta < 0)
224                /*
225                 * We're not too far behind.
226                 * Try to catch up.
227                 */
228                delta = 0.;
229
230        return (delta);
231}
232
233void Grabber::start()
234{
235        frameclock_ = gettimeofday();
236#ifndef WIN32
237        timeout();
238#endif
239}
240
241void Grabber::stop()
242{
243        cancel();
244}
245
246void Grabber::timeout()
247{
248        for (;;) {
249                double delta = tick(grab());
250                if (delta != 0.) {
251                        usched(delta);
252                        return;
253                }
254        }
255}
256
257int Grabber::grab()
258{
259        abort();
260        return (0);
261}
262
263void Grabber::crinit(int w, int h)
264{
265        blkw_ = w >> 4;
266        blkh_ = h >> 4;
267        scan_ = 0;
268        nblk_ = blkw_ * blkh_;
269        delete[] crvec_; //SV-XXX: Debian
270        crvec_ = new u_char[nblk_];
271        for (int i = 0; i < nblk_; ++i)
272                crvec_[i] = CR_MOTION|CR_SEND;
273}
274
275/* must call after set_size_xxx */
276void Grabber::allocref()
277{
278        delete[] ref_; //SV-XXX: Debian
279        ref_ = new u_char[framesize_];
280        memset((char*)ref_, 0, framesize_);
281}
282
283/*
284 * define these for REPLENISH macro used below
285 */
286#define ABS(v) if (v < 0) v = -v;
287
288#define DIFF4(in, frm, v) \
289        v += (in)[0] - (frm)[0]; \
290        v += (in)[1] - (frm)[1]; \
291        v += (in)[2] - (frm)[2]; \
292        v += (in)[3] - (frm)[3];
293
294#define DIFFLINE(in, frm, left, center, right) \
295        DIFF4(in, frm, left); \
296        DIFF4(in + 1*4, frm + 1*4, center); \
297        DIFF4(in + 2*4, frm + 2*4, center); \
298        DIFF4(in + 3*4, frm + 3*4, right); \
299        ABS(right); \
300        ABS(left); \
301        ABS(center);
302
303void Grabber::suppress(const u_char* devbuf)
304{
305        REPLENISH(devbuf, ref_, outw_, 1, 0, blkw_, 0, blkh_);
306}
307
308inline void save(const u_char* lum, u_char* cache, int stride)
309{
310        for (int i = 16; --i >= 0; ) {
311                ((u_int*)cache)[0] = ((u_int*)lum)[0];
312                ((u_int*)cache)[1] = ((u_int*)lum)[1];
313                ((u_int*)cache)[2] = ((u_int*)lum)[2];
314                ((u_int*)cache)[3] = ((u_int*)lum)[3];
315                cache += stride;
316                lum += stride;
317        }
318}
319
320/*
321 * Default save routine -- stuff new luma blocks into cache.
322 */
323void Grabber::saveblks(u_char* lum)
324{
325        u_char* crv = crvec_;
326        u_char* cache = ref_;
327        int stride = outw_;
328        stride = (stride << 4) - stride;
329        for (int y = 0; y < blkh_; y++) {
330                for (int x = 0; x < blkw_; x++) {
331                        if ((*crv++ & CR_SEND) != 0)
332                                save(lum, cache, outw_);
333                        cache += 16;
334                        lum += 16;
335                }
336                lum += stride;
337                cache += stride;
338        }
339}
340
341void Grabber::age_blocks()
342{
343        for (int i = 0; i < nblk_; ++i) {
344                int s = CR_STATE(crvec_[i]);
345                /*
346                 * Age this block.
347                 * Once we hit the age threshold, we
348                 * set CR_SEND as a hint to send a
349                 * higher-quality version of the block.
350                 * After this the block will stop aging,
351                 * until there is motion.  In the meantime,
352                 * we might send it as background fill
353                 * using the highest quality.
354                 */
355                if (s <= CR_AGETHRESH) {
356                        if (s == CR_AGETHRESH)
357                                s = CR_IDLE;
358                        else {
359                                if (++s == CR_AGETHRESH)
360                                        s |= CR_SEND;
361                        }
362                        crvec_[i] = s;
363                } else if (s == CR_BG)
364                        /*
365                         * reset the block to IDLE if it was sent
366                         * as a BG block in the last frame.
367                         */
368                        crvec_[i] = CR_IDLE;
369        }
370        /*
371         * Now go through and look for some idle blocks to send
372         * as background fill.
373         */
374        int blkno = rover_;
375        int n = (delta_ * 2. < frametime_)? idle_high_ : idle_low_;
376        while (n > 0) {
377                int s = CR_STATE(crvec_[blkno]);
378                if (s == CR_IDLE) {
379                        crvec_[blkno] = CR_SEND|CR_BG;
380                        --n;
381                }
382                if (++blkno >= nblk_) {
383                        blkno = 0;
384                        /* one way to guarantee loop termination */
385                        break;
386                }
387        }
388        rover_ = blkno;
389
390        /*
391         * Bump the CR scan pointer.  This variable controls which
392         * scan line of a block we use to make the replenishment
393         * decision.  We skip 3 lines at a time to quickly precess
394         * over the block.  Since 3 and 8 are coprime, we will
395         * sweep out every line.
396         */
397        scan_ = (scan_ + 3) & 7;
398}
399
400void Grabber::set_size_422(int w, int h)
401{
402        delete[] framebase_; //SV-XXX: Debian
403
404        inw_ = w;
405        inh_ = h;
406        w &=~ 0xf;
407        h &=~ 0xf;
408        outw_ = w;
409        outh_ = h;
410
411        framesize_ = w * h;
412        int n = 2 * framesize_ + 2 * GRABBER_VPAD * w;
413        framebase_ = new u_char[n];
414        memset(framebase_, 0x80, n);
415        frame_ = framebase_ + GRABBER_VPAD * w;
416        crinit(w, h);
417
418        vstart_ = 0;
419        vstop_ = blkh_;
420        hstart_ = 0;
421        hstop_ = blkw_;
422}
423
424void Grabber::set_size_411(int w, int h)
425{
426        delete[] framebase_; //SV-XXX: Debian
427
428        inw_ = w;
429        inh_ = h;
430        w &=~ 0xf;
431        h &=~ 0xf;
432        outw_ = w;
433        outh_ = h;
434
435        int s = w * h;
436        framesize_ = s;
437        int n = s + (s >> 1) + 2 * GRABBER_VPAD * outw_;
438        framebase_ = new u_char[n];
439        /* initialize to gray */
440        memset(framebase_, 0x80, n);
441        frame_ = framebase_ + GRABBER_VPAD * outw_;
442        crinit(w, h);
443
444        vstart_ = 0;
445        vstop_ = blkh_;
446        hstart_ = 0;
447        hstop_ = blkw_;
448}
449
450void Grabber::set_size_cif(int w, int h)
451{
452        delete[] framebase_; //SV-XXX: Debian
453        inw_ = w;
454        inh_ = h;
455
456        int ispal;
457        switch (w) {
458        case 640:
459                /* for qcam */
460                ispal = 0;
461                outw_ = 640;
462                outh_ = 480;
463                break;
464        case 320:
465                /* 1/2 NTSC */
466                ispal = 0;
467                outw_ = 352;
468                outh_ = 288;
469                break;
470
471        case 160:
472                /* 1/8 NTSC */
473                ispal = 0;
474                outw_ = 176;
475                outh_ = 144;
476                break;
477
478        case 352:
479                /* 1/2 CIF */
480                ispal = 1;
481                outw_ = 352;
482                outh_ = 288;
483                break;
484
485        case 176:
486                /* 1/8 CIF */
487                ispal = 1;
488                outw_ = 176;
489                outh_ = 144;
490
491        case 384:
492                /* 1/2 PAL */
493                ispal = 1;
494                outw_ = 352;
495                outh_ = 288;
496                break;
497
498        case 192:
499                /* 1/8 PAL */
500                ispal = 1;
501                outw_ = 176;
502                outh_ = 144;
503                break;
504
505        default:
506                /* XXX this shouldn't happen */
507                fprintf(stderr, "vic: CIF grabber: bad geometry\n");
508                abort();
509        }
510        int s = outw_ * outh_;
511        framesize_ = s;
512        int n = s + (s >> 1) + 2 * GRABBER_VPAD * outw_;
513        framebase_ = new u_char[n];
514        /* initialize to gray */
515        memset(framebase_, 0x80, n);
516        frame_ = framebase_ + GRABBER_VPAD * outw_;
517        crinit(outw_, outh_);
518
519        if (ispal) {
520                /* PAL: field is bigger than CIF */
521                vstart_ = 0;
522                vstop_ = blkh_;
523                hstart_ = 0;
524                hstop_ = blkw_;
525        } else {
526                /* NTSC: field is smaller than CIF */
527                vstart_ = 1;
528                vstop_ = vstart_ + inh_ / 16;
529                int nw = inw_ / 16;
530                int margin = (blkw_ - nw + 1) / 2;
531                hstart_ = margin;
532                hstop_ = blkw_ - margin;
533        }
534}
Note: See TracBrowser for help on using the browser.