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

Revision 4894, 49.9 KB (checked in by douglask, 3 years ago)

Ignore DirectShow? Decklink device

Fix for devices that don't have capture_resolution attribute

Revert to default of de-interlacing.

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