root/vic/branches/mpeg4/video/grabber-win32DS.cpp @ 4047

Revision 4047, 29.8 KB (checked in by piers, 7 years ago)

Added updated fix for divide by zero problem reported by Tom Uram.

  • Property svn:eol-style set to native
  • Property svn:executable set to *
  • 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 *  Portions Copyright (c) 2004 EarthLink, Inc.
5 *  All rights reserved.
6 *
7 *  Redistribution and use in source and binary forms, with or without
8 *  modification, are permitted provided that the following conditions
9 *  are met:
10 *  1. Redistributions of source code must retain the above copyright
11 *     notice, this list of conditions and the following disclaimer.
12 *  2. Redistributions in binary form must reproduce the above copyright
13 *     notice, this list of conditions and the following disclaimer in the
14 *     documentation and/or other materials provided with the distribution.
15 *  3. The name of the author may not be used to endorse or promote products
16 *     derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
22 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
27 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 * POSSIBILITY OF SUCH DAMAGE.
29 */
30
31/* grabber-win32DS.cpp
32
33On the implementation of DirectShow frame grabbing
34
35        Mark S. Petrovic
36        EarthLink, Inc.
37        Pasadena, CA  USA
38        petrovic@corp.earthlink.net
39
40I.
41
42Timeline:  started 9/27/2004; finished 11/05/2004
43
44II. 
45
46DirectShow video capture interface
47Developed under DirectX 9, but simple enough that it
48should work for DirectX 8 runtime systems. - msp 10/2004
49
50Key references:
51
521.  Programming Microsoft DirectShow for Digital Video and Television, by Mark Pesce
53
54http://www.amazon.com/exec/obidos/ASIN/0735618216/qid=1098459570/sr=2-1/ref=pd_ka_b_2_1/103-2872643-3555802
55
56This book was very helpful in understanding DirectShow and the requisite basic COM programming.
57
582.  MSDN DirectShow reference
59
60http://msdn.microsoft.com/library/default.asp?url=/library/en-us/directshow/htm/directshowreference.asp
61
62III. 
63
64Morning-after comments, 11/05/2004
65
661.  This code is good at dealing with simple USB-like (and maybe even
67Firewire-like) devices.  Such devices have no crossbar filters inserted behind the scenes
68by the graph builder, and which must be manipulated before grabbing begins.
69
702.  This code has a serious hack in dealing with devices that have "crossbar
71filters".  Crossbar filters come into play with video capture cards, but, from what
72I can tell, not with simple devices like USB cameras.  None of the USB cameras ship with WDM
73drivers that have crossbar filters.  The Hauppauge WinTV Go Model 190 does have a crossbar
74filter in its WDM driver.
75
76The hack is this:  if a graph has a crossbar, I configure it to capture from the Composite In
77port without qualification.  The user has no say in this.  Therefore, capturing from physical
78s-video or antenna ports is not possible in this code as written. 
79
80Crossbar filters are added automatically by the graph builder during pBuild_->RenderStream().
81Their purpose is to give
82the programmer a way to programmatically choose which physical input port is "wired" to
83the crossbar video decoder output port.  The crossbar video decoder output port pin is in turn
84connected by the graph builder to the "true" device video capture filter that one would normally consider
85to reside at the start of the capture graph.  In other words, the programmer does not, and cannot,
86insert crossbars manually.
87
88The crossbar reference: 
89
90http://msdn.microsoft.com/library/default.asp?url=/library/en-us/directshow/htm/workingwithcrossbars.asp
91
923.  The issue is this: 
93
94vic, by a reading of the tcl source code in ui-ctrlmenu.tcl, considers
95the objects "inputDevices" to be separate and distinct from the objects "grabbers".  A case
96can be made that vic thinks devices "have" grabbers, rather than device "is" grabber.
97DirectShow formally has neither concept; everything is a filter, or chains of filters.
98
99When tcl "inputDevices" are created in the current programming model in DirectShowScanner,
100the filter graph and the associated filter graph builder
101do not yet exist.  The filter graph is required, however, to locate the crossbar, and the crossbar
102in turn is inspected for physical input port names, such as Composite-in and s-video.  Because
103the graph builder does not exist when inputDevices is created, the UI cannot be populated
104with physical port names from which the user can choose to capture.
105
106The graph builder comes into being when a DirectShowGrabber object is created when the user clicks
107Transmit, and only when the grabber exists does sufficient information exist for the
108crossbar to be located, its physical port names inspected, and logical wiring configured.
109
110Two suggestions on how this model might be modified to give the user back the optoin
111of choosing physical ports on capture cards:
112
113a) create a dummy grabber of some sort during DirectShowScanner, and inspect the resulting
114crossbar then for port names.  "delete dummyGrabber" at that point, as its purpose
115has been fulfilled.
116
117b) totally rework the code, and possibly vic's object model, so that grabbers exist when
118input devices are created.  Probably not a good approach, but it is one possibility.
119
120*/
121
122#include <stdio.h>
123#include <assert.h>
124#include <stdlib.h>
125#include "config.h"
126#include <windows.h>
127
128#include "grabber.h"
129#include "device-input.h"
130#include "module.h"
131#include "rgb-converter.h"
132
133#include "grabber-win32DS.h"
134
135#ifndef VIDE0_FOR_WINDOWS
136static DirectShowScanner findDirectShowDevices;
137#endif
138
139IBaseFilter  *pCaptureFilter[NUM_DEVS];
140
141
142IAMCrossbar *pXBar   = NULL;
143   IBaseFilter *pFilter = NULL;
144
145
146//#########################################################################
147// Class Callback, for video framebuffer callback.  See DS Sample Grabber interface docs.
148
149STDMETHODIMP Callback::SampleCB(double sampleTime, IMediaSample *pSample) {
150   return E_NOTIMPL;
151}
152
153STDMETHODIMP Callback::BufferCB(double sampleTime, BYTE *pBuffer, long bufferLen) {
154
155   //debug_msg("Callback::BufferCB:  sampleTime= %f, pbuffer= %p, bufferLen= %ld\n", sampleTime, pBuffer, bufferLen);
156
157   WaitForSingleObject(grabber->cb_mutex_, INFINITE);
158   grabber->capture(pBuffer, bufferLen);
159   ReleaseMutex(grabber->cb_mutex_);
160   return S_OK;
161}
162
163//-----------------
164
165STDMETHODIMP Callback::QueryInterface(REFIID riid, void **ppvObject) {
166   if (NULL == ppvObject)
167      return E_POINTER;
168   if (riid == __uuidof(IUnknown))
169      *ppvObject = static_cast<IUnknown*>(this);
170   else if (riid == __uuidof(ISampleGrabberCB))
171      *ppvObject = static_cast<ISampleGrabberCB*>(this);
172   else
173      return E_NOTIMPL;
174   AddRef();
175   return S_OK;
176}
177
178//#########################################################################
179// DirectShowGrabber definition
180
181DirectShowGrabber::DirectShowGrabber(IBaseFilter *filt) {
182   HRESULT         hr;
183
184   /* Reference:  various, including
185      - Pesce, Chapter 11
186      - http://msdn.microsoft.com/library/default.asp?url=/library/en-us/directshow/htm/samplegrabberfilter.asp
187   */
188
189   debug_msg("new DirectShowGrabber()\n");
190   svideoPort = -1;
191   compositePort = -1;
192
193   crossbar = crossbarCursor = NULL; 
194
195   pCaptureFilter_ = filt;   
196   setport("external");
197
198   basewidth_  = CIF_BASE_WIDTH;
199   baseheight_ = CIF_BASE_HEIGHT;
200 
201   /*
202   ZeroMemory(&mt_, sizeof(AM_MEDIA_TYPE));
203   mt_.majortype = MEDIATYPE_Video;
204   mt_.subtype   = MEDIASUBTYPE_RGB24;
205   */
206
207   /*
208   mt_.majortype = MEDIATYPE_AnalogVideo;
209   mt_.subtype   = MEDIASUBTYPE_AnalogVideo_NTSC_M;
210   */
211
212   callback           = new Callback();   
213   callback->grabber  = this;     
214   debug_msg("DirectShowGrabber::DirectShowGrabber():  callback created, grabber set\n");
215
216   // Create the Filter Graph Manager
217   hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER,
218                         IID_IGraphBuilder, (void **)&pGraph_);
219   if (FAILED(hr)) {
220                Grabber::status_=-1;
221                return;
222   }
223   debug_msg("DirectShowGrabber::DirectShowGrabber():  graph instance acquired\n");
224   
225   // Obtain the interface used to run, stop, and pause the graph
226   hr = pGraph_->QueryInterface(IID_IMediaControl, (void **)&pMediaControl_);
227   //showErrorMessage(hr);
228   if (FAILED(hr)) {
229                Grabber::status_=-1;
230                return;
231   }
232   debug_msg("DirectShowGrabber::DirectShowGrabber():  graph media control interface acquired\n");
233   
234   // Create the capture graph builder helper object
235   hr = CoCreateInstance(CLSID_CaptureGraphBuilder2, NULL, CLSCTX_INPROC_SERVER,
236                         IID_ICaptureGraphBuilder2, (void **)&pBuild_);
237   //showErrorMessage(hr);
238   if (FAILED(hr)) {
239                Grabber::status_=-1;
240                return;
241   }
242   debug_msg("DirectShowGrabber::DirectShowGrabber():  graph builder interface acquired\n");
243
244   // Tell the capture graph builder about the FGM.
245   hr = pBuild_->SetFiltergraph(pGraph_);
246   //showErrorMessage(hr);
247   if (FAILED(hr)) {
248                Grabber::status_=-1;
249                return;
250   }
251   debug_msg("DirectShowGrabber::DirectShowGrabber():  graph associated with builder\n");
252
253   // Add the capture filter to the filter graph
254   hr = pGraph_->AddFilter(pCaptureFilter_, L"VicCaptureFilter");
255   //showErrorMessage(hr);
256   debug_msg("DirectShowGrabber::DirectShowGrabber():  capture filter added to graph\n");
257
258   // Set up the Sample Grabber transform filter
259   hr = CoCreateInstance(CLSID_SampleGrabber, NULL, CLSCTX_INPROC_SERVER,
260                         IID_IBaseFilter, (LPVOID *)&pGrabberBaseFilter_);
261   //showErrorMessage(hr);
262   debug_msg("DirectShowGrabber::DirectShowGrabber():  grabber base filter instance acquired\n");
263
264   hr = pGrabberBaseFilter_->QueryInterface(IID_ISampleGrabber, (void**)&pSampleGrabber_);
265   //showErrorMessage(hr);
266   debug_msg("DirectShowGrabber::DirectShowGrabber():  Sample Grabber interface acquired\n");
267
268   //hr = pSampleGrabber_->SetMediaType(&mt_);                          showErrorMessage(hr);
269   hr = pSampleGrabber_->SetOneShot(FALSE);                           //showErrorMessage(hr);
270   hr = pSampleGrabber_->SetCallback(callback, 1);                    //showErrorMessage(hr);
271   hr = pGraph_->AddFilter(pGrabberBaseFilter_,L"VicSampleGrabber");  //showErrorMessage(hr);
272
273   // Get the Null Renderer DS default filter
274   hr = CoCreateInstance(CLSID_NullRenderer, NULL, CLSCTX_INPROC_SERVER,
275                         IID_IBaseFilter, (LPVOID *)&pNullBaseFilter_);
276   //showErrorMessage(hr);
277    if (FAILED(hr)) {
278                Grabber::status_=-1;
279                return;
280   }
281   debug_msg("DirectShowGrabber::DirectShowGrabber():  Null Renderer interface acquired\n");
282
283   // Finally, add the Null Renderer "sink" to the graph
284   hr = pGraph_->AddFilter(pNullBaseFilter_,L"VicNullRenderer");
285    if (FAILED(hr)) {
286                Grabber::status_=-1;
287                return;
288   }
289   debug_msg("DirectShowGrabber::DirectShowGrabber():  Null Renderer added to graph\n");
290
291   if (!getCaptureCapabilities()) {
292                Grabber::status_=-1;
293                return;
294   }
295   // setCaptureOutputFormat();
296   
297   findCrossbar(pCaptureFilter_);
298   routeCrossbar();   
299
300   // We can presumably inspect crossbar==NULL here to determine if we want
301   // to change the Sample Grabber media type.
302   ZeroMemory(&mt_, sizeof(AM_MEDIA_TYPE));
303   mt_.majortype = MEDIATYPE_Video;
304   mt_.subtype   = MEDIASUBTYPE_RGB24;
305   hr            = pSampleGrabber_->SetMediaType(&mt_);
306   //showErrorMessage(hr);
307
308   // Connect the filters   
309   hr = pBuild_->RenderStream(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video,
310                              pCaptureFilter_, pGrabberBaseFilter_, pNullBaseFilter_);
311   //showErrorMessage(hr);
312   debug_msg("DirectShowGrabber::DirectShowGrabber():  builder render stream\n");
313   
314   if (FAILED(hr)) {
315                status_=-1;
316                return;
317   }
318}
319
320//--------------------------------
321
322DirectShowGrabber::~DirectShowGrabber() {
323    HRESULT hr;
324
325    debug_msg("~DirectShowGrabber()\n");
326
327    capturing_ = !capturing_;
328    hr         = pMediaControl_->Stop();
329    //showErrorMessage(hr);   
330
331    // Release COM objects in reverse order of instantiation
332    callback->Release();
333    pNullBaseFilter_.Release();
334    pSampleGrabber_.Release();
335    pGrabberBaseFilter_.Release();   
336    pBuild_.Release();   
337    pMediaControl_.Release();
338    pGraph_.Release();       
339}
340
341//--------------------------------
342
343void DirectShowGrabber::findCrossbar(IBaseFilter *pCapF) {
344   HRESULT     hr;
345   /*IAMCrossbar *pXBar   = NULL;
346   IBaseFilter *pFilter = NULL;*/
347
348   debug_msg("DirectShowGrabber::FindCrossbar()...\n");
349
350   hr = pBuild_->FindInterface(&LOOK_UPSTREAM_ONLY, NULL, pCapF, IID_IAMCrossbar,
351                             (void**)&pXBar);
352
353   if ( SUCCEEDED(hr) ) {
354      addCrossbar(pXBar);
355      hr = pXBar->QueryInterface(IID_IBaseFilter, (void**)&pFilter);
356      if ( SUCCEEDED(hr) ) {
357         findCrossbar(pFilter);
358         pFilter->Release();
359      }
360
361   }
362}
363
364//-------------------------------
365
366void DirectShowGrabber::addCrossbar(IAMCrossbar *xbar) {
367   Crossbar *pCross;
368
369   debug_msg("DirectShowGrabber::addCrossbar()\n");
370
371   pCross = new Crossbar(xbar);
372
373   if( crossbar == NULL ) {
374      crossbar = pCross;   
375   }
376   else {
377      crossbarCursor->next = pCross;     
378   }
379   crossbarCursor = pCross;
380}
381
382//-----------------------------
383
384void DirectShowGrabber::routeCrossbar() {
385    HRESULT     hr;
386    long        output           = -1;
387    long        input            = -1;
388    int         videoDecoderPort = -1;
389        int                 port;
390    long        related;
391    long        pinType;   
392    IAMCrossbar *xb;
393
394    if( crossbar == NULL ) return;       
395
396    xb = crossbar->getXBar();
397
398    xb->get_IsRoutedTo(0, &input);
399    debug_msg("DirectShowGrabber::routeCrossbar():  pin %d is routed to output pin 0\n", input);
400
401    hr = xb->get_PinCounts(&output, &input);
402
403    for( int i = 0; i < input; ++i ) {
404        xb->get_CrossbarPinInfo(TRUE, i, &related, &pinType);
405        if( pinType == PhysConn_Video_SVideo ) {
406            svideoPort = i;           
407        }
408        if( pinType == PhysConn_Video_Composite ) {
409            compositePort = i;           
410        }
411    }
412    for( int i = 0; i < output; ++i ) {
413       xb->get_CrossbarPinInfo(FALSE, i, &related, &pinType);
414       if( pinType == PhysConn_Video_VideoDecoder ) {
415           videoDecoderPort = i;           
416           break;
417       }
418    }
419
420       
421        if(strcmp(input_port_, "S-Video")==0){
422                port = svideoPort;
423        }else if(strcmp(input_port_, "Composite")==0){
424                port = compositePort;
425        }else{
426                port = 0;
427        }
428
429    if( xb->CanRoute(videoDecoderPort, port) == S_FALSE )
430        debug_msg("DirectShowGrabber::routeCrossbar():  cannot route input pin %d to output pin %d\n", port, videoDecoderPort);
431    else {
432        debug_msg("DirectShowGrabber::routeCrossbar() routing pin %d to pin %d\n", port, videoDecoderPort);
433        hr = xb->Route(videoDecoderPort, port);
434        //showErrorMessage(hr);
435    }
436
437    xb->get_IsRoutedTo(0, &input);
438    debug_msg("DirectShowGrabber::routeCrossbar():  pin %d is now routed to output pin 0\n", input);
439}
440
441//-----------------------------
442
443void DirectShowGrabber::start() {
444   HRESULT hr;
445
446   setsize();
447
448   // callback mutex
449   cb_mutex_ = CreateMutex(NULL, FALSE, NULL);
450   WaitForSingleObject(cb_mutex_, INFINITE);
451   
452   capturing_  = 1;   
453   last_frame_ = NULL;
454
455   debug_msg("DirectShowGrabber::start():  starting capture graph...\n");
456
457   // Run the graph...
458   hr = pMediaControl_->Run();
459   //showErrorMessage(hr);
460
461   Grabber::start();
462}
463
464//--------------------------------
465
466void DirectShowGrabber::stop() {
467   HRESULT hr;
468
469   debug_msg("DirectShowGrabber::stop() thread=%x\n", GetCurrentThreadId());
470
471   if (capturing_) {
472      hr = pMediaControl_->Stop();
473   }
474   //showErrorMessage(hr);
475   
476   CloseHandle(cb_mutex_); 
477   delete converter_;
478   capturing_  = 0;   
479   converter_  = 0;
480   last_frame_ = 0;
481
482   Grabber::stop();
483}
484
485//--------------------------------
486
487void DirectShowGrabber::fps(int f) {
488   if (f <= 0)
489      f = 1;
490   else if (u_int(f) > max_fps_)
491      f = max_fps_;
492
493   Grabber::fps(f);
494}
495
496//--------------------------------
497
498void DirectShowGrabber::capture(BYTE *frameBuf, long bufLen) {
499   last_frame_ = frameBuf;
500   // debug_msg("DirectShowGrabber::capture: frameBuf=%p, last_frame_=%p, bufLen=%ld\n", frameBuf, last_frame_, bufLen);
501}
502
503//--------------------------------
504
505int DirectShowGrabber::grab() {
506   int rval;
507
508   /*
509   debug_msg("DirectShowGrabber::grab: thread=%x w=%d h=%d frame_=%p fsize_=%d in=%dx%d out=%dx%d\n",
510             GetCurrentThreadId(),
511             basewidth_, baseheight_, frame_, framesize_,
512             inw_, inh_, outw_, outh_);
513   */
514
515   WaitForSingleObject(cb_mutex_, INFINITE);
516
517   if( last_frame_ == NULL || capturing_ == 0 ) {
518      ReleaseMutex(cb_mutex_);
519      return FALSE;
520   }
521
522   converter_->convert((u_int8_t*)last_frame_, width_,
523                       height_, frame_, outw_, outh_, TRUE);
524
525   last_frame_ = NULL;
526
527   suppress(frame_);
528   saveblks(frame_);
529   YuvFrame f(media_ts(), frame_, crvec_, outw_, outh_);
530
531   rval = (target_->consume(&f));
532
533   // release block so that callback can get new frame
534   ReleaseMutex(cb_mutex_);
535
536   return rval;
537}
538
539//--------------------------------
540
541void DirectShowGrabber::setport(const char *port) {
542
543   debug_msg("DirectShowGrabber::setport: %s thread=%x\n", port, GetCurrentThreadId());
544   strcpy(input_port_, port);
545   routeCrossbar();
546
547}
548
549//--------------------------------
550
551int DirectShowGrabber::getCaptureCapabilities() {
552   IAMStreamConfig          *pConfig;
553   AM_MEDIA_TYPE            *pmtConfig;
554   int                      iCount;
555   int                      iSize;
556   VIDEO_STREAM_CONFIG_CAPS scc;
557   HRESULT                  hr;
558
559   pConfig   = NULL;
560   hr        = pBuild_->FindInterface(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video,
561                                     pCaptureFilter_, IID_IAMStreamConfig, (void**)&pConfig);
562   if (FAILED(hr)) {
563                return FALSE;
564   }
565
566   max_width_ = 0;
567   max_height_ = 0;
568   iCount = iSize = 0;
569   hr     = pConfig->GetNumberOfCapabilities(&iCount, &iSize);
570   // Check the size to make sure we pass in the correct structure.
571   // The alternative output of iSize is AUDIO_STREAM_CONFIG_CAPS, btw.
572   if ( iSize == sizeof(VIDEO_STREAM_CONFIG_CAPS) ) {
573
574      for (int iFormat = 0; iFormat < iCount; iFormat++) {
575         hr = pConfig->GetStreamCaps(iFormat, &pmtConfig, (BYTE *)&scc);
576         //showErrorMessage(hr);
577         // Need to check for different formats and set up appropriate converter
578         // e.g // 59565955-0000-0010-8000-00AA00389B71  'UYVY' ==  MEDIASUBTYPE_UYVY
579         //     for simple firewire cam
580         if( SUCCEEDED(hr) ) {
581            if ((pmtConfig->majortype  == MEDIATYPE_Video)            &&
582                  (pmtConfig->subtype    == MEDIASUBTYPE_RGB24)       &&
583                  (pmtConfig->formattype == FORMAT_VideoInfo)         &&
584                  (pmtConfig->cbFormat   >= sizeof (VIDEOINFOHEADER)) &&
585                  (pmtConfig->pbFormat   != NULL)) {
586
587                           if(scc.MaxOutputSize.cx > max_width_){
588                               max_width_  = scc.MaxOutputSize.cx;
589                               max_height_ =  scc.MaxOutputSize.cy;
590                                        return TRUE;
591
592                           }
593                        }
594                 }
595          }
596   }
597   return FALSE;
598}
599
600void DirectShowGrabber::setCaptureOutputFormat() {
601   IAMStreamConfig          *pConfig;
602   int                      iCount;
603   int                      iSize;
604   VIDEOINFOHEADER          *pVih;
605   VIDEO_STREAM_CONFIG_CAPS scc;
606   AM_MEDIA_TYPE            *pmtConfig;
607   int                      formatSet;
608   HRESULT                  hr;
609
610   // Reference http://msdn.microsoft.com/library/default.asp?url=/library/en-us/directshow/htm/configurethevideooutputformat.asp
611
612   debug_msg("DirectShowGrabber::setCaptureOutputFormat(): enter...\n");
613
614   formatSet = 0;
615   pConfig   = NULL;
616   hr        = pBuild_->FindInterface(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video,
617                                     pCaptureFilter_, IID_IAMStreamConfig, (void**)&pConfig);
618   if (FAILED(hr)) {
619                Grabber::status_=-1;
620                return;
621   }
622
623   debug_msg("DirectShowGrabber::setCaptureOutputFormat(): IAMStreamConfig interface acquired\n");
624
625   iCount = iSize = 0;
626   hr     = pConfig->GetNumberOfCapabilities(&iCount, &iSize);
627   // Check the size to make sure we pass in the correct structure.
628   // The alternative output of iSize is AUDIO_STREAM_CONFIG_CAPS, btw.
629   if ( iSize == sizeof(VIDEO_STREAM_CONFIG_CAPS) ) {
630
631      for (int iFormat = 0; iFormat < iCount; iFormat++) {
632         hr = pConfig->GetStreamCaps(iFormat, &pmtConfig, (BYTE *)&scc);
633         //showErrorMessage(hr);
634
635         if( SUCCEEDED(hr) ) {
636            if ((pmtConfig->majortype  == MEDIATYPE_Video)            &&
637                  (pmtConfig->subtype    == MEDIASUBTYPE_RGB24)       &&
638                  (pmtConfig->formattype == FORMAT_VideoInfo)         &&
639                  (pmtConfig->cbFormat   >= sizeof (VIDEOINFOHEADER)) &&
640                  (pmtConfig->pbFormat   != NULL)                     &&
641                  (scc.MaxOutputSize.cx >= width_)                    &&
642                  (scc.MaxOutputSize.cy >= height_)){
643
644               pVih                        = (VIDEOINFOHEADER *)pmtConfig->pbFormat;                     
645               pVih->bmiHeader.biWidth     = width_;
646               pVih->bmiHeader.biHeight    = height_;
647               pVih->bmiHeader.biSizeImage = DIBSIZE(pVih->bmiHeader);
648               // AvgTimePerFrame value that specifies the video frame'
649               // average display time, in 100-nanosecond units.
650               if (fps_)
651                 pVih->AvgTimePerFrame     = 10000000/fps_;
652
653               debug_msg("Windows GDI BITMAPINFOHEADER follows:\n");
654               debug_msg("biWidth=        %d\n", pVih->bmiHeader.biWidth);
655               debug_msg("biHeight=       %d\n", pVih->bmiHeader.biHeight);
656               debug_msg("biSize=         %d\n", pVih->bmiHeader.biSize);
657               debug_msg("biPlanes=       %d\n", pVih->bmiHeader.biPlanes);
658               debug_msg("biBitCount=     %d\n", pVih->bmiHeader.biBitCount);
659               debug_msg("biCompression=  %d\n", pVih->bmiHeader.biCompression);
660               debug_msg("biSizeImage=    %d\n", pVih->bmiHeader.biSizeImage);
661               debug_msg("biXPelsPerMeter=%d\n", pVih->bmiHeader.biXPelsPerMeter);
662               debug_msg("biYPelsPerMeter=%d\n", pVih->bmiHeader.biYPelsPerMeter);
663               debug_msg("biClrUsed=      %d\n", pVih->bmiHeader.biClrUsed);
664               debug_msg("biClrImportant= %d\n", pVih->bmiHeader.biClrImportant);
665
666               hr = pConfig->SetFormat(pmtConfig);
667               //showErrorMessage(hr);
668
669               // XXX:  leak.  need to deal with this - msp
670               //DeleteMediaType(pmtConfig);
671               formatSet = 1;
672//               break;
673
674            }
675         }
676      }
677   }
678   pConfig->Release();
679
680   if( formatSet )
681      debug_msg("DirectShowGrabber::setCaptureOutputFormat:  format set\n");
682   else
683      debug_msg("DirectShowGrabber::setCaptureOutputFormat:  format not set\n");
684}
685
686//--------------------------------
687
688int DirectShowGrabber::command(int argc, const char* const* argv) {
689
690   if (argc == 3) {
691      if (strcmp(argv[1], "decimate") == 0) {
692         u_int dec = (u_int)atoi(argv[2]);
693         Tcl& tcl = Tcl::instance();
694         if (dec <= 0) {
695            tcl.resultf("%s: divide by zero", argv[0]);
696            return (TCL_ERROR);
697         }
698         debug_msg("DirectShowGrabber::command: decimate=%d (dec)=%d\n", dec, decimate_);
699         if (dec != decimate_) {
700            decimate_ = dec;
701            if (running_) {
702               stop();
703               setsize();
704                           setCaptureOutputFormat();
705               start();
706                        } else{
707               setsize();
708                           setCaptureOutputFormat();
709                        }
710         }
711         return (TCL_OK);
712      } else if (strcmp(argv[1], "port") == 0) {
713         setport(argv[2]);
714         return (TCL_OK);
715      } else if (strcmp(argv[1], "useconfig") ==0) {
716         if (strcmp(argv[2], "1") == 0)
717            useconfig_=1;
718         if (strcmp(argv[2], "0") == 0)
719            useconfig_=0;
720      }
721   }
722   return (Grabber::command(argc, argv));
723
724}
725
726//#########################################################################
727// DirectShowCIFGrabber class
728
729DirectShowCIFGrabber::DirectShowCIFGrabber(IBaseFilter *f) : DirectShowGrabber(f) {
730   debug_msg("DirectShowCIFGrabber\n");
731}
732
733//--------------------------------
734
735DirectShowCIFGrabber::~DirectShowCIFGrabber() {
736   debug_msg("~DirectShowCIFGrabber\n");
737}
738
739//--------------------------------
740
741void DirectShowCIFGrabber::start() {
742   DirectShowGrabber::start();
743   converter(new RGB_Converter_411(24, (u_int8_t *)NULL, 0));
744   ReleaseMutex(cb_mutex_);
745   Grabber::timeout();
746}
747
748//--------------------------------
749
750void DirectShowCIFGrabber::setsize() {
751
752   if(max_width_ >= D1_BASE_WIDTH){
753          max_width_ = D1_BASE_WIDTH;
754      max_height_ = D1_BASE_HEIGHT;
755   }
756
757   if(decimate_ == 1){
758       width_ = max_width_;
759       height_ = max_height_;
760   }else{
761       width_ = basewidth_  / decimate_;
762       height_ = baseheight_ / decimate_;
763   }
764
765   debug_msg("DirectShowCIFGrabber::setsize: %dx%d\n", width_, height_);
766
767   set_size_411(width_, height_);
768   allocref();
769}
770
771//#########################################################################
772// DirectShowDevice class
773
774DirectShowDevice::DirectShowDevice(char *friendlyName, IBaseFilter *pCapFilt) : InputDevice(friendlyName) { 
775
776   attri = new char[100];
777   attri[0] = 0;
778
779   debug_msg("new DirectShowDevice():  friendlyName=%s\n", friendlyName);
780   directShowFilter_  = pCapFilt;           
781   //SV: XXX got rid of 422 format since there's no grabber returned for it and vic crashes
782   attributes_        = "format { 422 411 } size { large small cif } port { external-in } ";
783   DirectShowCIFGrabber o(directShowFilter_);
784   
785   strcat(attri, "format { 411 } size { large small cif } port { ");
786   if(o.hasSVideo() || o.hasComposite()){
787     if(o.hasSVideo()){
788       strcat(attri, "S-Video ");
789     }
790     if(o.hasComposite()){
791       strcat(attri, "Composite ");
792     }
793   }else{
794           strcat(attri, "extern-in ");
795   }
796
797   strcat(attri, "} ");
798   attributes_ = attri;
799
800}
801
802DirectShowDevice::~DirectShowDevice(){
803        delete attri;
804}
805//--------------------------------
806
807int DirectShowDevice::command(int argc, const char* const* argv) {
808   Tcl& tcl = Tcl::instance();
809   if ((argc == 3) && (strcmp(argv[1], "open") == 0)) {
810      TclObject* o = 0;
811          if (strcmp(argv[2], "cif") == 0){
812         o = directShowGrabber_ = new DirectShowCIFGrabber(directShowFilter_);                 
813          }else if (strcmp(argv[2], "422") == 0)         
814         o = directShowGrabber_ = 0; // one day oughta be "new DirectShow422Grabber(directShowFilter_);"  // msp
815
816      if (o != 0)
817         Tcl::instance().result(o->name());
818      return (TCL_OK);
819   }
820   return (InputDevice::command(argc, argv));
821}
822
823//#########################################################################
824// DirectShowScanner class
825
826DirectShowScanner::DirectShowScanner() {
827   ICreateDevEnum *pDevEnum      = 0;
828   int             hr;
829   int             devNum;
830   char            nameBuf[80];
831   
832   // Reference:  Pesce, pp 54-56.   
833
834   debug_msg("new DirectShowScanner()\n");
835
836   // Initialize the COM subsystem
837   hr=CoInitialize(NULL);
838   if (FAILED(hr)) {
839           debug_msg("Failed COM subsystem initialisation.\n");
840                return;
841   }
842
843   // Create a helper object to find the capture devices.
844   hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, IID_ICreateDevEnum, (LPVOID*)&pDevEnum);
845   if (FAILED(hr)) {
846                debug_msg("Failed to Create a helper object to find the DS capture devices.\n");
847                CoUninitialize();
848                return;
849   }
850
851   IEnumMoniker *pEnum    = 0;
852   IMoniker     *pMoniker = 0;
853   IPropertyBag *pPropBag = 0;
854   VARIANT      varName;
855   CLSID                clsid;
856
857   // Get an enumerator over video capture filters
858   hr = pDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &pEnum, 0);
859   //showErrorMessage(hr);
860   if (FAILED(hr) || pEnum == 0) {
861                debug_msg("Failed to Get an enumerator over DS video capture filters.\n");
862                CoUninitialize();
863                return;
864   }
865
866   // Get the capture filter for each device installed, up to NUM_DEVS devices
867   for( devNum=0; devNum < NUM_DEVS; ++devNum) {
868      if ( pEnum->Next(1, &pMoniker, NULL) == S_OK ) {
869
870         hr = pMoniker->BindToStorage(0, 0, IID_IPropertyBag, (void **)&pPropBag);
871             if (FAILED(hr)) {
872                        debug_msg("Failed to Get propbag bound to storage on DS dev: %d\n", devNum);
873                        continue;
874                 }
875         //showErrorMessage(hr);
876         debug_msg("propbag bound to storage ok= %d\n", hr);
877
878         VariantInit(&varName);
879         hr = pPropBag->Read(L"FriendlyName", &varName, 0);
880                 if (FAILED(hr)) {
881                        debug_msg("Failed to Get friendly name read on DS dev: %d\n", devNum);
882                        continue;
883                 }
884         //showErrorMessage(hr);
885         debug_msg("friendly name read ok= %d\n", hr);
886
887         // Need this macro in atlconv.h to go from bStr to char* - msp
888         USES_CONVERSION;
889         strcpy(nameBuf, W2A(varName.bstrVal));
890
891         debug_msg("DirectShowScanner::DirectShowScanner():  found nameBuf/FriendlyName=%s\n", nameBuf);
892
893         // needs work, but don't add drivers that look like VFW drivers - msp
894         //if( (strstr(nameBuf, "VFW") == NULL) ) {
895         hr = pMoniker->BindToObject(0, 0, IID_IBaseFilter, (void **)(pCaptureFilter+devNum));
896         if (FAILED(hr)) {
897                debug_msg("Failed to Get friendly name read on DS dev: %d\n", devNum);
898                continue;
899         }
900         debug_msg("capture filter bound ok= [%d} %s\n", hr, nameBuf);
901         pCaptureFilter[devNum]->GetClassID(&clsid);
902         if (!IsEqualGUID(clsid,CLSID_VfwCapture))       {
903                debug_msg("Adding capture filter %d\n", hr);
904                devs_[devNum] = new DirectShowDevice(strdup(nameBuf), pCaptureFilter[devNum]);
905         } else {
906            debug_msg("discarding an apparent VFW device= %s\n", nameBuf);
907            devs_[devNum] = NULL;
908         }
909         VariantClear(&varName);
910         pPropBag->Release();
911      }
912   }
913
914   // Release these objects so COM can release their memory
915   pMoniker->Release();
916   pEnum->Release();
917   pDevEnum->Release();
918}
919
920//--------------------------------
921
922DirectShowScanner::~DirectShowScanner() {
923   // This should be printed only when the app exits - msp
924   CoUninitialize();
925   debug_msg("~DirectShowScanner()\n");
926}
Note: See TracBrowser for help on using the browser.