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

Revision 4363, 38.9 KB (checked in by douglask, 6 years ago)

Corrected invocation of YUV conversion routines.

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