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

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

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

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