root/vic/branches/mpeg4/video/grabber-win32.cpp @ 4064

Revision 4064, 28.2 KB (checked in by piers, 7 years ago)

Updates to fix grabbers on windows:
- Old vfw Win32 made more robust to failing grabbers
- Win32DS now attempts to select best resolution to fix the selected capture size. It is also now possible to select PAL or NTSC from Signal.. menu to adjust captured video size.
- Altered code so it chooses the capture size before calling RenderStream? on filterGraph - that way the filter graph manager can insert the appropriate colour space converter.
- Uses set_size_cif() in size() - which falls thru to ste_size_411() not handled. Fixed bug in Grabber.cpp: set_size_cif() where width=176 didn't have a break statement.
- tweaked ui-ctrlmenu.tcl so Signal..(type) is set before decimate on - so win32 grabber can use it.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1/*
2 *  Copyright (c) 1996 John Brezak
3 *  Copyright (c) 1996 Isidor Kouvelas (University College London)
4 *  All rights reserved.
5 *
6 *  Redistribution and use in source and binary forms, with or without
7 *  modification, are permitted provided that the following conditions
8 *  are met:
9 *  1. Redistributions of source code must retain the above copyright
10 *     notice, this list of conditions and the following disclaimer.
11 *  2. Redistributions in binary form must reproduce the above copyright
12 *     notice, this list of conditions and the following disclaimer in the
13 *     documentation and/or other materials provided with the distribution.
14 *  3. The name of the author may not be used to endorse or promote products
15 *     derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
21 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
25 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 * POSSIBILITY OF SUCH DAMAGE.
28 */
29#include <stdio.h>
30#include <assert.h>
31#include <stdlib.h>
32#include "config.h"
33#include <windows.h>
34//#include <mmsystem.h>
35#include <vfw.h>
36
37#include "grabber.h"
38#include "device-input.h"
39#include "module.h"
40#include "rgb-converter.h"
41
42
43extern "C" HINSTANCE TkWinGetAppInstance();
44
45int capture_;
46
47static const int NTSC_BASE_WIDTH  = 640;
48static const int NTSC_BASE_HEIGHT = 480;
49static const int PAL_BASE_WIDTH  = 768;
50static const int PAL_BASE_HEIGHT = 568;
51static const int CIF_BASE_WIDTH  = 704;
52static const int CIF_BASE_HEIGHT = 576;
53
54static int bit_options[] = { 16, 24, 32, 8, 4, 1, 0 };
55
56enum device_type_e {
57        Generic,
58        gray_QuickCam_95,
59        gray_QuickCam_NT,
60        MCT,
61        SMII,
62        Miro_dc20_95,
63        Miro_dc20_NT,
64        AV_Master,
65    Intel_SVR3,
66};
67
68static device_type_e
69get_device_type(const char *deviceName)
70{
71        if (!strncmp(deviceName, "QuickCam", 8)) {
72                debug_msg("Device=gray_QuickCam_95\n");
73                return (gray_QuickCam_95);
74        }
75        if (!strncmp(deviceName, "quickcam.dll", 12)) {
76                debug_msg("Device=gray_QuickCam_NT\n");
77                return (gray_QuickCam_NT);
78        }
79        if (!strncmp(deviceName, "miroVIDEO DC20", 14)) {
80                debug_msg("Device=Miro_dc20_95\n");
81                return (Miro_dc20_95);
82        }
83        if (!strncmp(deviceName, "dc20.dll", 8)) {
84                debug_msg("Device=Miro_dc20_NT\n");
85                return (Miro_dc20_NT);
86        }
87        if (!strncmp(deviceName, " MCT VMPlus", 10)) {
88                debug_msg("Device=MCT\n");
89                return (MCT);
90        }
91        if (!strncmp(deviceName, "Screen Machine II", 16)) {
92                debug_msg("Device=SMII\n");
93                return (SMII);
94        }
95        if (!strncmp(deviceName, "AV Master", 9)) {
96                debug_msg("Device=AV_Master\n");
97                return (AV_Master);
98        }
99        if (!strncmp(deviceName, "ISVR III", 8)) {
100                debug_msg("Device=ISVR III\n");
101                return (Intel_SVR3);
102    }
103        debug_msg("Device=Generic: %s\n", deviceName);
104        return (Generic);
105}
106
107static char *
108get_comp_name(DWORD fourcc)
109{
110        static char name[5];
111
112        switch (fourcc) {
113        case 0:
114                return ("BI_RGB");
115        default:
116                char *p = (char *)&fourcc;
117                for (int i = 0; i < 4; i++)
118                        if (isprint(p[i]))
119                                name[i] = p[i];
120                        else
121                                name[i] = ' ';
122                name[4] = 0;
123                return (name);
124        }
125}
126
127class IC_Converter : public Converter {
128public:
129        IC_Converter(DWORD comp, int bpp, int inw, int inh);
130        ~IC_Converter();
131        virtual void convert(u_int8_t *in, int inw, int inh, u_int8_t *frm, int outw, int outh, int invert);
132protected:
133        BITMAPINFOHEADER        bihIn_, bihOut_;
134        HIC                     hIC_;
135        RGB_Converter           *converter_;
136        u_char                  *rgb_;
137        int                     rgb_bpp_;
138};
139
140IC_Converter::IC_Converter(DWORD comp, int bpp, int inw, int inh)
141        : converter_(0), rgb_(0), rgb_bpp_(0), hIC_(0)
142{
143        debug_msg("IC_Converter: comp=%x (%s) bpp=%d\n", comp, get_comp_name(comp), bpp);
144
145        bihIn_.biSize = bihOut_.biSize = sizeof(BITMAPINFOHEADER);
146        bihIn_.biXPelsPerMeter = bihIn_.biYPelsPerMeter = bihOut_.biXPelsPerMeter = bihOut_.biYPelsPerMeter = 0;
147        bihIn_.biPlanes = bihOut_.biPlanes= 1;
148
149        bihIn_.biCompression = comp;
150        bihIn_.biClrUsed = bihOut_.biClrImportant = 0;
151        bihIn_.biWidth = inw;
152        bihIn_.biHeight = inh;
153        bihIn_.biBitCount = bpp;
154        bihIn_.biSizeImage = bihIn_.biWidth * bihIn_.biHeight * bihIn_.biPlanes * bihIn_.biBitCount / 8;
155
156        bihOut_.biCompression = BI_RGB;
157        bihOut_.biClrUsed = bihOut_.biClrImportant = 0;
158        bihOut_.biWidth = inw;
159        bihOut_.biHeight = inh;
160        int i = 0;
161        do {
162                bihOut_.biBitCount = bit_options[i++];
163                bihOut_.biSizeImage = bihOut_.biWidth * bihOut_.biHeight * bihOut_.biPlanes * bihOut_.biBitCount / 8;
164                if (hIC_ = ICLocate(ICTYPE_VIDEO, 0L, (LPBITMAPINFOHEADER)&bihIn_, (LPBITMAPINFOHEADER)&bihOut_, ICMODE_DECOMPRESS))
165                        break;
166        } while (bihOut_.biBitCount > 0);
167
168        if (hIC_) {
169                ICINFO icinfo;
170                ICGetInfo(hIC_, &icinfo, sizeof(icinfo));
171                debug_msg("IC: Located %s with bpp %d\n", icinfo.szName, bihOut_.biBitCount);
172        }
173
174        if (bihOut_.biBitCount <= 8)
175                hIC_ = 0;
176
177        rgb_ = new u_char[bihOut_.biSizeImage];
178        rgb_bpp_ = bihOut_.biBitCount;
179        debug_msg("IC_Converter: rgb_bpp_ = %d\n", rgb_bpp_);
180
181        if (hIC_ == 0)
182                fprintf(stderr, "ICLocate: Unable to find supported bpp for format %x!\n", comp);
183        else if (ICDecompressBegin(hIC_, &bihIn_, &bihOut_) != ICERR_OK) {
184                fprintf(stderr, "ICDecompressBegin failed!\n");
185                hIC_ = 0;
186        }
187}
188
189IC_Converter::~IC_Converter()
190{
191        if (rgb_) {
192                delete [] rgb_;
193                rgb_ = 0;
194        }
195        if (hIC_) {
196                ICDecompressEnd(hIC_);
197                hIC_ = 0;
198        }
199        if (converter_) {
200                delete converter_;
201                converter_ = 0;
202        }
203}
204
205void
206IC_Converter::convert(u_int8_t *in, int inw, int inh, u_int8_t *frm, int outw, int outh, int invert)
207{
208        if (hIC_ == 0)
209                return;
210
211        if (ICDecompress(hIC_, 0, &bihIn_, in, &bihOut_, rgb_) != ICERR_OK)
212                debug_msg("ICDecompress failed!\n");
213
214        converter_->convert(rgb_, inw, inh, frm, outw, outh, invert);
215}
216
217class IC_Converter_411 : public IC_Converter {
218public:
219        IC_Converter_411(DWORD comp, int bpp, int inw, int inh);
220};
221
222IC_Converter_411::IC_Converter_411(DWORD comp, int bpp, int inw, int inh)
223        : IC_Converter(comp, bpp, inw, inh)
224{
225        converter_ = new RGB_Converter_411(rgb_bpp_, NULL, 0);
226}
227
228class IC_Converter_422 : public IC_Converter {
229public:
230        IC_Converter_422(DWORD comp, int bpp, int inw, int inh);
231};
232
233IC_Converter_422::IC_Converter_422(DWORD comp, int bpp, int inw, int inh)
234        : IC_Converter(comp, bpp, inw, inh)
235{
236        converter_ = new RGB_Converter_422(rgb_bpp_, NULL, 0);
237}
238
239class YUYV_Converter_411 : public Converter {
240public:
241        virtual void convert(u_int8_t *in, int inw, int inh, u_int8_t *frm, int outw, int outh, int invert = 0);
242};
243
244void YUYV_Converter_411::convert(u_int8_t *in, int inw, int inh, u_int8_t *frm, int outw, int outh, int invert)
245{
246        u_int8_t *yp = (u_int8_t*)frm;
247        int off = outw * outh;
248        u_int8_t *up = (u_int8_t*)(frm + off);
249        off += off >> 2;
250        u_int8_t *vp = (u_int8_t*)(frm + off);
251
252        unsigned short *p = (unsigned short *)in;
253
254        int h = min(inh, outh);
255        int w = min(inw, outw);
256
257        int next_line = inw * 2;
258        if (invert) {
259                in += 2 * inw * (inh - 1 - (inh - h) / 2);
260                next_line = -next_line;
261        } else
262                in += 2 * inw * (inh - h) / 2;
263        int inpad = (inw - w) * 2;
264        in += inpad / 2;
265        int outpad = outw - w;
266        int outvpad = ((outh - h) / 2) & (outw > 176? ~0xf: ~0x3);
267
268        yp += outw * outvpad + outpad / 2;
269        up += outw / 4 * outvpad + outpad / 4;
270        vp += outw / 4 * outvpad + outpad / 4;
271
272        for (h >>= 1; --h > 0;) {
273                for (int x = w; x > 0; x -= 2) {
274                        yp[outw] = in[next_line];
275                        *yp++ = *in++;
276                        *up++ = *in++ ^ 0x80;
277
278                        yp[outw] = in[next_line];
279                        *yp++ = *in++;
280                        *vp++ = *in++ ^ 0x80;
281                }
282                in += inpad;
283                yp += outw + outpad;
284                up += outpad >> 1;
285                vp += outpad >> 1;
286                if (invert)
287                        in -= 6 * inw;
288                else
289                        in += inw << 1;
290        }
291}
292
293class YUYV_Converter_422 : public Converter {
294public:
295        virtual void convert(u_int8_t *in, int inw, int inh, u_int8_t *frm, int outw, int outh, int invert = 0);
296};
297
298void YUYV_Converter_422::convert(u_int8_t *in, int inw, int inh, u_int8_t *frm, int outw, int outh, int invert)
299{
300        u_int8_t *yp = (u_int8_t*)frm;
301        int off = outw * outh;
302        u_int8_t *up = (u_int8_t*)(frm + off);
303        off += off >> 1;
304        u_int8_t *vp = (u_int8_t*)(frm + off);
305
306        assert(inw == outw);
307       
308        if (invert)
309                in += 2 * inw * (inh - 1 - (inh - outh) / 2);
310        else
311                in += 2 * inw * (inh - outh) / 2;
312
313        for (int h = outh; h > 0; h--) {
314                for (int w = outw; w > 0; w -= 2) {
315                        *yp++ = *in++;
316                        *up++ = *in++ ^ 0x80;
317                        *yp++ = *in++;
318                        *vp++ = *in++ ^ 0x80;
319                }
320                if (invert)
321                        in -= inw << 2;
322        }
323}
324
325class VfwGrabber;
326class VfwGrabber : public Grabber {
327 public:
328        VfwGrabber(const int dev);
329        virtual ~VfwGrabber();
330        virtual int command(int argc, const char*const* argv);
331        inline void converter(Converter* v) { converter_ = v; }
332        void capture(VfwGrabber *gw, LPBYTE);
333        inline int is_pal() const { return (max_fps_ == 25); }
334        int capturing_;
335
336 protected:
337        virtual void start();
338        virtual void stop();
339        virtual void fps(int);
340        virtual void setsize() = 0;
341        virtual int grab();
342        void setport(const char *port);
343
344        device_type_e devtype_;
345        int useconfig_;
346        int dev_;
347        int connected_;
348        u_int max_fps_;
349        int basewidth_;
350        int baseheight_;
351        u_int decimate_;        /* division of base sizes */
352       
353        HWND                    capwin_;
354        CAPDRIVERCAPS           caps_;
355        CAPSTATUS               status_;
356        CAPTUREPARMS            parms_;
357        LPBITMAPINFOHEADER      fmt_;
358        u_int                   fmtsize_;
359
360        HANDLE                  frame_sem_;
361    HANDLE          cb_mutex_;
362        LPBYTE                  last_frame_;
363        Converter               *converter_;
364       
365 private:
366        static LRESULT CALLBACK VideoHandler(HWND, LPVIDEOHDR);
367        static LRESULT CALLBACK ErrorHandler(HWND, int, LPCSTR);
368};
369
370class VfwCIFGrabber : public VfwGrabber
371{
372 public:
373        VfwCIFGrabber(const int dev);
374        ~VfwCIFGrabber();
375 protected:
376        virtual void start();
377        virtual void setsize();
378};
379
380class Vfw422Grabber : public VfwGrabber
381{
382 public:
383        Vfw422Grabber(const int dev);
384        ~Vfw422Grabber();
385 protected:
386        virtual void start();
387        virtual void setsize();
388};
389
390class VfwDevice : public InputDevice {
391 public:
392        VfwDevice(const char* name, int index);
393        ~VfwDevice();
394        virtual int command(int argc, const char*const* argv);
395 protected:
396        DWORD vfwdev_;
397        VfwGrabber *grabber_;
398};
399
400#define NUM_DEVS 4
401
402class VfwScanner {
403 public:
404        VfwScanner(const int n);
405        ~VfwScanner();
406 protected:
407        VfwDevice *devs_[NUM_DEVS];
408};
409
410static VfwScanner find_vfw_devices(NUM_DEVS);
411
412VfwScanner::VfwScanner(const int n)
413{
414        char deviceName[80] ;
415        char deviceVersion[100] ;
416       
417        for (int index = 0 ; index < n; index++) {
418                if (capGetDriverDescription(index,
419                                            (LPSTR)deviceName,
420                                            sizeof(deviceName),
421                                            (LPSTR)deviceVersion,
422                                            sizeof(deviceVersion))) {
423                        debug_msg("Adding device %d\n", index);
424                        devs_[index] = new VfwDevice(strdup(deviceName), index);
425                } else
426                        devs_[index] = NULL;
427        }
428}
429
430VfwScanner::~VfwScanner()
431{
432        debug_msg("~VfwScanner\n");
433        for (int i = 0; i < NUM_DEVS; i++)
434                if (devs_[i]) {
435                        debug_msg("Deleteing device %d\n", i);
436                        delete devs_[i];
437        }
438}
439
440VfwDevice::VfwDevice(const char* name, int index) :
441        InputDevice(name), vfwdev_(DWORD(-1)), grabber_(0)
442{
443        debug_msg("VfwDevice: [%d] \"%s\"\n", index, name);
444
445        if (index >= 0) {
446                vfwdev_ = index;
447                switch (get_device_type(name)) {
448                case gray_QuickCam_95:
449                        attributes_ = "format { 422 411 } size { small cif } port { QuickCam } ";
450                        break;
451                case Generic:
452                default:
453                        attributes_ = "format { 422 411 } size { large small cif } port { external-in } ";
454                        break;
455                }
456        } else
457                attributes_ = "disabled";
458}
459
460VfwDevice::~VfwDevice()
461{
462        /* The following should not be necessary but there is no call to
463         * delete the grabber when vic exits while capturing. */
464        if (grabber_) {
465                if (capture_==1) {
466                        debug_msg("VfwDevice::~VfwDevice deleting grabber!\n");
467                        delete grabber_;
468                }
469        }
470}
471
472int VfwDevice::command(int argc, const char*const* argv)
473{
474        Tcl& tcl = Tcl::instance();
475        if ((argc == 3) && (strcmp(argv[1], "open") == 0)) {
476                TclObject* o = 0;
477                if (strcmp(argv[2], "422") == 0)
478                        o = grabber_ = new Vfw422Grabber(vfwdev_);
479                else if (strcmp(argv[2], "cif") == 0)
480                        o = grabber_ = new VfwCIFGrabber(vfwdev_);
481                if (o != 0)
482                        Tcl::instance().result(o->name());
483                return (TCL_OK);
484        }
485        return (InputDevice::command(argc, argv));
486}
487
488VfwGrabber::VfwGrabber(const int dev) : dev_(dev), connected_(0),
489        last_frame_(0), devtype_(Generic), useconfig_(0), converter_(0), fmt_(0), frame_sem_(0), cb_mutex_(0)
490{
491        char deviceName[80] ;
492        char deviceVersion[100] ;
493       
494        if (!capGetDriverDescription(dev, (LPSTR)deviceName, sizeof(deviceName), (LPSTR)deviceVersion, sizeof(deviceVersion))) {
495                fprintf(stderr, "VfwGrabber: Cannot get driver info!\n");
496                Grabber::status_=-1;
497                return;
498        }
499
500        devtype_ = get_device_type(deviceName);
501        setport("external");
502        if (is_pal()) {
503                basewidth_ = PAL_BASE_WIDTH;
504                baseheight_ = PAL_BASE_HEIGHT;
505        } else {
506                basewidth_ = NTSC_BASE_WIDTH;
507                baseheight_ = NTSC_BASE_HEIGHT;
508        }
509}
510
511VfwGrabber::~VfwGrabber()
512{
513        debug_msg("~VfwGrabber\n");
514        if (capwin_) {
515                if (capturing_) {
516                        debug_msg("stopping...\n");
517                        capCaptureStop(capwin_);
518                        ReleaseSemaphore(frame_sem_, 1, NULL);
519                        CloseHandle(frame_sem_);
520                        capturing_ = 0;
521                        capture_=0;
522                }
523                if (connected_) {
524                        debug_msg("disconnecting...\n");
525                        capDriverDisconnect(capwin_);
526                        connected_ = 0;
527                }
528                capSetCallbackOnVideoStream(capwin_, NULL);
529                DestroyWindow(capwin_);
530        }
531}
532
533VfwCIFGrabber::VfwCIFGrabber(const int dev) : VfwGrabber(dev)
534{
535        debug_msg("VfwCIFGrabber\n");
536}
537
538VfwCIFGrabber::~VfwCIFGrabber()
539{
540        debug_msg("~VfwCIFGrabber\n");
541}
542
543Vfw422Grabber::Vfw422Grabber(const int dev) : VfwGrabber(dev)
544{
545        debug_msg("Vfw422Grabber\n");
546}
547
548Vfw422Grabber::~Vfw422Grabber()
549{
550        debug_msg("~Vfw422Grabber\n");
551}
552void Vfw422Grabber::setsize()
553{
554        int w = basewidth_ / decimate_;
555        int h = baseheight_ / decimate_;
556        debug_msg("Vfw422Grabber::setsize: %dx%d\n", w, h);
557        set_size_422(w, h);
558        allocref();
559}
560
561void VfwCIFGrabber::setsize()
562{
563        int w = basewidth_ / decimate_;
564        int h = baseheight_ / decimate_;
565        debug_msg("VfwCIFGrabber::setsize: %dx%d\n", w, h);
566        set_size_cif(w, h);
567        allocref();
568}
569
570void VfwGrabber::fps(int f)
571{
572        if (f <= 0)
573                f = 1;
574        else if (u_int(f) > max_fps_)
575                f = max_fps_;
576        Grabber::fps(f);
577
578#ifdef NDEF
579        if (capturing_) {
580                stop();
581                start();
582        }
583#endif
584}
585
586extern "C" {
587extern char **__argv;
588}
589
590void VfwGrabber::start()
591{
592        debug_msg("VfwGrabber::start() thread=%x\n", GetCurrentThreadId());
593        debug_msg("basewidth_=%d, baseheight_=%d, decimate_=%d\n", basewidth_, baseheight_, decimate_);
594
595        /*
596         * The quickcam driver seems to completely ignore the information
597         * given in the capSetVideoFormat call. To get around this we have
598         * to set the desired values in the quickcam.ini file before we
599         * connect the driver.
600         */
601        if (devtype_ == gray_QuickCam_95) {
602                /* Should really use binary name in calls below instad of Vic */
603                debug_msg("argv %s\n", *__argv);
604                switch (decimate_) {
605                case 4:
606                        WritePrivateProfileString("Vic", "Size40", "4", "quickcam.ini");
607                        WritePrivateProfileString("Vic", "Xfermode", "4", "quickcam.ini");
608                        break;
609                case 2:
610                        WritePrivateProfileString("Vic", "Size40", "8", "quickcam.ini");
611                        WritePrivateProfileString("Vic", "Xfermode", "0", "quickcam.ini");
612                        break;
613                default:
614                        debug_msg("Quickcam cannot do this decimation!\n");
615                        break;
616                }
617        }
618
619    /* lock out VideoHandler until everything is set up - cmg */
620    cb_mutex_ = CreateMutex(NULL,FALSE,NULL);
621    WaitForSingleObject(cb_mutex_,INFINITE);
622
623        if ((capwin_ = capCreateCaptureWindow((LPSTR)"Capture Window", WS_POPUP | WS_CAPTION,
624                CW_USEDEFAULT, CW_USEDEFAULT, (basewidth_ / decimate_ + GetSystemMetrics(SM_CXFIXEDFRAME)),
625                (baseheight_ / decimate_ + GetSystemMetrics(SM_CYCAPTION) + GetSystemMetrics(SM_CYFIXEDFRAME)),
626                (HWND) 0, (int) 0)) == NULL) {
627                  debug_msg("capCreateCaptureWindow: failed - %lu\n", capwin_);
628                  abort();
629        }
630        capSetCallbackOnError(capwin_, ErrorHandler);
631        if (!capSetCallbackOnVideoStream(capwin_, VideoHandler)) {
632                debug_msg("capSetCallbackOnVideoStream: failed - %lu\n", GetLastError());
633                /*abort();*/
634        }
635        if (!capDriverConnect(capwin_, dev_)) {
636                debug_msg( "capDriverConnect: dev=%d failed - %lu\n", dev_, GetLastError());
637                stop();
638                return;
639                /*abort();*/
640        }
641
642        if (useconfig_) {
643                capDlgVideoFormat(capwin_);
644                capDlgVideoSource(capwin_);
645                capDlgVideoDisplay(capwin_);
646        }
647
648        capSetUserData(capwin_, this);
649        debug_msg("SetUserData=%x\n", this);
650        connected_ = 1;
651
652        if (!capDriverGetCaps(capwin_, &caps_, sizeof(caps_))) {
653                debug_msg( "capGetDriverCaps: failed - %lu\n", GetLastError());
654                /*abort();*/
655        }
656
657        debug_msg("capdrivercaps: overlay=%d dlgSource=%d dlgFmt=%d dlgDis=%d init=%d pal=%d\n",
658                caps_.fHasOverlay, caps_.fHasDlgVideoSource,
659                caps_.fHasDlgVideoFormat, caps_.fHasDlgVideoDisplay,
660                caps_.fCaptureInitialized, caps_.fDriverSuppliesPalettes);
661       
662        fmtsize_ = capGetVideoFormatSize(capwin_);
663        fmt_ = (LPBITMAPINFOHEADER)new u_char[fmtsize_];
664        if (!capGetVideoFormat(capwin_, fmt_, fmtsize_)) {
665                debug_msg("capGetVideoFormat: failed - %lu\n", GetLastError());
666                /*abort();*/
667        }
668        int orig_comp = fmt_->biCompression;
669        int orig_bpp = fmt_->biBitCount;
670        debug_msg("Original comp=%x (%s) bpp=%d\n", orig_comp, get_comp_name(orig_comp), orig_bpp);
671
672        fmt_->biPlanes = 1;
673
674        /* if the driver isn't set to either pal or ntsc then use default ntsc values */
675        if ((fmt_->biWidth != 192 && fmt_->biWidth != 176 && fmt_->biWidth != 384 && fmt_->biWidth != 352 && fmt_->biWidth != 768 && fmt_->biWidth != 704) ||
676                (fmt_->biHeight!= 142 && fmt_->biHeight!= 144 && fmt_->biHeight!= 284 && fmt_->biHeight!= 288 && fmt_->biHeight!= 568 && fmt_->biHeight!= 576)) {
677                fmt_->biWidth = basewidth_ / decimate_;
678                fmt_->biHeight = baseheight_ / decimate_;
679        }
680
681        switch (devtype_) {
682        case gray_QuickCam_NT:
683        case Miro_dc20_95:
684        case Miro_dc20_NT:
685        case AV_Master:
686    case Generic:
687                if (useconfig_) {
688                        if (caps_.fHasDlgVideoFormat) capDlgVideoFormat(capwin_);
689                        if (caps_.fHasDlgVideoSource) capDlgVideoSource(capwin_);
690                }
691                delete [] fmt_;
692                fmtsize_ = capGetVideoFormatSize(capwin_);
693                fmt_ = (LPBITMAPINFOHEADER) new u_char [fmtsize_];
694            capGetVideoFormat(capwin_, fmt_, fmtsize_);
695                break;
696        case Intel_SVR3:
697                /* the driver does not like to generic query much
698                 * just use yuv9 */
699                fmt_->biCompression = mmioFOURCC('Y','V','U','9');
700                fmt_->biBitCount = 9;
701                fmt_->biSizeImage = fmt_->biWidth * fmt_->biHeight * fmt_->biPlanes * fmt_->biBitCount / 8;
702                if (!capSetVideoFormat(capwin_, fmt_, fmtsize_)) {
703                        debug_msg("Intel SVR3 format failed!\n");
704                        fprintf(stderr, "Unable to set SVR3 to YUV9 format!\n");
705                        stop();
706                        abort();
707                }
708                break;
709        case gray_QuickCam_95:
710                /* We cannot use Generic as QuickCam says YES to anything
711                 * but does not do it! */
712                fmt_->biCompression = BI_RGB;
713                fmt_->biBitCount = 4;
714                fmt_->biSizeImage = fmt_->biWidth * fmt_->biHeight * fmt_->biPlanes * fmt_->biBitCount / 8;
715                capSetVideoFormat(capwin_, fmt_, fmtsize_);
716                break;
717        case MCT:
718#ifdef NDEF
719                fmt_->biCompression = mmioFOURCC('M','Y','4','2');
720                fmt_->biBitCount = 16;
721                fmt_->biSizeImage = fmt_->biWidth * fmt_->biHeight * fmt_->biPlanes * fmt_->biBitCount / 8;
722                if (capSetVideoFormat(capwin_, fmt_, fmtsize_))
723                        break;
724                debug_msg("MCT MY42 format failed!\n");
725#endif
726                goto Generic;
727        case SMII:
728                fmt_->biCompression = mmioFOURCC('Y','U','Y','V');
729                fmt_->biBitCount = 16;
730                fmt_->biSizeImage = fmt_->biWidth * fmt_->biHeight * fmt_->biPlanes * fmt_->biBitCount / 8;
731                if (capSetVideoFormat(capwin_, fmt_, fmtsize_))
732                        break;
733                debug_msg("SMII YUYV format failed!\n");
734                goto Generic;
735        Generic:
736        default:
737                /* Try to figure out what compression formats are supported. */
738                fmt_->biCompression = BI_RGB;
739                int i = 0;
740                do {
741                        fmt_->biBitCount = bit_options[i++];
742                        fmt_->biSizeImage = fmt_->biWidth * fmt_->biHeight * fmt_->biPlanes * fmt_->biBitCount / 8;
743                        if (capSetVideoFormat(capwin_, fmt_, fmtsize_))
744                                break;
745                } while (fmt_->biBitCount > 0);
746                if (fmt_->biBitCount == 0)
747                        debug_msg("Unable to find supported RGB format!\n");
748                else
749                        break;
750
751                /* RGB failed. Try using a decompressor... */
752                fmt_->biCompression = orig_comp;
753                fmt_->biBitCount = orig_bpp;
754                fmt_->biSizeImage = fmt_->biWidth * fmt_->biHeight * fmt_->biPlanes * fmt_->biBitCount / 8;
755                if (!capSetVideoFormat(capwin_, fmt_, fmtsize_)) {
756                        fprintf(stderr, "Unable to set size in native fmt!\n");
757                        stop();
758                        abort();
759                }
760                break;
761        }
762
763        /* OK now lets see what the driver really thinks about the format! */
764        delete [] fmt_;
765        fmtsize_ = capGetVideoFormatSize(capwin_);
766        fmt_ = (LPBITMAPINFOHEADER) new u_char [fmtsize_];
767        capGetVideoFormat(capwin_, fmt_, fmtsize_);
768        debug_msg("bitmapinfo: comp= %x (%s) w=%d h=%d planes=%d bitcnt=%d szImage=%d ClrUsed=%d ClrImp=%d\n",
769                fmt_->biCompression, get_comp_name(fmt_->biCompression),
770                fmt_->biWidth, fmt_->biHeight,
771                fmt_->biPlanes, fmt_->biBitCount, fmt_->biSizeImage,
772                fmt_->biClrUsed, fmt_->biClrImportant);
773
774        assert(fmt_->biPlanes == 1);
775        debug_msg("Using biBitCount = %d\n", fmt_->biBitCount);
776        if (fmt_->biWidth != (LONG) (basewidth_ / decimate_)) {
777                /* The driver is totally stupid so accept it's settings! */
778                debug_msg("Stupid driver. Accepting %x %d*%d*%d\n", fmt_->biCompression, fmt_->biWidth, fmt_->biHeight, fmt_->biBitCount);
779                switch (fmt_->biWidth) {
780                        case 640:
781                        case 320:
782                        case 160:
783                                max_fps_ = 30;
784                                basewidth_ = NTSC_BASE_WIDTH;
785                                baseheight_ = NTSC_BASE_HEIGHT;
786                                break;
787                        case 704:
788                        case 352:
789                        case 176:
790                                max_fps_ = 25;
791                                basewidth_ = CIF_BASE_WIDTH;
792                                baseheight_ = CIF_BASE_HEIGHT;
793                                break;
794                        case 768:
795                        case 384:
796                        case 192:
797                                max_fps_ = 25;
798                                basewidth_ = PAL_BASE_WIDTH;
799                                baseheight_ = PAL_BASE_HEIGHT;
800                                break;
801
802                        default:
803                                fprintf(stderr, "Image dimensions not suitable.\n");
804                                max_fps_ = 25;
805                                basewidth_ = PAL_BASE_WIDTH;
806                                baseheight_ = PAL_BASE_HEIGHT;
807                                return;
808                }
809                decimate_ = basewidth_ / fmt_->biWidth;
810                setsize();
811        }
812       
813        if (!capCaptureGetSetup(capwin_, &parms_, sizeof(parms_))) {
814                fprintf(stderr, "capCaptureGetSetup: failed - %lu\n", GetLastError());
815                /*abort();*/
816        }
817
818        debug_msg("GetSetup: uSec=%d drop=%d DOS=%d nVid=%d yield=%d\n",
819                parms_.dwRequestMicroSecPerFrame,
820                parms_.wPercentDropForError,
821                parms_.fUsingDOSMemory,
822                parms_.wNumVideoRequested,
823                parms_.fYield);
824
825        /*1e6 / double(max_fps_);*/
826        /*(DWORD)frametime_*/
827        parms_.dwRequestMicroSecPerFrame = (unsigned long) (1e6 / double(max_fps_));
828        parms_.wPercentDropForError = 100;
829        parms_.fUsingDOSMemory = FALSE;
830        parms_.wNumVideoRequested = 3;
831        parms_.fYield = TRUE;
832        parms_.fMakeUserHitOKToCapture = FALSE;
833        parms_.fCaptureAudio = FALSE;
834        parms_.vKeyAbort = 0;
835        parms_.fAbortLeftMouse = FALSE;
836        parms_.fAbortRightMouse = FALSE;
837        parms_.fLimitEnabled = FALSE;
838        parms_.fMCIControl = FALSE;
839        parms_.wStepCaptureAverageFrames=1;
840
841        if (!capCaptureSetSetup(capwin_, &parms_, sizeof(parms_))) {
842                fprintf(stderr, "capCaptureSetSetup: failed - %lu\n", GetLastError());
843                /*abort();*/
844        }
845
846#ifdef NDEF
847        MoveWindow(capwin_, 0, 0,
848                   (basewidth_ / decimate_ + GetSystemMetrics(SM_CXFIXEDFRAME)),
849                   (baseheight_ / decimate_ + GetSystemMetrics(SM_CYCAPTION)
850                    + GetSystemMetrics(SM_CYFIXEDFRAME)), TRUE);
851
852        ShowWindow(capwin_, SW_SHOW);
853       
854        if (caps_.fHasOverlay) {
855                capOverlay(capwin_, TRUE);
856        } else {
857                capPreviewRate(capwin_, 66);
858                capPreview(capwin_, TRUE);
859        }
860#endif
861        frame_sem_ = CreateSemaphore(NULL, 0, 1, NULL);
862        if (!capCaptureSequenceNoFile(capwin_)) {
863                fprintf(stderr, "capCaptureSequenceNoFile: failed - %lu\n", GetLastError());
864                /*abort();*/
865        } else {
866                capturing_ = 1;
867                capture_=1;
868        }
869        last_frame_ = 0;
870
871        Grabber::start();
872}
873
874void Vfw422Grabber::start()
875{
876        VfwGrabber::start();
877        switch (fmt_->biCompression) {
878        case BI_RGB:
879                converter(new RGB_Converter_422(fmt_->biBitCount, (u_int8_t *)(fmt_ + 1), fmt_->biClrUsed));
880                break;
881        case mmioFOURCC('Y','U','Y','V'):
882                converter(new YUYV_Converter_422());
883                break;
884        default:
885                converter(new IC_Converter_422(fmt_->biCompression, fmt_->biBitCount, fmt_->biWidth, fmt_->biHeight));
886                break;
887        }
888    /* allow video handler callback to progress */
889    ReleaseMutex(cb_mutex_);
890    Grabber::timeout();
891}
892
893void VfwCIFGrabber::start()
894{
895        VfwGrabber::start();
896        if (fmt_!=NULL) {
897            switch (fmt_->biCompression) {
898                case BI_RGB:
899                    converter(new RGB_Converter_411(fmt_->biBitCount, (u_int8_t *)(fmt_ + 1), fmt_->biClrUsed));
900                    break;
901                case mmioFOURCC('Y','U','Y','V'):
902                    converter(new YUYV_Converter_411());
903                    break;
904                default:
905                    converter(new IC_Converter_411(fmt_->biCompression, fmt_->biBitCount, fmt_->biWidth, fmt_->biHeight));
906                    break;
907            }
908        }
909    /* allow video handler callback to progress */
910    ReleaseMutex(cb_mutex_);
911    Grabber::timeout();
912}
913
914void VfwGrabber::stop()
915{
916    debug_msg("VfwWindow::stop() thread=%x\n", GetCurrentThreadId());
917
918    if (cb_mutex_!=NULL) {
919        CloseHandle(cb_mutex_);
920        cb_mutex_=0;
921    } else 
922        return;
923
924    if (capturing_)
925        capCaptureStop(capwin_);
926    /* ensure this won't be called */
927    capSetCallbackOnVideoStream(capwin_, NULL);
928    capturing_ = 0;
929    capture_=0;
930    if (frame_sem_!=0 ) {
931        ReleaseSemaphore(frame_sem_, 1, NULL);
932        CloseHandle(frame_sem_);
933    }
934#ifdef NDEF
935        if (caps_.fHasOverlay)
936                capOverlay(capwin_, FALSE);
937        else
938                capPreview(capwin_, FALSE);
939#endif
940
941        if (capwin_!=NULL)
942            capDriverDisconnect(capwin_);
943        connected_ = 0;
944
945        if (converter_!=NULL) //if (fmt_->biCompression == BI_RGB)
946                delete converter_;
947        converter_ = 0;
948
949        delete [] fmt_;
950
951        capSetCallbackOnError(capwin_, NULL);
952        DestroyWindow(capwin_);
953        capwin_ = NULL;
954        last_frame_ = 0;
955
956        Grabber::stop();
957}
958
959void VfwGrabber::setport(const char *port)
960{
961        debug_msg("setport: %s thread=%x\n", port, GetCurrentThreadId());
962
963        /* Decision about PAL / NTSC has to be made at this point */
964        max_fps_ = 30;
965}
966
967int VfwGrabber::command(int argc, const char*const* argv)
968{
969        if (argc == 3) {
970                if (strcmp(argv[1], "decimate") == 0) {
971                        u_int dec = (u_int)atoi(argv[2]);
972                        Tcl& tcl = Tcl::instance();
973                        if (dec <= 0) {
974                                tcl.resultf("%s: divide by zero", argv[0]);
975                                return (TCL_ERROR);
976                        }
977                        debug_msg("VfwGrabber::command: decimate=%d (dec)=%d\n", dec, decimate_);
978                        if (dec != decimate_) {
979                                decimate_ = dec;
980                                if (running_) {
981                                        stop();
982                                        setsize();
983                                        start();
984                                } else
985                                        setsize();
986                        }
987                        return (TCL_OK);       
988                } else if (strcmp(argv[1], "port") == 0) {
989                        setport(argv[2]);
990                        return (TCL_OK);
991                } else if (strcmp(argv[1], "useconfig") ==0) {
992                        if (strcmp(argv[2], "1") == 0) useconfig_=1;
993                        if (strcmp(argv[2], "0") == 0) useconfig_=0;
994                }
995        }
996        return (Grabber::command(argc, argv));
997}
998
999LRESULT CALLBACK
1000VfwGrabber::ErrorHandler(HWND hwnd, int id, LPCSTR err)
1001{
1002        if (hwnd == NULL)
1003                return ((LRESULT)FALSE);
1004        if (id == 0)
1005                return ((LRESULT)TRUE);
1006
1007        debug_msg("ErrorHandler: thread=%x [id=%d] %s\n",
1008                GetCurrentThreadId(), id, err);
1009
1010        return ((LRESULT)TRUE);
1011}
1012
1013LRESULT CALLBACK
1014VfwGrabber::VideoHandler(HWND hwnd, LPVIDEOHDR vh)
1015{
1016        static int not_done = 0;
1017
1018        VfwGrabber *gp = (VfwGrabber*)capGetUserData(hwnd);
1019    /* in case we are not fast enough */
1020    if (gp==NULL) return ((LRESULT)TRUE);
1021
1022    /* Block grab code until frame has been copied into last_frame_ (in capture function) */
1023    WaitForSingleObject(gp->cb_mutex_,INFINITE);
1024
1025#ifdef DEBUG__ 
1026        debug_msg("VfwGrabber::VideoHandler: thread=%x data=%x flags=%x len=%d time=%d\n",
1027                GetCurrentThreadId(),
1028                vh->lpData, vh->dwFlags, vh->dwBytesUsed, vh->dwTimeCaptured);
1029#endif
1030
1031        if (vh->dwFlags & VHDR_DONE)
1032                (gp->capture)(gp, vh->lpData);
1033        else if (not_done++ % 10 == 0)
1034                debug_msg("Frames not ready! %d\n", not_done);
1035
1036    /* Release to process frame in grab */
1037    ReleaseMutex(gp->cb_mutex_);
1038        return ((LRESULT)TRUE);
1039}
1040
1041void
1042VfwGrabber::capture(VfwGrabber *gw, LPBYTE frame)
1043{
1044#ifdef DEBUG
1045        if (gw->last_frame_ != NULL)
1046                debug_msg("Last frame not grabbed!\n");
1047#endif
1048        if (capturing_) gw->last_frame_ = frame;
1049}
1050
1051int
1052VfwGrabber::grab()
1053{
1054#ifdef DEBUG_GRAB
1055        debug_msg("VfwGrabber::grab: thread=%x w=%d h=%d frame_=%d fsize_=%d in=%dx%d out=%dx%d\n",
1056                GetCurrentThreadId(),
1057                basewidth_, baseheight_, frame_, framesize_,
1058                inw_, inh_, outw_, outh_);
1059#endif
1060
1061    /* block the VideoHandler callback code until we've processed frame */
1062    WaitForSingleObject(cb_mutex_,INFINITE);
1063
1064    if (last_frame_ == NULL || capturing_ == 0) {
1065        ReleaseMutex(cb_mutex_);
1066                return (FALSE);
1067    }
1068
1069        converter_->convert((u_int8_t*)last_frame_, basewidth_ / decimate_, baseheight_ / decimate_, frame_, outw_, outh_, TRUE);
1070        last_frame_ = NULL;
1071        suppress(frame_);
1072        saveblks(frame_);
1073        YuvFrame f(media_ts(), frame_, crvec_, outw_, outh_);
1074        int rval = (target_->consume(&f));
1075
1076    /* release block so that VideoHandler can get new frame */
1077    ReleaseMutex(cb_mutex_);
1078    return rval;
1079}
Note: See TracBrowser for help on using the browser.