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

Revision 4750, 44.3 KB (checked in by douglask, 4 years ago)

Inconsistency in ordering of MediaSubTypes? between SampleGrabber? and Capture Filter.

Thanks to Andrew Rowley for spotting

  • 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#define _WIN32_DCOM
122#include <stdio.h>
123#include <assert.h>
124#include <stdlib.h>
125#include "config.h"
126#include <windows.h>
127
128#include "debug.h"
129#include "grabber.h"
130#include "device-input.h"
131#include "module.h"
132#include "rgb-converter.h" 
133#include "yuv_convert.h"
134
135#include "grabber-win32DS.h"
136
137#ifndef VIDE0_FOR_WINDOWS
138static DirectShowScanner findDirectShowDevices;
139#endif
140
141static const GUID MEDIASUBTYPE_I420 =
142{0x30323449, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};
143
144static const GUID MEDIASUBTYPE_HDYC =
145{0x43594448, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};
146
147
148#define NAMEBUF_LEN 200
149
150IBaseFilter  *pCaptureFilter[NUM_DEVS];
151
152//#########################################################################
153// Class Callback, for video framebuffer callback.  See DS Sample Grabber interface docs.
154
155STDMETHODIMP Callback::SampleCB(double sampleTime, IMediaSample *pSample) {
156   return E_NOTIMPL;
157}
158
159STDMETHODIMP Callback::BufferCB(double sampleTime, BYTE *pBuffer, long bufferLen) {
160
161   ///debug_msg("Callback::BufferCB:  sampleTime= %f, pbuffer= %p, bufferLen= %ld\n", sampleTime, pBuffer, bufferLen);
162
163   WaitForSingleObject(grabber->cb_mutex_, INFINITE);
164   grabber->capture(pBuffer, bufferLen);
165   ReleaseMutex(grabber->cb_mutex_);
166   return S_OK;
167}
168
169//-----------------
170
171STDMETHODIMP Callback::QueryInterface(REFIID riid, void **ppvObject) {
172   if (NULL == ppvObject)
173      return E_POINTER;
174   if (riid == __uuidof(IUnknown))
175      *ppvObject = static_cast<IUnknown*>(this);
176   else if (riid == __uuidof(ISampleGrabberCB))
177      *ppvObject = static_cast<ISampleGrabberCB*>(this);
178   else
179      return E_NOTIMPL;
180   AddRef();
181   return S_OK;
182}
183
184//#########################################################################
185// DirectShowGrabber definition
186
187DirectShowGrabber::DirectShowGrabber(IBaseFilter *filt, const char * cformat, const char *nick)  {
188   HRESULT         hr;
189   WCHAR           nameBufW[NAMEBUF_LEN];
190
191   /* Reference:  various, including
192      - Pesce, Chapter 11
193      - http://msdn.microsoft.com/library/default.asp?url=/library/en-us/directshow/htm/samplegrabberfilter.asp
194   */
195
196   debug_msg("new DirectShowGrabber()\n");
197   decimate_ = 2;  //default
198   converter_=0;
199   cb_mutex_=0;
200   crossbar_ = crossbarCursor_ = NULL;
201   pDVVideoDecoder_=0;
202   pDVDecoderBaseFilter_=0;
203   pNullBaseFilter_=0;
204   pSampleGrabber_=0;
205   pGrabberBaseFilter_=0;
206   pMediaControl_=0;
207   pGraph_=0;
208   pBuild_=0;
209   dwRegister_=0;
210   pCaptureFilter_ = filt;
211   pXBar_   = NULL;
212   pFilter_ = NULL;
213   capturing_=0;
214   max_fps_ = 30;
215
216   memset(inputPorts, 0, NUM_PORTS);
217       
218   numInputPorts = 0;
219   initializedPorts = 0;
220   svideoPortNum = compositePortNum = 1;
221
222   if(!strcmp(cformat, "420"))
223       cformat_ = CF_420;
224   else if(!strcmp(cformat, "422"))
225       cformat_ = CF_422;
226   else if(!strcmp(cformat, "cif"))
227       cformat_ = CF_CIF;
228
229   have_I420_ = false;
230   have_UYVY_ = false;
231   have_YUY2_ = false;
232   have_RGB24_= false;
233   have_HDYC_= false;
234   have_DVSD_ = false;
235
236   setport("external-in");
237
238   basewidth_  = NTSC_BASE_WIDTH;
239   baseheight_ = NTSC_BASE_HEIGHT;
240   /*if( is_pal() ) {
241      basewidth_  = PAL_BASE_WIDTH;
242      baseheight_ = PAL_BASE_HEIGHT;
243   } else {
244      basewidth_  = NTSC_BASE_WIDTH;
245      baseheight_ = NTSC_BASE_HEIGHT;
246   }
247   mt_.majortype = MEDIATYPE_AnalogVideo;
248   mt_.subtype   = MEDIASUBTYPE_AnalogVideo_NTSC_M;
249   */
250
251   // callback mutex
252   cb_mutex_ = CreateMutex(NULL, FALSE, NULL);
253
254   callback_           = new Callback();
255   callback_->grabber  = this;
256   debug_msg("DirectShowGrabber::DirectShowGrabber():  callback created, grabber set\n");
257
258   // Make a graph builder object we can use for capture graph building
259   // Create the capture graph builder helper object
260   hr = CoCreateInstance(CLSID_CaptureGraphBuilder2, NULL, CLSCTX_INPROC_SERVER,
261                         IID_ICaptureGraphBuilder2, (void **)&pBuild_);
262   //showErrorMessage(hr);
263   if (FAILED(hr)) {
264                Grabber::status_=-1;
265                return;
266   }
267   debug_msg("DirectShowGrabber::DirectShowGrabber():  graph builder interface acquired\n");
268
269   // Create the Filter Graph Manager
270   hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER,
271                         IID_IGraphBuilder, (void **)&pGraph_);
272   if (FAILED(hr)) {
273                Grabber::status_=-1;
274                return;
275   }
276   debug_msg("DirectShowGrabber::DirectShowGrabber():  graph instance acquired\n");
277
278    // Tell the capture graph builder about the Filter Graph Manager (FGM).
279   hr = pBuild_->SetFiltergraph(pGraph_);
280   //showErrorMessage(hr);
281   if (FAILED(hr)) {
282                Grabber::status_=-1;
283                return;
284   }
285   debug_msg("DirectShowGrabber::DirectShowGrabber():  graph associated with builder\n");
286
287   nameBufW[0] = '\0';
288   hr = MultiByteToWideChar(CP_ACP, 0, nick, -1, nameBufW, NAMEBUF_LEN);
289
290   // Add the capture filter (obtained by the DirectShowDevice Scanner) to the filter graph
291   //hr = pGraph_->AddFilter(pCaptureFilter_, L"VicCaptureFilter");
292   hr = pGraph_->AddFilter(pCaptureFilter_, nameBufW);
293
294   debug_msg("DirectShowGrabber::DirectShowGrabber():  capture filter added to graph: %d\n", hr);
295   //IAMVideoCompression *pVC;
296   /*pCaptureFilter_->AddRef();
297   hr = pBuild_->RenderStream(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video,
298                              pCaptureFilter_, NULL, NULL);
299
300   hr = pBuild_->FindInterface(&PIN_CATEGORY_CAPTURE,
301                               &MEDIATYPE_Video, pCaptureFilter_,
302                               IID_IAMVideoCompression, (void **)&pVC);*/
303
304   // Set up the Sample Grabber transform filter
305   hr = CoCreateInstance(CLSID_SampleGrabber, NULL, CLSCTX_INPROC_SERVER,
306                         IID_IBaseFilter, (LPVOID *)&pGrabberBaseFilter_);
307   //showErrorMessage(hr);
308   debug_msg("DirectShowGrabber::DirectShowGrabber():  grabber base filter instance acquired: %d\n", hr);
309
310   hr = pGrabberBaseFilter_->QueryInterface(IID_ISampleGrabber, (void**)&pSampleGrabber_);
311   //showErrorMessage(hr);
312   debug_msg("DirectShowGrabber::DirectShowGrabber():  Sample Grabber interface acquired\n");
313
314   //hr = pSampleGrabber_->SetMediaType(&mt_);                          showErrorMessage(hr);
315   hr = pSampleGrabber_->SetOneShot(FALSE);                           //showErrorMessage(hr);
316   hr = pSampleGrabber_->SetCallback(callback_, 1);                    //showErrorMessage(hr);
317   hr = pGraph_->AddFilter(pGrabberBaseFilter_,L"VicSampleGrabber");  //showErrorMessage(hr);
318
319   // Get the Null Renderer DS default filter
320   hr = CoCreateInstance(CLSID_NullRenderer, NULL, CLSCTX_INPROC_SERVER,
321                         IID_IBaseFilter, (LPVOID *)&pNullBaseFilter_);
322   //showErrorMessage(hr);
323   if (FAILED(hr)) {
324                Grabber::status_=-1;
325                return;
326   }
327   debug_msg("DirectShowGrabber::DirectShowGrabber():  Null Renderer interface acquired\n");
328
329   // Finally, add the Null Renderer "sink" to the graph
330   hr = pGraph_->AddFilter(pNullBaseFilter_,L"VicNullRenderer");
331    if (FAILED(hr)) {
332                Grabber::status_=-1;
333                return;
334   }
335   debug_msg("DirectShowGrabber::DirectShowGrabber():  Null Renderer added to graph\n");
336
337   if (!getCaptureCapabilities()) {
338                Grabber::status_=-1;
339                return;
340   }
341   //Not needed as width & height aren't known yet.
342   setCaptureOutputFormat();
343
344   if (findCrossbar(pCaptureFilter_)) {
345         routeCrossbar();
346   }
347
348   ZeroMemory(&mt_, sizeof(AM_MEDIA_TYPE));
349   mt_.majortype = MEDIATYPE_Video;
350
351   if (cformat_ == CF_422) {
352           if (have_HDYC_) {
353                   mt_.subtype = MEDIASUBTYPE_HDYC; // Blackmagic Packed YUV 422
354           } else if (have_YUY2_) {
355                   mt_.subtype = MEDIASUBTYPE_YUY2; // Packed YUV 422
356           } else if (have_UYVY_) {
357                   mt_.subtype = MEDIASUBTYPE_UYVY; // Packed YUV 422
358           } else if (have_I420_) {
359                   mt_.subtype = MEDIASUBTYPE_I420; // Planar YUV 420
360           } else if (have_RGB24_) {
361                   mt_.subtype = MEDIASUBTYPE_RGB24; // RGB 24 bit
362           } else {
363                   mt_.subtype = MEDIASUBTYPE_UYVY;
364           }
365   } else {
366           if (have_HDYC_) {
367                   mt_.subtype = MEDIASUBTYPE_HDYC; // Blackmagic Packed YUV 422
368           } else if (have_I420_) {
369                   mt_.subtype = MEDIASUBTYPE_I420; // Planar YUV 420
370           } else if (have_YUY2_) {
371                   mt_.subtype = MEDIASUBTYPE_YUY2; // Packed YUV 422
372           } else if (have_UYVY_) {
373                   mt_.subtype = MEDIASUBTYPE_UYVY; // Packed YUV 422
374           } else if (have_RGB24_) {
375                   mt_.subtype = MEDIASUBTYPE_RGB24; // RGB 24 bit
376           } else {
377                   mt_.subtype = MEDIASUBTYPE_UYVY;
378           }
379   }
380
381   hr = pSampleGrabber_->SetMediaType(&mt_);
382   //showErrorMessage(hr);
383
384   // Obtain the interface used to run, stop, and pause the graph
385   hr = pGraph_->QueryInterface(IID_IMediaControl, (void **)&pMediaControl_);
386   //showErrorMessage(hr);
387   if (FAILED(hr)) {
388                Grabber::status_=-1;
389                return;
390   }
391   debug_msg("DirectShowGrabber::DirectShowGrabber():  graph media control interface acquired\n");
392
393   if (have_DVSD_) {
394           // Get the DV Video Codec DS default filter
395           hr = CoCreateInstance(CLSID_DVVideoCodec, NULL, CLSCTX_INPROC_SERVER,
396                   IID_IBaseFilter, (LPVOID *)&pDVDecoderBaseFilter_);
397           if (FAILED(hr)) {
398                   Grabber::status_=-1;
399                   return;
400           }
401           debug_msg("DirectShowGrabber::DirectShowGrabber():  DV Video Codec interface acquired\n");
402
403           hr = pDVDecoderBaseFilter_->QueryInterface(IID_IIPDVDec, (void**)&pDVVideoDecoder_);
404           debug_msg("DirectShowGrabber::DirectShowGrabber():  Sample Grabber interface acquired\n");
405
406           // Add the DV Video Codec to the graph
407           hr = pGraph_->AddFilter(pDVDecoderBaseFilter_, L"Vic DV Video Decoder");
408           if (FAILED(hr)) {
409                   Grabber::status_=-1;
410                   return;
411           }
412           debug_msg("DirectShowGrabber::DirectShowGrabber():  DV Video Decoder added to graph\n");
413   }
414
415   IMoniker * pMoniker = NULL;
416   IRunningObjectTable *pROT = NULL;
417
418   // register the filter graph instance in the Running Object Table (ROT)
419   // so can use GraphEdit to view graph, e.g.
420   //    in GraphEdit's File menu, click "Connect to Remote Graph..."
421   if (SUCCEEDED(GetRunningObjectTable(0, &pROT))) {
422           const size_t STRING_LENGTH = 256;
423
424           WCHAR wsz[STRING_LENGTH];
425           swprintf(wsz, STRING_LENGTH, L"FilterGraph %08x pid %08x", (DWORD_PTR)pGraph_, GetCurrentProcessId());
426
427           HRESULT hr = CreateItemMoniker(L"!", wsz, &pMoniker);
428           if (SUCCEEDED(hr)) {
429                   hr = pROT->Register(ROTFLAGS_REGISTRATIONKEEPSALIVE, pGraph_, pMoniker, &dwRegister_);
430                   pMoniker->Release();
431           }
432           pROT->Release();
433   }
434}
435
436//--------------------------------
437
438DirectShowGrabber::~DirectShowGrabber() {
439    HRESULT hr;
440
441    debug_msg("~DirectShowGrabber()\n");
442
443    //capturing_ = !capturing_;
444    if (capturing_) hr  = pMediaControl_->Stop();
445    //showErrorMessage(hr);
446
447    CloseHandle(cb_mutex_);
448
449    // unregister the filter graph instance in the Running Object Table (ROT).
450    IRunningObjectTable *pROT;
451    if (SUCCEEDED(GetRunningObjectTable(0, &pROT))) {
452        pROT->Revoke(dwRegister_);
453        pROT->Release();
454    }
455
456    // Release COM objects in reverse order of instantiation
457    callback_->Release();
458    //delete callback; - done by above Release() call
459    if (have_DVSD_) {
460        pDVVideoDecoder_->Release();
461        pDVDecoderBaseFilter_->Release();
462    }
463    pNullBaseFilter_->Release();
464    pSampleGrabber_->Release();
465    pGrabberBaseFilter_->Release();
466    pMediaControl_->Release();
467    pGraph_->Release();
468    pBuild_->Release();
469}
470
471//--------------------------------
472
473bool DirectShowGrabber::findCrossbar(IBaseFilter *pCapF) {
474   HRESULT     hr;
475
476   debug_msg("DirectShowGrabber::FindCrossbar()...\n");
477
478   hr = pBuild_->FindInterface(&LOOK_UPSTREAM_ONLY, NULL, pCapF, IID_IAMCrossbar,
479                             (void**)&pXBar_);
480
481   if ( SUCCEEDED(hr) ) {
482      addCrossbar(pXBar_);
483      hr = pXBar_->QueryInterface(IID_IBaseFilter, (void**)&pFilter_);
484      if ( SUCCEEDED(hr) ) {
485         debug_msg("DirectShowGrabber::FindCrossbar()...Found and added\n");
486         //findCrossbar(pFilter_);
487         //pFilter_.Release();
488         return TRUE;
489      }
490   }
491   return FALSE;
492}
493
494//-------------------------------
495
496void DirectShowGrabber::addCrossbar(IAMCrossbar *xbar) {
497   Crossbar *pCross;
498
499   debug_msg("DirectShowGrabber::addCrossbar()\n");
500
501   pCross = new Crossbar(xbar);
502
503   if( crossbar_ == NULL ) {
504      crossbar_ = pCross;
505   }
506   else {
507      crossbarCursor_->next = pCross;
508   }
509   crossbarCursor_ = pCross;
510}
511
512//-----------------------------
513
514void DirectShowGrabber::routeCrossbar() {
515    HRESULT     hr;
516    long        output           = -1;
517    long        input            = -1;
518    int         videoDecoderPort = -1;
519    int         port;
520    long        related;
521    long        pinType;
522    IAMCrossbar *xb;
523
524    if( crossbar_ == NULL ) return;
525
526    xb = crossbar_->getXBar();
527
528    xb->get_IsRoutedTo(0, &input);
529    debug_msg("DirectShowGrabber::routeCrossbar():  pin %d is routed to output pin 0\n", input);
530
531    hr = xb->get_PinCounts(&output, &input);
532    if(initializedPorts == 0) {
533        initializedPorts=1;
534        svideoPortNum = 1;
535        compositePortNum = 1;
536        numInputPorts = 0;
537        for( int i = 0; i < input; ++i ) {
538            xb->get_CrossbarPinInfo(TRUE, i, &related, &pinType);
539            if( pinType == PhysConn_Video_SVideo ) {
540                debug_msg("Found svideo port %d\n", svideoPortNum);
541                inputPorts[numInputPorts] = new Port();
542                inputPorts[numInputPorts]->id = i;
543                if( svideoPortNum > 1 )
544                    sprintf(inputPorts[numInputPorts]->name,"S-Video%d", svideoPortNum );
545                else
546                    strcpy(inputPorts[numInputPorts]->name,"S-Video");
547                numInputPorts++;
548                svideoPortNum++;
549
550            }
551            if( pinType == PhysConn_Video_Composite ) {
552                debug_msg("Found composite port %d\n", compositePortNum);
553                inputPorts[numInputPorts] = new Port();
554                inputPorts[numInputPorts]->id = i;
555                if(compositePortNum > 1 )
556                    sprintf(inputPorts[numInputPorts]->name,"Composite%d", compositePortNum);
557                else
558                    strcpy(inputPorts[numInputPorts]->name,"Composite");
559                numInputPorts++;
560                compositePortNum++;
561            }
562        }
563    }
564
565    for( int i = 0; i < output; ++i ) {
566        xb->get_CrossbarPinInfo(FALSE, i, &related, &pinType);
567        if( pinType == PhysConn_Video_VideoDecoder ) {
568            videoDecoderPort = i;
569            break;
570        }
571    }
572
573    port = 0;
574    for( int i=0; i<numInputPorts; i++) {
575        if (strcmp(input_port_,inputPorts[i]->name) == 0) {
576            port = inputPorts[i]->id;
577            break;
578        }
579    }
580    if( port == 0 ) {
581        debug_msg("**** Failed to find port for %s\n", input_port_);
582    }
583
584    if( xb->CanRoute(videoDecoderPort, port) == S_FALSE )
585        debug_msg("DirectShowGrabber::routeCrossbar():  cannot route input pin %d to output pin %d\n", port, videoDecoderPort);
586    else {
587        debug_msg("DirectShowGrabber::routeCrossbar() routing pin %d to pin %d\n", port, videoDecoderPort);
588        hr = xb->Route(videoDecoderPort, port);
589        //showErrorMessage(hr);
590    }
591
592    xb->get_IsRoutedTo(videoDecoderPort, &input);
593    debug_msg("DirectShowGrabber::routeCrossbar():  pin %d is now routed to output pin %d\n", input, videoDecoderPort);
594}
595
596//-----------------------------
597
598void DirectShowGrabber::start() {
599   HRESULT hr;
600
601   setsize();
602   setCaptureOutputFormat();
603   hr = pBuild_->RenderStream(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video,
604   pCaptureFilter_, pGrabberBaseFilter_, pNullBaseFilter_);
605   if (SUCCEEDED(hr) )
606       debug_msg("DirectShowGrabber::DirectShowGrabber():  builder render stream\n");
607   else {
608       debug_msg("DirectShowGrabber::DirectShowGrabber():  FAILED to builder render stream: %x\n", hr);
609       // This _usually_ fails because RendersStream has already been called - we should really
610       // diassemble the filterGraph and rebuild it when we change stuff.
611       //stop();
612       //return;
613   }
614
615   WaitForSingleObject(cb_mutex_, INFINITE);
616
617   capturing_  = 1;
618   last_frame_ = NULL;
619
620   debug_msg("DirectShowGrabber::start():  starting capture graph...\n");
621
622   // Run the graph...
623   hr = pMediaControl_->Run();
624   if (SUCCEEDED(hr) )
625       debug_msg("DirectShowGrabber::DirectShowGrabber():  Graph set to Run\n");
626
627   //showErrorMessage(hr);
628
629   switch (cformat_) {
630   case CF_420:
631   case CF_CIF:
632           converter(new RGB_Converter_420(24, (u_int8_t *)NULL, 0));
633           break;
634   case CF_422:
635           converter(new RGB_Converter_422(24, (u_int8_t *)NULL, 0));
636           break;
637   }
638
639   Grabber::start();
640   ReleaseMutex(cb_mutex_);
641   Grabber::timeout();
642}
643
644//--------------------------------
645
646void DirectShowGrabber::stop() {
647   HRESULT hr;
648
649   debug_msg("DirectShowGrabber::stop() thread=%x\n", GetCurrentThreadId());
650
651   if (capturing_) {
652      hr = pMediaControl_->Stop();
653   }
654   //showErrorMessage(hr);
655   ReleaseMutex(cb_mutex_);
656
657   if (converter_) {
658           delete converter_;
659           converter_ = 0;
660   }
661   capturing_  = 0;
662   last_frame_ = 0;
663
664   Grabber::stop();
665}
666
667//--------------------------------
668
669void DirectShowGrabber::fps(int f) {
670   if (f <= 0)
671      f = 1;
672   else if (u_int(f) > max_fps_)
673      f = max_fps_;
674
675   Grabber::fps(f);
676}
677
678void DirectShowGrabber::setsize() {
679
680   if (decimate_ == 1 && !have_DVSD_){  //i.e. Large
681       width_ = max_width_;
682       height_ = max_height_;
683   } else {
684       width_ = basewidth_  / decimate_;
685       height_ = baseheight_ / decimate_;
686   }
687
688   if (have_DVSD_) {
689       switch(decimate_) {
690           case 1:
691             pDVVideoDecoder_->put_IPDisplay(DVRESOLUTION_FULL);
692             break;
693           case 2:
694             pDVVideoDecoder_->put_IPDisplay(DVRESOLUTION_HALF);
695             break;
696           case 4:
697             pDVVideoDecoder_->put_IPDisplay(DVRESOLUTION_QUARTER);
698             break;
699       }
700   }
701
702   debug_msg("DirectShowGrabber::setsize: %dx%d\n", width_, height_);
703
704   switch (cformat_) {
705       case CF_CIF:
706           set_size_cif(width_, height_);
707           break;
708       case CF_420:
709           set_size_420(width_, height_);
710           break;
711       case CF_422:
712           set_size_422(width_, height_);
713           break;
714   }
715
716   allocref();
717}
718
719//--------------------------------
720
721void DirectShowGrabber::capture(BYTE *frameBuf, long bufLen) {
722   last_frame_ = frameBuf;
723   // debug_msg("DirectShowGrabber::capture: frameBuf=%p, last_frame_=%p, bufLen=%ld\n", frameBuf, last_frame_, bufLen);
724}
725
726//--------------------------------
727
728int DirectShowGrabber::grab() {
729   int rval;
730
731
732   //debug_msg("DirectShowGrabber::grab: thread=%x w=%d h=%d bw=%d bh=%d frame_=%p fsize_=%d in=%dx%d out=%dx%d\n",
733   //          GetCurrentThreadId(),
734   //          width_, height_, basewidth_, baseheight_, frame_, framesize_,
735   //          inw_, inh_, outw_, outh_);
736
737   WaitForSingleObject(cb_mutex_, INFINITE);
738
739   if( last_frame_ == NULL || capturing_ == 0 ) {
740      ReleaseMutex(cb_mutex_);
741      return FALSE;
742   }
743   switch (cformat_) {
744   case CF_420:
745   case CF_CIF:
746     if (have_HDYC_)
747       packedUYVY422_to_planarYUYV420((char *)frame_, outw_, outh_, (char *)last_frame_, inw_, inh_);
748     else if (have_I420_)
749       planarYUYV420_to_planarYUYV420((char *)frame_, outw_, outh_, (char *)last_frame_, inw_, inh_);
750     else if (have_YUY2_)
751       packedYUYV422_to_planarYUYV420((char *)frame_, outw_, outh_, (char *)last_frame_, inw_, inh_);
752     else if (have_UYVY_)
753       packedUYVY422_to_planarYUYV420((char *)frame_, outw_, outh_, (char *)last_frame_, inw_, inh_);
754     else if (have_RGB24_)
755       converter_->convert((u_int8_t*)last_frame_, width_, height_, frame_, outw_, outh_, TRUE);
756     else
757       packedUYVY422_to_planarYUYV420((char *)frame_, outw_, outh_, (char *)last_frame_, inw_, inh_);
758     break;
759
760   case CF_422:
761     if (have_HDYC_)
762       packedUYVY422_to_planarYUYV422((char *)frame_, outw_, outh_, (char *)last_frame_, inw_, inh_);
763     if (have_YUY2_)
764       packedYUYV422_to_planarYUYV422((char *)frame_, outw_, outh_, (char *)last_frame_, inw_, inh_);
765     else if (have_UYVY_)
766       packedUYVY422_to_planarYUYV422((char *)frame_, outw_, outh_, (char *)last_frame_, inw_, inh_);
767     else if (have_I420_)
768       planarYUYV420_to_planarYUYV422((char *)frame_, outw_, outh_, (char *)last_frame_, inw_, inh_);
769     else if (have_RGB24_)
770       converter_->convert((u_int8_t*)last_frame_, width_, height_, frame_, outw_, outh_, TRUE);
771         else
772       packedUYVY422_to_planarYUYV422((char *)frame_, outw_, outh_, (char *)last_frame_, inw_, inh_);
773     break;
774   }
775
776   last_frame_ = NULL;
777
778   suppress(frame_);
779   saveblks(frame_);
780   YuvFrame f(media_ts(), frame_, crvec_, outw_, outh_);
781
782   rval = (target_->consume(&f));
783
784   // release block so that callback can get new frame
785   ReleaseMutex(cb_mutex_);
786
787   return rval;
788}
789
790//--------------------------------
791
792void DirectShowGrabber::setport(const char *port) {
793
794   debug_msg("DirectShowGrabber::setport: %s thread=%x\n", port, GetCurrentThreadId());
795   strcpy(input_port_, port);
796   routeCrossbar();
797
798}
799//  Free an existing media type (ie free resources it holds)
800
801void FreeMediaType(AM_MEDIA_TYPE& mt)
802{
803    if (mt.cbFormat != 0) {
804        CoTaskMemFree((PVOID)mt.pbFormat);
805
806        // Strictly unnecessary but tidier
807        mt.cbFormat = 0;
808        mt.pbFormat = NULL;
809    }
810    if (mt.pUnk != NULL) {
811        mt.pUnk->Release();
812        mt.pUnk = NULL;
813    }
814}
815// general purpose function to delete a heap allocated AM_MEDIA_TYPE structure
816// which is useful when calling IEnumMediaTypes::Next as the interface
817// implementation allocates the structures which you must later delete
818// the format block may also be a pointer to an interface to release
819
820void  DeleteMediaType( AM_MEDIA_TYPE *pmt)
821{
822    // allow NULL pointers for coding simplicity
823
824    if (pmt == NULL) {
825        return;
826    }
827
828    FreeMediaType(*pmt);
829    CoTaskMemFree((PVOID)pmt);
830}
831//--------------------------------
832
833int DirectShowGrabber::getCaptureCapabilities(int preferred_max_height) {
834   IAMStreamConfig          *pConfig;
835   AM_MEDIA_TYPE            *pmtConfig;
836   int                      iCount;
837   int                      iSize;
838   VIDEO_STREAM_CONFIG_CAPS scc;
839   HRESULT                  hr;
840   VIDEOINFOHEADER          *pVih;
841
842   pConfig   = NULL;
843   hr        = pBuild_->FindInterface(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video,
844                                     pCaptureFilter_, IID_IAMStreamConfig, (void**)&pConfig);
845   if (FAILED(hr)) {
846       return FALSE;
847   }
848
849   max_width_ = 0;
850   max_height_ = 0;
851   min_width_ = 0xFFFF;
852   min_height_ = 0xFFFF;
853   iCount = iSize = 0;
854   hr     = pConfig->GetNumberOfCapabilities(&iCount, &iSize);
855   // Check the size to make sure we pass in the correct structure.
856   // The alternative output of iSize is AUDIO_STREAM_CONFIG_CAPS, btw.
857   if ( iSize == sizeof(VIDEO_STREAM_CONFIG_CAPS) ) {
858
859       for (int iFormat = 0; iFormat < iCount; iFormat++) {
860           hr = pConfig->GetStreamCaps(iFormat, &pmtConfig, (BYTE *)&scc);
861           //showErrorMessage(hr);
862           if( SUCCEEDED(hr) ) {
863               if ((pmtConfig->majortype  == MEDIATYPE_Video)          &&
864                   (pmtConfig->formattype == FORMAT_VideoInfo)         &&
865                   (pmtConfig->cbFormat   >= sizeof (VIDEOINFOHEADER)) &&
866                   (pmtConfig->pbFormat   != NULL)) {
867                       if(scc.MaxOutputSize.cy > max_height_ && scc.MaxOutputSize.cy <= preferred_max_height){
868                           max_width_  = scc.MaxOutputSize.cx;
869                           max_height_ =  scc.MaxOutputSize.cy;
870                       }
871                       if(scc.MinOutputSize.cx < min_width_){
872                           min_width_  = scc.MinOutputSize.cx;
873                           min_height_ =  scc.MinOutputSize.cy;
874                       }
875                       if (pmtConfig->subtype == MEDIASUBTYPE_I420) {
876                           have_I420_ = true; // Planar YUV 420
877                       } else if (pmtConfig->subtype == MEDIASUBTYPE_UYVY) {
878                           have_UYVY_ = true; // Packed YUV 422
879                       } else if (pmtConfig->subtype == MEDIASUBTYPE_YUY2) {
880                           have_YUY2_ = true; // Packed YUV 422
881                       } else if (pmtConfig->subtype == MEDIASUBTYPE_HDYC) {
882                           have_HDYC_ = true; // Blackmagic Packed YUV 422
883                       } else if (pmtConfig->subtype == MEDIASUBTYPE_RGB24) {
884                           have_RGB24_ = true; // RGB 24 bit
885                       } else if (pmtConfig->subtype == MEDIASUBTYPE_dvsd) {
886                           have_DVSD_ = true; // DV Standard definition
887                       }
888
889                       debug_msg("Windows GDI BITMAPINFOHEADER follows:\n");
890                       pVih                        = (VIDEOINFOHEADER *)pmtConfig->pbFormat;
891                       debug_msg("biWidth=        %d\n", pVih->bmiHeader.biWidth);
892                       debug_msg("biHeight=       %d\n", pVih->bmiHeader.biHeight);
893                       debug_msg("biSize=         %d\n", pVih->bmiHeader.biSize);
894                       debug_msg("biPlanes=       %d\n", pVih->bmiHeader.biPlanes);
895                       debug_msg("biBitCount=     %d\n", pVih->bmiHeader.biBitCount);
896                       debug_msg("biCompression=  %d\n", pVih->bmiHeader.biCompression);
897                       debug_msg("biSizeImage=    %d\n", pVih->bmiHeader.biSizeImage);
898                       debug_msg("biXPelsPerMeter=%d\n", pVih->bmiHeader.biXPelsPerMeter);
899                       debug_msg("biYPelsPerMeter=%d\n", pVih->bmiHeader.biYPelsPerMeter);
900                   }
901                   DeleteMediaType(pmtConfig);
902           }
903       }
904   }
905   pConfig->Release();
906
907   if (min_height_>=NTSC_BASE_HEIGHT && preferred_max_height != 1080)
908       getCaptureCapabilities(1080);
909
910   if (max_width_>0)
911       return TRUE;
912
913   return FALSE;
914}
915
916void DirectShowGrabber::setCaptureOutputFormat() {
917   IAMStreamConfig          *pConfig;
918   int                      iCount;
919   int                      iSize;
920   int                      curr_w=0;
921   int                      curr_h=0;
922   int                      temp_w, temp_h;
923   VIDEOINFOHEADER          *pVih;
924   VIDEO_STREAM_CONFIG_CAPS scc;
925   AM_MEDIA_TYPE            *pmtConfig;
926   int                      formatSet;
927   HRESULT                  hr;
928
929   // Reference http://msdn.microsoft.com/library/default.asp?url=/library/en-us/directshow/htm/configurethevideooutputformat.asp
930
931   debug_msg("DirectShowGrabber::setCaptureOutputFormat(): enter...\n");
932
933   formatSet = 0;
934   pConfig   = NULL;
935   hr        = pBuild_->FindInterface(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video,
936                                     pCaptureFilter_, IID_IAMStreamConfig, (void**)&pConfig);
937   if (FAILED(hr)) {
938        debug_msg("Failed to FindInterface\n");
939        Grabber::status_=-1;
940        return;
941   }
942
943   debug_msg("DirectShowGrabber::setCaptureOutputFormat(): IAMStreamConfig interface acquired\n");
944
945   iCount = iSize = 0;
946   hr     = pConfig->GetNumberOfCapabilities(&iCount, &iSize);
947   // Check the size to make sure we pass in the correct structure.
948   // The alternative output of iSize is AUDIO_STREAM_CONFIG_CAPS, btw.
949   if ( iSize == sizeof(VIDEO_STREAM_CONFIG_CAPS) ) {
950      GUID mediasubtype = MEDIASUBTYPE_NULL;
951
952      switch (cformat_) {
953      case CF_420:
954      case CF_CIF:
955          if (have_HDYC_)
956              mediasubtype = MEDIASUBTYPE_HDYC; // Blackmagic Packed YUV 422
957                  else if (have_I420_)
958              mediasubtype = MEDIASUBTYPE_I420; // Planar YUV 420
959          else if (have_YUY2_)
960              mediasubtype = MEDIASUBTYPE_YUY2; // Packed YUV 422
961          else if (have_UYVY_)
962              mediasubtype = MEDIASUBTYPE_UYVY; // Packed YUV 422
963          else if (have_RGB24_)
964              mediasubtype = MEDIASUBTYPE_RGB24; // RGB 24 bit
965        break;
966
967      case CF_422:
968          if (have_HDYC_)
969              mediasubtype = MEDIASUBTYPE_HDYC; // Blackmagic Packed YUV 422
970          else if (have_YUY2_)
971              mediasubtype = MEDIASUBTYPE_YUY2; // Packed YUV 422
972          else if (have_UYVY_)
973              mediasubtype = MEDIASUBTYPE_UYVY; // Packed YUV 422
974          else if (have_I420_)
975              mediasubtype = MEDIASUBTYPE_I420; // Planar YUV 420
976          else if (have_RGB24_)
977              mediasubtype = MEDIASUBTYPE_RGB24; // RGB 24 bit
978          break;
979      }
980
981      for (int iFormat = 0; iFormat < iCount; iFormat++) {
982         hr = pConfig->GetStreamCaps(iFormat, &pmtConfig, (BYTE *)&scc);
983         //showErrorMessage(hr);
984
985         if( SUCCEEDED(hr) ) {
986            if ((pmtConfig->majortype  == MEDIATYPE_Video)            &&
987                  (pmtConfig->subtype == mediasubtype || mediasubtype == MEDIASUBTYPE_NULL) &&
988                  (pmtConfig->formattype == FORMAT_VideoInfo)         &&
989                  (pmtConfig->cbFormat   >= sizeof (VIDEOINFOHEADER)) &&
990                  (pmtConfig->pbFormat   != NULL) /*                  &&
991                  (scc.MaxOutputSize.cx <= width_)                    &&
992                  (scc.MaxOutputSize.cy <= height_)*/){
993
994               if ( (width_ <= scc.MaxOutputSize.cx) && (width_ >= scc.MinOutputSize.cx) &&
995                                   (height_ <= scc.MaxOutputSize.cx) && (height_ >= scc.MinOutputSize.cy)) {
996
997                    pVih = (VIDEOINFOHEADER *)pmtConfig->pbFormat;
998                    //pVih->bmiHeader.biWidth     = width_;
999                    //pVih->bmiHeader.biHeight    = height_;
1000                    //pVih->bmiHeader.biSizeImage = DIBSIZE(pVih->bmiHeader);
1001                    // AvgTimePerFrame value that specifies the video frame'
1002                    // average display time, in 100-nanosecond units.
1003                    //if (fps_)
1004                    //pVih->AvgTimePerFrame = 10000000/fps_;
1005
1006                    debug_msg("fps_= %d, AvgTimePerFrame: %d\n", fps_, pVih->AvgTimePerFrame);
1007
1008                    debug_msg("Windows GDI BITMAPINFOHEADER follows:\n");
1009                    debug_msg("biWidth=        %d\n", pVih->bmiHeader.biWidth);
1010                    debug_msg("biHeight=       %d\n", pVih->bmiHeader.biHeight);
1011                    debug_msg("biSize=         %d\n", pVih->bmiHeader.biSize);
1012                    debug_msg("biPlanes=       %d\n", pVih->bmiHeader.biPlanes);
1013                    debug_msg("biBitCount=     %d\n", pVih->bmiHeader.biBitCount);
1014                    debug_msg("biCompression=  %d\n", pVih->bmiHeader.biCompression);
1015                    debug_msg("biSizeImage=    %d\n", pVih->bmiHeader.biSizeImage);
1016                    debug_msg("biXPelsPerMeter=%d\n", pVih->bmiHeader.biXPelsPerMeter);
1017                    debug_msg("biYPelsPerMeter=%d\n", pVih->bmiHeader.biYPelsPerMeter);
1018                    debug_msg("biClrUsed=      %d\n", pVih->bmiHeader.biClrUsed);
1019                    debug_msg("biClrImportant= %d\n", pVih->bmiHeader.biClrImportant);
1020
1021                    temp_w = pVih->bmiHeader.biWidth;
1022                    temp_h = pVih->bmiHeader.biHeight;
1023                    pVih->bmiHeader.biWidth     = width_;
1024                    pVih->bmiHeader.biHeight    = height_;
1025                    hr = pConfig->SetFormat(pmtConfig);
1026                    if (SUCCEEDED(hr)) {
1027                        curr_w = width_;
1028                        curr_h = height_;
1029                        formatSet = 1;
1030                        debug_msg("Set(wxh): %dx%d, and Got: %dx%d\n",width_,height_, pVih->bmiHeader.biWidth, pVih->bmiHeader.biHeight);
1031                        break;
1032                    } else {
1033                        if ((temp_w < width_) && (temp_h < height_)) {
1034                            pVih->bmiHeader.biWidth = temp_w;
1035                            pVih->bmiHeader.biHeight = temp_h;
1036                            hr = pConfig->SetFormat(pmtConfig);
1037                            if (SUCCEEDED(hr)) {
1038                                curr_w = temp_w;
1039                                curr_h = temp_h;
1040                                formatSet = 1;
1041                                debug_msg("Set(wxh): %dx%d, and Got: %dx%d\n",width_,height_, pVih->bmiHeader.biWidth, pVih->bmiHeader.biHeight);
1042                            }
1043                        } else {
1044                            debug_msg("Failed to Set format this time - trying again\n");
1045                        }
1046                    }
1047               }
1048            }
1049            DeleteMediaType(pmtConfig);
1050         }
1051      }
1052   }
1053   pConfig->Release();
1054
1055   if ( formatSet ) {
1056      if ( (curr_w != width_) || (curr_h != height_ )) {
1057           width_  = curr_w;
1058           height_ = curr_h;
1059           debug_msg("DirectShowGrabber::setCaptureOutputFormat:  format set to near res: %dx%d\n",width_,height_);
1060      } else
1061           debug_msg("DirectShowGrabber::setCaptureOutputFormat:  format set\n");
1062   }
1063   else
1064      debug_msg("DirectShowGrabber::setCaptureOutputFormat:  format not set\n");
1065}
1066
1067//--------------------------------
1068
1069int DirectShowGrabber::command(int argc, const char* const* argv) {
1070
1071   if (argc == 3) {
1072      if (strcmp(argv[1], "decimate") == 0) {
1073         u_int dec = (u_int)atoi(argv[2]);
1074         Tcl& tcl = Tcl::instance();
1075         if (dec <= 0) {
1076            tcl.resultf("%s: divide by zero", argv[0]);
1077            return (TCL_ERROR);
1078         }
1079         debug_msg("DirectShowGrabber::command: decimate=%d (dec)=%d\n", dec, decimate_);
1080         if (dec != decimate_) {
1081            decimate_ = dec;
1082            if (running_) {
1083                   stop();
1084                   setsize();
1085                   setCaptureOutputFormat();
1086                   start();
1087            } else{
1088                   setsize();
1089                   setCaptureOutputFormat();
1090            }
1091         }
1092         return (TCL_OK);
1093      } else if (strcmp(argv[1], "port") == 0) {
1094         setport(argv[2]);
1095         return (TCL_OK);
1096      } else if (strcmp(argv[1], "type") == 0) {
1097         if (strcmp(argv[2], "auto") == 0)
1098                   ;
1099         else if (strcmp(argv[2], "pal") == 0) {
1100                 if (have_DVSD_) { // DV Standard definition
1101                         basewidth_  = 720;
1102                         baseheight_ = 576;
1103                 } else {
1104                         basewidth_  = CIF_BASE_WIDTH;
1105                         baseheight_ = CIF_BASE_HEIGHT;
1106                 }
1107         } else if (strcmp(argv[2], "ntsc") == 0) {
1108                 if (have_DVSD_) { // DV Standard definition
1109                         basewidth_  = 720;
1110                         baseheight_ = 480;
1111                 } else {
1112                         basewidth_  = NTSC_BASE_WIDTH;
1113                         baseheight_ = NTSC_BASE_HEIGHT;
1114                 }
1115         }
1116         if (running_) {
1117                   stop();
1118                   setsize();
1119                   setCaptureOutputFormat();
1120                   start();
1121         } else {
1122                   setsize();
1123                   setCaptureOutputFormat();
1124         }
1125         return (TCL_OK);
1126      } else if (strcmp(argv[1], "useconfig") ==0) {
1127         if (strcmp(argv[2], "1") == 0)
1128            useconfig_=1;
1129         if (strcmp(argv[2], "0") == 0)
1130            useconfig_=0;
1131      }
1132   }
1133   return (Grabber::command(argc, argv));
1134
1135}
1136
1137
1138//#########################################################################
1139// DirectShowDevice class
1140
1141DirectShowDevice::DirectShowDevice(char *friendlyName, IBaseFilter *pCapFilt) : InputDevice(friendlyName) {
1142
1143   attri_ = new char[255];
1144   attri_[0] = 0;
1145
1146   debug_msg("new DirectShowDevice():  friendlyName=%s\n", friendlyName);
1147   pDirectShowFilter_  = pCapFilt;
1148   DirectShowGrabber o(pDirectShowFilter_, "420", friendlyName);
1149
1150   strcat(attri_, "format { 420 422 cif } size { ");
1151
1152   if ((o.minHeight() > (CIF_BASE_HEIGHT / 2)) && !o.hasDV_SD()) {
1153     strcat(attri_, "large");
1154   } else if (o.maxWidth() < NTSC_BASE_WIDTH) {
1155     strcat(attri_, "small cif");
1156   } else {
1157     strcat(attri_, "small cif large");
1158   }
1159
1160   strcat(attri_, " } type { pal ntsc } port { ");
1161
1162   Port **inputPorts = o.getInputPorts();
1163   if(inputPorts[0] != NULL) {
1164       int i=0;
1165       while( i <  NUM_PORTS && inputPorts[i] != NULL ) {
1166           strcat(attri_, inputPorts[i]->name);
1167           strcat(attri_, " ");
1168           i++;
1169       }
1170   }else{
1171       strcat(attri_, "external-in ");
1172   }
1173
1174   strcat(attri_, "} ");
1175   attributes_ = attri_;
1176}
1177
1178DirectShowDevice::~DirectShowDevice(){
1179    // Release necessary as ATL smart pointers are NOT used.
1180    pDirectShowFilter_->Release();
1181    delete attri_;
1182}
1183//--------------------------------
1184
1185int DirectShowDevice::command(int argc, const char* const* argv) {
1186   Tcl& tcl = Tcl::instance();
1187   if ((argc == 3) && (strcmp(argv[1], "open") == 0)) {
1188      TclObject* o = 0;
1189
1190      o = directShowGrabber_ = new DirectShowGrabber(pDirectShowFilter_, argv[2]);
1191      if (o != 0)
1192         Tcl::instance().result(o->name());
1193      return (TCL_OK);
1194   }
1195   return (InputDevice::command(argc, argv));
1196}
1197
1198//#########################################################################
1199// DirectShowScanner class
1200
1201DirectShowScanner::DirectShowScanner():pMoniker_(0)
1202{
1203        ICreateDevEnum *pDevEnum      = 0;
1204        int             hr;
1205        int             devNum;
1206        char            nameBuf[NAMEBUF_LEN];
1207
1208        // Reference:  Pesce, pp 54-56.
1209
1210        debug_msg("new DirectShowScanner()\n");
1211
1212        // Initialize the COM subsystem - it seems that CoInitializeEx is reccommended with COINIT_MULTITHREADED as the driver will spawn threads.
1213        hr=CoInitializeEx(NULL,COINIT_MULTITHREADED);
1214        if (FAILED(hr)) {
1215                debug_msg("Failed COM subsystem initialisation.\n");
1216                return;
1217        }
1218
1219        // Create a helper object to find the capture devices.
1220        hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, IID_ICreateDevEnum, (LPVOID*)&pDevEnum);
1221        if (FAILED(hr)) {
1222                debug_msg("Failed to Create a helper object to find the DS capture devices.\n");
1223                CoUninitialize();
1224                return;
1225        }
1226
1227        IEnumMoniker *pEnum    = 0;
1228        IPropertyBag *pPropBag = 0;
1229        VARIANT      varName;
1230        CLSID           clsid;
1231
1232        // Get an enumerator over video capture filters
1233        hr = pDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &pEnum, 0);
1234        //showErrorMessage(hr);
1235        if (FAILED(hr) || pEnum == 0) {
1236                debug_msg("Failed to Get an enumerator over DS video capture filters.\n");
1237                CoUninitialize();
1238                return;
1239        }
1240
1241        // Get the capture filter for each device installed, up to NUM_DEVS devices
1242        for( devNum=0; devNum < NUM_DEVS; ++devNum) {
1243                if ( pEnum->Next(1, &pMoniker_, NULL) == S_OK ) {
1244
1245                        hr = pMoniker_->BindToStorage(0, 0, IID_IPropertyBag, (void **)&pPropBag);
1246                        if (FAILED(hr)) {
1247                                debug_msg("Failed to Get propbag bound to storage on DS dev: %d\n", devNum);
1248                                continue;
1249                    }
1250                        //showErrorMessage(hr);
1251                        debug_msg("propbag bound to storage ok= %d\n", hr);
1252
1253                        VariantInit(&varName);
1254                        hr = pPropBag->Read(L"FriendlyName", &varName, 0);
1255                        if (FAILED(hr)) {
1256                                debug_msg("Failed to Get friendly name read on DS dev: %d\n", devNum);
1257                                VariantClear(&varName);
1258                                pPropBag->Release();
1259                                continue;
1260                    }
1261                        //showErrorMessage(hr);
1262                        debug_msg("friendly name read ok= %d\n", hr);
1263
1264                        // Need this macro in atlconv.h to go from bStr to char* - msp
1265                        //USES_CONVERSION;
1266
1267                        nameBuf[0] = '\0';
1268                        WideCharToMultiByte(CP_ACP, 0, varName.bstrVal, -1, nameBuf, NAMEBUF_LEN,  NULL, NULL);
1269                        //strcpy(nameBuf, W2A(varName.bstrVal));
1270
1271                        debug_msg("DirectShowScanner::DirectShowScanner():  found nameBuf/FriendlyName=%s\n", nameBuf);
1272
1273                        // needs work, but don't add drivers that look like VFW drivers - msp
1274                        //if( (strstr(nameBuf, "VFW") == NULL) ) {
1275                        hr = pMoniker_->BindToObject(0, 0, IID_IBaseFilter, (void **)&pCaptureFilter[devNum]);
1276                        if (FAILED(hr)) {
1277                                debug_msg("Failed to Get friendly name read on DS dev: %d\n", devNum);
1278                                VariantClear(&varName);
1279                                pPropBag->Release();
1280                                continue;
1281                        }
1282                        debug_msg("capture filter bound ok= [%d} %s\n", hr, nameBuf);
1283                        pCaptureFilter[devNum]->GetClassID(&clsid);
1284                        VariantClear(&varName);
1285                        if (!IsEqualGUID(clsid,CLSID_VfwCapture))        {
1286                                pMoniker_->AddRef();
1287                                debug_msg("Adding capture filter %d\n", hr);
1288                                devs_[devNum] = new DirectShowDevice(strdup(nameBuf), pCaptureFilter[devNum]);
1289                        } else {
1290                                debug_msg("discarding an apparent VFW device= %s\n", nameBuf);
1291                                devs_[devNum] = NULL;
1292                                pMoniker_->Release();
1293                    }
1294                        pPropBag->Release();
1295                }
1296        }
1297
1298        // Release these objects so COM can release their memory
1299        pEnum->Release();
1300        pDevEnum->Release();
1301}
1302
1303//--------------------------------
1304
1305DirectShowScanner::~DirectShowScanner() {
1306   // This should be printed only when the app exits - msp
1307   CoUninitialize();
1308   debug_msg("~DirectShowScanner()\n");
1309}
Note: See TracBrowser for help on using the browser.