Doc: update video processing tutorial code for OpenCV v2.4.9 and v3a
[profile/ivi/opencv.git] / modules / videoio / src / cap_dshow.cpp
1 /*M///////////////////////////////////////////////////////////////////////////////////////
2 //
3 //  IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
4 //
5 //  By downloading, copying, installing or using the software you agree to this license.
6 //  If you do not agree to this license, do not download, install,
7 //  copy or use the software.
8 //
9 //
10 //                        Intel License Agreement
11 //                For Open Source Computer Vision Library
12 //
13 // Copyright (C) 2000, Intel Corporation, all rights reserved.
14 // Third party copyrights are property of their respective owners.
15 //
16 // Redistribution and use in source and binary forms, with or without modification,
17 // are permitted provided that the following conditions are met:
18 //
19 //   * Redistribution's of source code must retain the above copyright notice,
20 //     this list of conditions and the following disclaimer.
21 //
22 //   * Redistribution's in binary form must reproduce the above copyright notice,
23 //     this list of conditions and the following disclaimer in the documentation
24 //     and/or other materials provided with the distribution.
25 //
26 //   * The name of Intel Corporation may not be used to endorse or promote products
27 //     derived from this software without specific prior written permission.
28 //
29 // This software is provided by the copyright holders and contributors "as is" and
30 // any express or implied warranties, including, but not limited to, the implied
31 // warranties of merchantability and fitness for a particular purpose are disclaimed.
32 // In no event shall the Intel Corporation or contributors be liable for any direct,
33 // indirect, incidental, special, exemplary, or consequential damages
34 // (including, but not limited to, procurement of substitute goods or services;
35 // loss of use, data, or profits; or business interruption) however caused
36 // and on any theory of liability, whether in contract, strict liability,
37 // or tort (including negligence or otherwise) arising in any way out of
38 // the use of this software, even if advised of the possibility of such damage.
39 //
40 //M*/
41
42 #include "precomp.hpp"
43
44 #if (defined WIN32 || defined _WIN32) && defined HAVE_DSHOW
45 #include "cap_dshow.hpp"
46
47 /*
48    DirectShow-based Video Capturing module is based on
49    videoInput library by Theodore Watson:
50    http://muonics.net/school/spring05/videoInput/
51
52    Below is the original copyright
53 */
54
55 //THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
56 //IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
57 //FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
58 //AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
59 //LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
60 //OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
61 //THE SOFTWARE.
62
63 //////////////////////////////////////////////////////////
64 //Written by Theodore Watson - theo.watson@gmail.com    //
65 //Do whatever you want with this code but if you find   //
66 //a bug or make an improvement I would love to know!    //
67 //                                                      //
68 //Warning This code is experimental                     //
69 //use at your own risk :)                               //
70 //////////////////////////////////////////////////////////
71 /////////////////////////////////////////////////////////
72 /*                     Shoutouts
73
74 Thanks to:
75
76            Dillip Kumar Kara for crossbar code.
77            Zachary Lieberman for getting me into this stuff
78            and for being so generous with time and code.
79            The guys at Potion Design for helping me with VC++
80            Josh Fisher for being a serious C++ nerd :)
81            Golan Levin for helping me debug the strangest
82            and slowest bug in the world!
83
84            And all the people using this library who send in
85            bugs, suggestions and improvements who keep me working on
86            the next version - yeah thanks a lot ;)
87
88 */
89 /////////////////////////////////////////////////////////
90
91 #if defined _MSC_VER && _MSC_VER >= 100
92 //'sprintf': name was marked as #pragma deprecated
93 #pragma warning(disable: 4995)
94 #endif
95
96 #include <tchar.h>
97 #include <stdlib.h>
98 #include <stdio.h>
99 #include <math.h>
100 #include <string.h>
101 #include <wchar.h>
102
103 #include <vector>
104
105 //Include Directshow stuff here so we don't worry about needing all the h files.
106 #if defined _MSC_VER && _MSC_VER >= 1500
107 #  include "DShow.h"
108 #  include "strmif.h"
109 #  include "Aviriff.h"
110 #  include "dvdmedia.h"
111 #  include "bdaiface.h"
112 #else
113 #  ifdef _MSC_VER
114 #  define __extension__
115    typedef BOOL WINBOOL;
116 #endif
117
118 #include "dshow/dshow.h"
119 #include "dshow/dvdmedia.h"
120 #include "dshow/bdatypes.h"
121
122 interface IEnumPIDMap : public IUnknown
123 {
124 public:
125     virtual HRESULT STDMETHODCALLTYPE Next(
126         /* [in] */ ULONG cRequest,
127         /* [size_is][out][in] */ PID_MAP *pPIDMap,
128         /* [out] */ ULONG *pcReceived) = 0;
129
130     virtual HRESULT STDMETHODCALLTYPE Skip(
131         /* [in] */ ULONG cRecords) = 0;
132
133     virtual HRESULT STDMETHODCALLTYPE Reset( void) = 0;
134
135     virtual HRESULT STDMETHODCALLTYPE Clone(
136         /* [out] */ IEnumPIDMap **ppIEnumPIDMap) = 0;
137
138     virtual ~IEnumPIDMap() {}
139 };
140
141 interface IMPEG2PIDMap : public IUnknown
142 {
143     virtual HRESULT STDMETHODCALLTYPE MapPID(
144         /* [in] */ ULONG culPID,
145         /* [in] */ ULONG *pulPID,
146         /* [in] */ MEDIA_SAMPLE_CONTENT MediaSampleContent) = 0;
147
148     virtual HRESULT STDMETHODCALLTYPE UnmapPID(
149         /* [in] */ ULONG culPID,
150         /* [in] */ ULONG *pulPID) = 0;
151
152     virtual HRESULT STDMETHODCALLTYPE EnumPIDMap(
153         /* [out] */ IEnumPIDMap **pIEnumPIDMap) = 0;
154
155     virtual ~IMPEG2PIDMap() {}
156 };
157
158 #endif
159
160 //for threading
161 #include <process.h>
162
163 //this is for TryEnterCriticalSection
164 #ifndef _WIN32_WINNT
165 #define _WIN32_WINNT 0x400
166 #endif
167
168
169 /*
170 MEDIASUBTYPE_I420 : TGUID ='{30323449-0000-0010-8000-00AA00389B71}';
171 MEDIASUBTYPE_Y800 : TGUID ='{30303859-0000-0010-8000-00AA00389B71}';
172 MEDIASUBTYPE_Y8   : TGUID ='{20203859-0000-0010-8000-00AA00389B71}';
173 MEDIASUBTYPE_Y160 : TGUID ='{30363159-0000-0010-8000-00AA00389B71}';
174 MEDIASUBTYPE_YV16 : TGUID ='{32315659-0000-0010-8000-00AA00389B71}';
175 MEDIASUBTYPE_Y422 : TGUID ='{32323459-0000-0010-8000-00AA00389B71}';
176 MEDIASUBTYPE_GREY : TGUID ='{59455247-0000-0010-8000-00AA00389B71}';
177 */
178
179 #include <initguid.h>
180
181 DEFINE_GUID(MEDIASUBTYPE_GREY, 0x59455247, 0x0000, 0x0010, 0x80, 0x00,
182     0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
183 DEFINE_GUID(MEDIASUBTYPE_Y8, 0x20203859, 0x0000, 0x0010, 0x80, 0x00,
184     0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
185 DEFINE_GUID(MEDIASUBTYPE_Y800, 0x30303859, 0x0000, 0x0010, 0x80, 0x00,
186     0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
187
188 DEFINE_GUID(CLSID_CaptureGraphBuilder2,0xbf87b6e1,0x8c27,0x11d0,0xb3,0xf0,0x00,0xaa,0x00,0x37,0x61,0xc5);
189 DEFINE_GUID(CLSID_FilterGraph,0xe436ebb3,0x524f,0x11ce,0x9f,0x53,0x00,0x20,0xaf,0x0b,0xa7,0x70);
190 DEFINE_GUID(CLSID_NullRenderer,0xc1f400a4,0x3f08,0x11d3,0x9f,0x0b,0x00,0x60,0x08,0x03,0x9e,0x37);
191 DEFINE_GUID(CLSID_SampleGrabber,0xc1f400a0,0x3f08,0x11d3,0x9f,0x0b,0x00,0x60,0x08,0x03,0x9e,0x37);
192 DEFINE_GUID(CLSID_SystemDeviceEnum,0x62be5d10,0x60eb,0x11d0,0xbd,0x3b,0x00,0xa0,0xc9,0x11,0xce,0x86);
193 DEFINE_GUID(CLSID_VideoInputDeviceCategory,0x860bb310,0x5d01,0x11d0,0xbd,0x3b,0x00,0xa0,0xc9,0x11,0xce,0x86);
194 DEFINE_GUID(FORMAT_VideoInfo,0x05589f80,0xc356,0x11ce,0xbf,0x01,0x00,0xaa,0x00,0x55,0x59,0x5a);
195 DEFINE_GUID(IID_IAMAnalogVideoDecoder,0xc6e13350,0x30ac,0x11d0,0xa1,0x8c,0x00,0xa0,0xc9,0x11,0x89,0x56);
196 DEFINE_GUID(IID_IAMCameraControl,0xc6e13370,0x30ac,0x11d0,0xa1,0x8c,0x00,0xa0,0xc9,0x11,0x89,0x56);
197 DEFINE_GUID(IID_IAMCrossbar,0xc6e13380,0x30ac,0x11d0,0xa1,0x8c,0x00,0xa0,0xc9,0x11,0x89,0x56);
198 DEFINE_GUID(IID_IAMStreamConfig,0xc6e13340,0x30ac,0x11d0,0xa1,0x8c,0x00,0xa0,0xc9,0x11,0x89,0x56);
199 DEFINE_GUID(IID_IAMVideoProcAmp,0xc6e13360,0x30ac,0x11d0,0xa1,0x8c,0x00,0xa0,0xc9,0x11,0x89,0x56);
200 DEFINE_GUID(IID_IBaseFilter,0x56a86895,0x0ad4,0x11ce,0xb0,0x3a,0x00,0x20,0xaf,0x0b,0xa7,0x70);
201 DEFINE_GUID(IID_ICaptureGraphBuilder2,0x93e5a4e0,0x2d50,0x11d2,0xab,0xfa,0x00,0xa0,0xc9,0xc6,0xe3,0x8d);
202 DEFINE_GUID(IID_ICreateDevEnum,0x29840822,0x5b84,0x11d0,0xbd,0x3b,0x00,0xa0,0xc9,0x11,0xce,0x86);
203 DEFINE_GUID(IID_IGraphBuilder,0x56a868a9,0x0ad4,0x11ce,0xb0,0x3a,0x00,0x20,0xaf,0x0b,0xa7,0x70);
204 DEFINE_GUID(IID_IMPEG2PIDMap,0xafb6c2a1,0x2c41,0x11d3,0x8a,0x60,0x00,0x00,0xf8,0x1e,0x0e,0x4a);
205 DEFINE_GUID(IID_IMediaControl,0x56a868b1,0x0ad4,0x11ce,0xb0,0x3a,0x00,0x20,0xaf,0x0b,0xa7,0x70);
206 DEFINE_GUID(IID_IMediaFilter,0x56a86899,0x0ad4,0x11ce,0xb0,0x3a,0x00,0x20,0xaf,0x0b,0xa7,0x70);
207 DEFINE_GUID(IID_ISampleGrabber,0x6b652fff,0x11fe,0x4fce,0x92,0xad,0x02,0x66,0xb5,0xd7,0xc7,0x8f);
208 DEFINE_GUID(LOOK_UPSTREAM_ONLY,0xac798be0,0x98e3,0x11d1,0xb3,0xf1,0x00,0xaa,0x00,0x37,0x61,0xc5);
209 DEFINE_GUID(MEDIASUBTYPE_AYUV,0x56555941,0x0000,0x0010,0x80,0x00,0x00,0xaa,0x00,0x38,0x9b,0x71);
210 DEFINE_GUID(MEDIASUBTYPE_IYUV,0x56555949,0x0000,0x0010,0x80,0x00,0x00,0xaa,0x00,0x38,0x9b,0x71);
211 DEFINE_GUID(MEDIASUBTYPE_RGB24,0xe436eb7d,0x524f,0x11ce,0x9f,0x53,0x00,0x20,0xaf,0x0b,0xa7,0x70);
212 DEFINE_GUID(MEDIASUBTYPE_RGB32,0xe436eb7e,0x524f,0x11ce,0x9f,0x53,0x00,0x20,0xaf,0x0b,0xa7,0x70);
213 DEFINE_GUID(MEDIASUBTYPE_RGB555,0xe436eb7c,0x524f,0x11ce,0x9f,0x53,0x00,0x20,0xaf,0x0b,0xa7,0x70);
214 DEFINE_GUID(MEDIASUBTYPE_RGB565,0xe436eb7b,0x524f,0x11ce,0x9f,0x53,0x00,0x20,0xaf,0x0b,0xa7,0x70);
215 DEFINE_GUID(MEDIASUBTYPE_I420,0x49343230,0x0000,0x0010,0x80,0x00,0x00,0xaa,0x00,0x38,0x9b,0x71);
216 DEFINE_GUID(MEDIASUBTYPE_UYVY,0x59565955,0x0000,0x0010,0x80,0x00,0x00,0xaa,0x00,0x38,0x9b,0x71);
217 DEFINE_GUID(MEDIASUBTYPE_Y211,0x31313259,0x0000,0x0010,0x80,0x00,0x00,0xaa,0x00,0x38,0x9b,0x71);
218 DEFINE_GUID(MEDIASUBTYPE_Y411,0x31313459,0x0000,0x0010,0x80,0x00,0x00,0xaa,0x00,0x38,0x9b,0x71);
219 DEFINE_GUID(MEDIASUBTYPE_Y41P,0x50313459,0x0000,0x0010,0x80,0x00,0x00,0xaa,0x00,0x38,0x9b,0x71);
220 DEFINE_GUID(MEDIASUBTYPE_YUY2,0x32595559,0x0000,0x0010,0x80,0x00,0x00,0xaa,0x00,0x38,0x9b,0x71);
221 DEFINE_GUID(MEDIASUBTYPE_YUYV,0x56595559,0x0000,0x0010,0x80,0x00,0x00,0xaa,0x00,0x38,0x9b,0x71);
222 DEFINE_GUID(MEDIASUBTYPE_YV12,0x32315659,0x0000,0x0010,0x80,0x00,0x00,0xaa,0x00,0x38,0x9b,0x71);
223 DEFINE_GUID(MEDIASUBTYPE_YVU9,0x39555659,0x0000,0x0010,0x80,0x00,0x00,0xaa,0x00,0x38,0x9b,0x71);
224 DEFINE_GUID(MEDIASUBTYPE_YVYU,0x55595659,0x0000,0x0010,0x80,0x00,0x00,0xaa,0x00,0x38,0x9b,0x71);
225 DEFINE_GUID(MEDIASUBTYPE_MJPG,0x47504A4D, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71); // MGB
226 DEFINE_GUID(MEDIATYPE_Interleaved,0x73766169,0x0000,0x0010,0x80,0x00,0x00,0xaa,0x00,0x38,0x9b,0x71);
227 DEFINE_GUID(MEDIATYPE_Video,0x73646976,0x0000,0x0010,0x80,0x00,0x00,0xaa,0x00,0x38,0x9b,0x71);
228 DEFINE_GUID(PIN_CATEGORY_CAPTURE,0xfb6c4281,0x0353,0x11d1,0x90,0x5f,0x00,0x00,0xc0,0xcc,0x16,0xba);
229 DEFINE_GUID(PIN_CATEGORY_PREVIEW,0xfb6c4282,0x0353,0x11d1,0x90,0x5f,0x00,0x00,0xc0,0xcc,0x16,0xba);
230
231 interface ISampleGrabberCB : public IUnknown
232 {
233     virtual HRESULT STDMETHODCALLTYPE SampleCB(
234         double SampleTime,
235         IMediaSample *pSample) = 0;
236
237     virtual HRESULT STDMETHODCALLTYPE BufferCB(
238         double SampleTime,
239         BYTE *pBuffer,
240         LONG BufferLen) = 0;
241
242     virtual ~ISampleGrabberCB() {}
243 };
244
245 interface ISampleGrabber : public IUnknown
246 {
247     virtual HRESULT STDMETHODCALLTYPE SetOneShot(
248         BOOL OneShot) = 0;
249
250     virtual HRESULT STDMETHODCALLTYPE SetMediaType(
251         const AM_MEDIA_TYPE *pType) = 0;
252
253     virtual HRESULT STDMETHODCALLTYPE GetConnectedMediaType(
254         AM_MEDIA_TYPE *pType) = 0;
255
256     virtual HRESULT STDMETHODCALLTYPE SetBufferSamples(
257         BOOL BufferThem) = 0;
258
259     virtual HRESULT STDMETHODCALLTYPE GetCurrentBuffer(
260         LONG *pBufferSize,
261         LONG *pBuffer) = 0;
262
263     virtual HRESULT STDMETHODCALLTYPE GetCurrentSample(
264         IMediaSample **ppSample) = 0;
265
266     virtual HRESULT STDMETHODCALLTYPE SetCallback(
267         ISampleGrabberCB *pCallback,
268         LONG WhichMethodToCallback) = 0;
269
270     virtual ~ISampleGrabber() {}
271 };
272
273 #ifndef HEADER
274 #define HEADER(p) (&(((VIDEOINFOHEADER*)(p))->bmiHeader))
275 #endif
276
277 //Example Usage
278 /*
279     //create a videoInput object
280     videoInput VI;
281
282     //Prints out a list of available devices and returns num of devices found
283     int numDevices = VI.listDevices();
284
285     int device1 = 0;  //this could be any deviceID that shows up in listDevices
286     int device2 = 1;  //this could be any deviceID that shows up in listDevices
287
288     //if you want to capture at a different frame rate (default is 30)
289     //specify it here, you are not guaranteed to get this fps though.
290     //VI.setIdealFramerate(dev, 60);
291
292     //setup the first device - there are a number of options:
293
294     VI.setupDevice(device1);                           //setup the first device with the default settings
295     //VI.setupDevice(device1, VI_COMPOSITE);           //or setup device with specific connection type
296     //VI.setupDevice(device1, 320, 240);               //or setup device with specified video size
297     //VI.setupDevice(device1, 320, 240, VI_COMPOSITE); //or setup device with video size and connection type
298
299     //VI.setFormat(device1, VI_NTSC_M);                //if your card doesn't remember what format it should be
300                                                        //call this with the appropriate format listed above
301                                                        //NOTE: must be called after setupDevice!
302
303     //optionally setup a second (or third, fourth ...) device - same options as above
304     VI.setupDevice(device2);
305
306     //As requested width and height can not always be accomodated
307     //make sure to check the size once the device is setup
308
309     int width   = VI.getWidth(device1);
310     int height  = VI.getHeight(device1);
311     int size    = VI.getSize(device1);
312
313     unsigned char * yourBuffer1 = new unsigned char[size];
314     unsigned char * yourBuffer2 = new unsigned char[size];
315
316     //to get the data from the device first check if the data is new
317     if(VI.isFrameNew(device1)){
318         VI.getPixels(device1, yourBuffer1, false, false);   //fills pixels as a BGR (for openCV) unsigned char array - no flipping
319         VI.getPixels(device1, yourBuffer2, true, true);     //fills pixels as a RGB (for openGL) unsigned char array - flipping!
320     }
321
322     //same applies to device2 etc
323
324     //to get a settings dialog for the device
325     VI.showSettingsWindow(device1);
326
327
328     //Shut down devices properly
329     VI.stopDevice(device1);
330     VI.stopDevice(device2);
331 */
332
333
334 //////////////////////////////////////   VARS AND DEFS   //////////////////////////////////
335
336
337 //STUFF YOU CAN CHANGE
338
339 //change for verbose debug info
340 static bool verbose = true;
341
342 //if you need VI to use multi threaded com
343 //#define VI_COM_MULTI_THREADED
344
345 //STUFF YOU DON'T CHANGE
346
347 //videoInput defines
348 #define VI_VERSION      0.1995
349 #define VI_MAX_CAMERAS  20
350 #define VI_NUM_TYPES    20 //MGB
351 #define VI_NUM_FORMATS  18 //DON'T TOUCH
352
353 //defines for setPhyCon - tuner is not as well supported as composite and s-video
354 #define VI_COMPOSITE 0
355 #define VI_S_VIDEO   1
356 #define VI_TUNER     2
357 #define VI_USB       3
358 #define VI_1394      4
359
360 //defines for formats
361 #define VI_NTSC_M   0
362 #define VI_PAL_B    1
363 #define VI_PAL_D    2
364 #define VI_PAL_G    3
365 #define VI_PAL_H    4
366 #define VI_PAL_I    5
367 #define VI_PAL_M    6
368 #define VI_PAL_N    7
369 #define VI_PAL_NC   8
370 #define VI_SECAM_B  9
371 #define VI_SECAM_D  10
372 #define VI_SECAM_G  11
373 #define VI_SECAM_H  12
374 #define VI_SECAM_K  13
375 #define VI_SECAM_K1 14
376 #define VI_SECAM_L  15
377 #define VI_NTSC_M_J 16
378 #define VI_NTSC_433 17
379
380
381 //allows us to directShow classes here with the includes in the cpp
382 struct ICaptureGraphBuilder2;
383 struct IGraphBuilder;
384 struct IBaseFilter;
385 struct IAMCrossbar;
386 struct IMediaControl;
387 struct ISampleGrabber;
388 struct IMediaEventEx;
389 struct IAMStreamConfig;
390 struct _AMMediaType;
391 class SampleGrabberCallback;
392 typedef _AMMediaType AM_MEDIA_TYPE;
393
394 //keeps track of how many instances of VI are being used
395 //don't touch
396 //static int comInitCount = 0;
397
398
399 ////////////////////////////////////////   VIDEO DEVICE   ///////////////////////////////////
400
401 class videoDevice{
402
403
404     public:
405
406         videoDevice();
407         void setSize(int w, int h);
408         void NukeDownstream(IBaseFilter *pBF);
409         void destroyGraph();
410         ~videoDevice();
411
412         int videoSize;
413         int width;
414         int height;
415
416         int tryWidth;
417         int tryHeight;
418         GUID tryVideoType;
419
420         ICaptureGraphBuilder2 *pCaptureGraph;    // Capture graph builder object
421         IGraphBuilder *pGraph;                    // Graph builder object
422         IMediaControl *pControl;                // Media control object
423         IBaseFilter *pVideoInputFilter;          // Video Capture filter
424         IBaseFilter *pGrabberF;
425         IBaseFilter * pDestFilter;
426         IAMStreamConfig *streamConf;
427         ISampleGrabber * pGrabber;                // Grabs frame
428         AM_MEDIA_TYPE * pAmMediaType;
429
430         IMediaEventEx * pMediaEvent;
431
432         GUID videoType;
433         long formatType;
434
435         SampleGrabberCallback * sgCallback;
436
437         bool tryDiffSize;
438         bool useCrossbar;
439         bool readyToCapture;
440         bool sizeSet;
441         bool setupStarted;
442         bool specificFormat;
443         bool autoReconnect;
444         int  nFramesForReconnect;
445         unsigned long nFramesRunning;
446         int  connection;
447         int  storeConn;
448         int  myID;
449         long requestedFrameTime; //ie fps
450
451         char  nDeviceName[255];
452         WCHAR wDeviceName[255];
453
454         unsigned char * pixels;
455         char * pBuffer;
456
457 };
458
459 //////////////////////////////////////   VIDEO INPUT   /////////////////////////////////////
460 class videoInput{
461
462     public:
463         videoInput();
464         ~videoInput();
465
466         //turns off console messages - default is to print messages
467         static void setVerbose(bool _verbose);
468
469         //Functions in rough order they should be used.
470         static int listDevices(bool silent = false);
471
472         //needs to be called after listDevices - otherwise returns NULL
473         static char * getDeviceName(int deviceID);
474
475         //choose to use callback based capture - or single threaded
476         void setUseCallback(bool useCallback);
477
478         //call before setupDevice
479         //directshow will try and get the closest possible framerate to what is requested
480         void setIdealFramerate(int deviceID, int idealFramerate);
481
482         //some devices will stop delivering frames after a while - this method gives you the option to try and reconnect
483         //to a device if videoInput detects that a device has stopped delivering frames.
484         //you MUST CALL isFrameNew every app loop for this to have any effect
485         void setAutoReconnectOnFreeze(int deviceNumber, bool doReconnect, int numMissedFramesBeforeReconnect);
486
487         //Choose one of these five to setup your device
488         bool setupDevice(int deviceID);
489         bool setupDevice(int deviceID, int w, int h);
490         bool setupDeviceFourcc(int deviceID, int w, int h,int fourcc);
491
492         //These two are only for capture cards
493         //USB and Firewire cameras souldn't specify connection
494         bool setupDevice(int deviceID, int connection);
495         bool setupDevice(int deviceID, int w, int h, int connection);
496
497         bool setFourcc(int deviceNumber, int fourcc);
498
499         //If you need to you can set your NTSC/PAL/SECAM
500         //preference here. if it is available it will be used.
501         //see #defines above for available formats - eg VI_NTSC_M or VI_PAL_B
502         //should be called after setupDevice
503         //can be called multiple times
504         bool setFormat(int deviceNumber, int format);
505
506         //Tells you when a new frame has arrived - you should call this if you have specified setAutoReconnectOnFreeze to true
507         bool isFrameNew(int deviceID);
508
509         bool isDeviceSetup(int deviceID);
510
511         //Returns the pixels - flipRedAndBlue toggles RGB/BGR flipping - and you can flip the image too
512         unsigned char * getPixels(int deviceID, bool flipRedAndBlue = true, bool flipImage = false);
513
514         //Or pass in a buffer for getPixels to fill returns true if successful.
515         bool getPixels(int id, unsigned char * pixels, bool flipRedAndBlue = true, bool flipImage = false);
516
517         //Launches a pop up settings window
518         //For some reason in GLUT you have to call it twice each time.
519         void showSettingsWindow(int deviceID);
520
521         //Manual control over settings thanks.....
522         //These are experimental for now.
523         bool setVideoSettingFilter(int deviceID, long Property, long lValue, long Flags = 0, bool useDefaultValue = false);
524         bool setVideoSettingFilterPct(int deviceID, long Property, float pctValue, long Flags = 0);
525         bool getVideoSettingFilter(int deviceID, long Property, long &min, long &max, long &SteppingDelta, long &currentValue, long &flags, long &defaultValue);
526
527         bool setVideoSettingCamera(int deviceID, long Property, long lValue, long Flags = 0, bool useDefaultValue = false);
528         bool setVideoSettingCameraPct(int deviceID, long Property, float pctValue, long Flags = 0);
529         bool getVideoSettingCamera(int deviceID, long Property, long &min, long &max, long &SteppingDelta, long &currentValue, long &flags, long &defaultValue);
530
531         //bool setVideoSettingCam(int deviceID, long Property, long lValue, long Flags = NULL, bool useDefaultValue = false);
532
533         //get width, height and number of pixels
534         int  getWidth(int deviceID);
535         int  getHeight(int deviceID);
536         int  getSize(int deviceID);
537         int  getFourcc(int deviceID);
538         double getFPS(int deviceID);
539
540         //completely stops and frees a device
541         void stopDevice(int deviceID);
542
543         //as above but then sets it up with same settings
544         bool restartDevice(int deviceID);
545
546         //number of devices available
547         int  devicesFound;
548
549         // mapping from OpenCV CV_CAP_PROP to videoinput/dshow properties
550         int getVideoPropertyFromCV(int cv_property);
551         int getCameraPropertyFromCV(int cv_property);
552
553     private:
554         void setPhyCon(int deviceID, int conn);
555         void setAttemptCaptureSize(int deviceID, int w, int h,GUID mediaType=MEDIASUBTYPE_RGB24);
556         bool setup(int deviceID);
557         void processPixels(unsigned char * src, unsigned char * dst, int width, int height, bool bRGB, bool bFlip);
558         int  start(int deviceID, videoDevice * VD);
559         int  getDeviceCount();
560         void getMediaSubtypeAsString(GUID type, char * typeAsString);
561         GUID *getMediaSubtypeFromFourcc(int fourcc);
562         int    getFourccFromMediaSubtype(GUID type);
563
564         void getVideoPropertyAsString(int prop, char * propertyAsString);
565         void getCameraPropertyAsString(int prop, char * propertyAsString);
566
567         HRESULT getDevice(IBaseFilter **pSrcFilter, int deviceID, WCHAR * wDeviceName, char * nDeviceName);
568         static HRESULT ShowFilterPropertyPages(IBaseFilter *pFilter);
569         static HRESULT ShowStreamPropertyPages(IAMStreamConfig  *pStream);
570
571         HRESULT SaveGraphFile(IGraphBuilder *pGraph, WCHAR *wszPath);
572         HRESULT routeCrossbar(ICaptureGraphBuilder2 **ppBuild, IBaseFilter **pVidInFilter, int conType, GUID captureMode);
573
574         //don't touch
575         static bool comInit();
576         static bool comUnInit();
577
578         int  connection;
579         int  callbackSetCount;
580         bool bCallback;
581
582         GUID CAPTURE_MODE;
583
584         //Extra video subtypes
585         GUID MEDIASUBTYPE_Y800;
586         GUID MEDIASUBTYPE_Y8;
587         GUID MEDIASUBTYPE_GREY;
588
589         videoDevice * VDList[VI_MAX_CAMERAS];
590         GUID mediaSubtypes[VI_NUM_TYPES];
591         long formatTypes[VI_NUM_FORMATS];
592
593         static void __cdecl basicThread(void * objPtr);
594
595         static char deviceNames[VI_MAX_CAMERAS][255];
596
597 };
598
599 ///////////////////////////  HANDY FUNCTIONS  /////////////////////////////
600
601 static void MyFreeMediaType(AM_MEDIA_TYPE& mt){
602     if (mt.cbFormat != 0)
603     {
604         CoTaskMemFree((PVOID)mt.pbFormat);
605         mt.cbFormat = 0;
606         mt.pbFormat = NULL;
607     }
608     if (mt.pUnk != NULL)
609     {
610         // Unecessary because pUnk should not be used, but safest.
611         mt.pUnk->Release();
612         mt.pUnk = NULL;
613     }
614 }
615
616 static void MyDeleteMediaType(AM_MEDIA_TYPE *pmt)
617 {
618     if (pmt != NULL)
619     {
620         MyFreeMediaType(*pmt);
621         CoTaskMemFree(pmt);
622     }
623 }
624
625 //////////////////////////////  CALLBACK  ////////////////////////////////
626
627 //Callback class
628 class SampleGrabberCallback : public ISampleGrabberCB{
629 public:
630
631     //------------------------------------------------
632     SampleGrabberCallback(){
633         InitializeCriticalSection(&critSection);
634         freezeCheck = 0;
635
636
637         bufferSetup         = false;
638         newFrame            = false;
639         latestBufferLength  = 0;
640
641         hEvent = CreateEvent(NULL, true, false, NULL);
642     }
643
644
645     //------------------------------------------------
646     virtual ~SampleGrabberCallback(){
647         ptrBuffer = NULL;
648         DeleteCriticalSection(&critSection);
649         CloseHandle(hEvent);
650         if(bufferSetup){
651             delete[] pixels;
652         }
653     }
654
655
656     //------------------------------------------------
657     bool setupBuffer(int numBytesIn){
658         if(bufferSetup){
659             return false;
660         }else{
661             numBytes            = numBytesIn;
662             pixels              = new unsigned char[numBytes];
663             bufferSetup         = true;
664             newFrame            = false;
665             latestBufferLength  = 0;
666         }
667         return true;
668     }
669
670
671     //------------------------------------------------
672     STDMETHODIMP_(ULONG) AddRef() { return 1; }
673     STDMETHODIMP_(ULONG) Release() { return 2; }
674
675
676     //------------------------------------------------
677     STDMETHODIMP QueryInterface(REFIID, void **ppvObject){
678         *ppvObject = static_cast<ISampleGrabberCB*>(this);
679         return S_OK;
680     }
681
682
683     //This method is meant to have less overhead
684     //------------------------------------------------
685     STDMETHODIMP SampleCB(double , IMediaSample *pSample){
686         if(WaitForSingleObject(hEvent, 0) == WAIT_OBJECT_0) return S_OK;
687
688         HRESULT hr = pSample->GetPointer(&ptrBuffer);
689
690         if(hr == S_OK){
691             latestBufferLength = pSample->GetActualDataLength();
692               if(latestBufferLength == numBytes){
693                 EnterCriticalSection(&critSection);
694                       memcpy(pixels, ptrBuffer, latestBufferLength);
695                     newFrame    = true;
696                     freezeCheck = 1;
697                 LeaveCriticalSection(&critSection);
698                 SetEvent(hEvent);
699             }else{
700                 printf("ERROR: SampleCB() - buffer sizes do not match\n");
701             }
702         }
703
704         return S_OK;
705     }
706
707
708     //This method is meant to have more overhead
709     STDMETHODIMP BufferCB(double, BYTE *, long){
710         return E_NOTIMPL;
711     }
712
713     int freezeCheck;
714
715     int latestBufferLength;
716     int numBytes;
717     bool newFrame;
718     bool bufferSetup;
719     unsigned char * pixels;
720     unsigned char * ptrBuffer;
721     CRITICAL_SECTION critSection;
722     HANDLE hEvent;
723 };
724
725
726 //////////////////////////////  VIDEO DEVICE  ////////////////////////////////
727
728 // ----------------------------------------------------------------------
729 //    Should this class also be the callback?
730 //
731 // ----------------------------------------------------------------------
732
733 videoDevice::videoDevice(){
734
735      pCaptureGraph      = NULL;    // Capture graph builder object
736      pGraph             = NULL;    // Graph builder object
737      pControl           = NULL;    // Media control object
738      pVideoInputFilter  = NULL; // Video Capture filter
739      pGrabber           = NULL; // Grabs frame
740      pDestFilter        = NULL; // Null Renderer Filter
741      pGrabberF          = NULL; // Grabber Filter
742      pMediaEvent        = NULL;
743      streamConf         = NULL;
744      pAmMediaType       = NULL;
745
746      //This is our callback class that processes the frame.
747      sgCallback           = new SampleGrabberCallback();
748      sgCallback->newFrame = false;
749
750      //Default values for capture type
751      videoType          = MEDIASUBTYPE_RGB24;
752      connection         = PhysConn_Video_Composite;
753      storeConn          = 0;
754
755      videoSize          = 0;
756      width              = 0;
757      height             = 0;
758
759      tryWidth           = 640;
760      tryHeight          = 480;
761      tryVideoType = MEDIASUBTYPE_RGB24;
762      nFramesForReconnect= 10000;
763      nFramesRunning     = 0;
764      myID               = -1;
765
766      tryDiffSize        = true;
767      useCrossbar        = false;
768      readyToCapture     = false;
769      sizeSet            = false;
770      setupStarted       = false;
771      specificFormat     = false;
772      autoReconnect      = false;
773      requestedFrameTime = -1;
774
775      memset(wDeviceName, 0, sizeof(WCHAR) * 255);
776      memset(nDeviceName, 0, sizeof(char) * 255);
777
778 }
779
780
781 // ----------------------------------------------------------------------
782 //    The only place we are doing new
783 //
784 // ----------------------------------------------------------------------
785
786 void videoDevice::setSize(int w, int h){
787     if(sizeSet){
788         if(verbose)printf("SETUP: Error device size should not be set more than once \n");
789     }
790     else
791     {
792         width               = w;
793         height              = h;
794         videoSize           = w*h*3;
795         sizeSet             = true;
796         pixels              = new unsigned char[videoSize];
797         pBuffer             = new char[videoSize];
798
799         memset(pixels, 0 , videoSize);
800         sgCallback->setupBuffer(videoSize);
801
802     }
803 }
804
805
806 // ----------------------------------------------------------------------
807 //    Borrowed from the SDK, use it to take apart the graph from
808 //  the capture device downstream to the null renderer
809 // ----------------------------------------------------------------------
810
811 void videoDevice::NukeDownstream(IBaseFilter *pBF){
812     IPin *pP, *pTo;
813     ULONG u;
814     IEnumPins *pins = NULL;
815     PIN_INFO pininfo;
816     HRESULT hr = pBF->EnumPins(&pins);
817     pins->Reset();
818     while (hr == NOERROR)
819     {
820         hr = pins->Next(1, &pP, &u);
821         if (hr == S_OK && pP)
822         {
823             pP->ConnectedTo(&pTo);
824             if (pTo)
825             {
826                 hr = pTo->QueryPinInfo(&pininfo);
827                 if (hr == NOERROR)
828                 {
829                     if (pininfo.dir == PINDIR_INPUT)
830                     {
831                         NukeDownstream(pininfo.pFilter);
832                         pGraph->Disconnect(pTo);
833                         pGraph->Disconnect(pP);
834                         pGraph->RemoveFilter(pininfo.pFilter);
835                     }
836                     pininfo.pFilter->Release();
837                     pininfo.pFilter = NULL;
838                 }
839                 pTo->Release();
840             }
841             pP->Release();
842         }
843     }
844     if (pins) pins->Release();
845 }
846
847
848 // ----------------------------------------------------------------------
849 //    Also from SDK
850 // ----------------------------------------------------------------------
851
852 void videoDevice::destroyGraph(){
853     HRESULT hr = 0;
854      //int FuncRetval=0;
855      //int NumFilters=0;
856
857     int i = 0;
858     while (hr == NOERROR)
859     {
860         IEnumFilters * pEnum = 0;
861         ULONG cFetched;
862
863         // We must get the enumerator again every time because removing a filter from the graph
864         // invalidates the enumerator. We always get only the first filter from each enumerator.
865         hr = pGraph->EnumFilters(&pEnum);
866         if (FAILED(hr)) { if(verbose)printf("SETUP: pGraph->EnumFilters() failed. \n"); return; }
867
868         IBaseFilter * pFilter = NULL;
869         if (pEnum->Next(1, &pFilter, &cFetched) == S_OK)
870         {
871             FILTER_INFO FilterInfo;
872             memset(&FilterInfo, 0, sizeof(FilterInfo));
873             hr = pFilter->QueryFilterInfo(&FilterInfo);
874             FilterInfo.pGraph->Release();
875
876             int count = 0;
877             char buffer[255];
878             memset(buffer, 0, 255 * sizeof(char));
879
880             while( FilterInfo.achName[count] != 0x00 )
881             {
882                 buffer[count] = (char)FilterInfo.achName[count];
883                 count++;
884             }
885
886             if(verbose)printf("SETUP: removing filter %s...\n", buffer);
887             hr = pGraph->RemoveFilter(pFilter);
888             if (FAILED(hr)) { if(verbose)printf("SETUP: pGraph->RemoveFilter() failed. \n"); return; }
889             if(verbose)printf("SETUP: filter removed %s  \n",buffer);
890
891             pFilter->Release();
892             pFilter = NULL;
893         }
894         else break;
895         pEnum->Release();
896         pEnum = NULL;
897         i++;
898     }
899
900  return;
901 }
902
903
904 // ----------------------------------------------------------------------
905 // Our deconstructor, attempts to tear down graph and release filters etc
906 // Does checking to make sure it only is freeing if it needs to
907 // Probably could be a lot cleaner! :)
908 // ----------------------------------------------------------------------
909
910 videoDevice::~videoDevice(){
911
912     if(setupStarted){ if(verbose)printf("\nSETUP: Disconnecting device %i\n", myID); }
913     else{
914         if(sgCallback){
915             sgCallback->Release();
916             delete sgCallback;
917         }
918         return;
919     }
920
921     HRESULT HR = NOERROR;
922
923     //Stop the callback and free it
924     if( (sgCallback) && (pGrabber) )
925     {
926         pGrabber->SetCallback(NULL, 1);
927         if(verbose)printf("SETUP: freeing Grabber Callback\n");
928         sgCallback->Release();
929
930         //delete our pixels
931         if(sizeSet){
932              delete[] pixels;
933              delete[] pBuffer;
934         }
935
936         delete sgCallback;
937     }
938
939     //Check to see if the graph is running, if so stop it.
940      if( (pControl) )
941     {
942         HR = pControl->Pause();
943         if (FAILED(HR)) if(verbose)printf("ERROR - Could not pause pControl\n");
944
945         HR = pControl->Stop();
946         if (FAILED(HR)) if(verbose)printf("ERROR - Could not stop pControl\n");
947     }
948
949     //Disconnect filters from capture device
950     if( (pVideoInputFilter) )NukeDownstream(pVideoInputFilter);
951
952     //Release and zero pointers to our filters etc
953     if( (pDestFilter) ){        if(verbose)printf("SETUP: freeing Renderer \n");
954                                 (pDestFilter)->Release();
955                                 (pDestFilter) = 0;
956     }
957     if( (pVideoInputFilter) ){  if(verbose)printf("SETUP: freeing Capture Source \n");
958                                 (pVideoInputFilter)->Release();
959                                 (pVideoInputFilter) = 0;
960     }
961     if( (pGrabberF) ){          if(verbose)printf("SETUP: freeing Grabber Filter  \n");
962                                 (pGrabberF)->Release();
963                                 (pGrabberF) = 0;
964     }
965     if( (pGrabber) ){           if(verbose)printf("SETUP: freeing Grabber  \n");
966                                 (pGrabber)->Release();
967                                 (pGrabber) = 0;
968     }
969     if( (pControl) ){           if(verbose)printf("SETUP: freeing Control   \n");
970                                 (pControl)->Release();
971                                 (pControl) = 0;
972     }
973     if( (pMediaEvent) ){        if(verbose)printf("SETUP: freeing Media Event  \n");
974                                 (pMediaEvent)->Release();
975                                 (pMediaEvent) = 0;
976     }
977     if( (streamConf) ){         if(verbose)printf("SETUP: freeing Stream  \n");
978                                 (streamConf)->Release();
979                                 (streamConf) = 0;
980     }
981
982     if( (pAmMediaType) ){       if(verbose)printf("SETUP: freeing Media Type  \n");
983                                 MyDeleteMediaType(pAmMediaType);
984     }
985
986     if((pMediaEvent)){
987             if(verbose)printf("SETUP: freeing Media Event  \n");
988             (pMediaEvent)->Release();
989             (pMediaEvent) = 0;
990     }
991
992     //Destroy the graph
993     if( (pGraph) )destroyGraph();
994
995     //Release and zero our capture graph and our main graph
996     if( (pCaptureGraph) ){      if(verbose)printf("SETUP: freeing Capture Graph \n");
997                                 (pCaptureGraph)->Release();
998                                 (pCaptureGraph) = 0;
999     }
1000     if( (pGraph) ){             if(verbose)printf("SETUP: freeing Main Graph \n");
1001                                 (pGraph)->Release();
1002                                 (pGraph) = 0;
1003     }
1004
1005     //delete our pointers
1006     delete pDestFilter;
1007     delete pVideoInputFilter;
1008     delete pGrabberF;
1009     delete pGrabber;
1010     delete pControl;
1011     delete streamConf;
1012     delete pMediaEvent;
1013     delete pCaptureGraph;
1014     delete pGraph;
1015
1016     if(verbose)printf("SETUP: Device %i disconnected and freed\n\n",myID);
1017 }
1018
1019
1020 //////////////////////////////  VIDEO INPUT  ////////////////////////////////
1021 ////////////////////////////  PUBLIC METHODS  ///////////////////////////////
1022
1023
1024 // ----------------------------------------------------------------------
1025 // Constructor - creates instances of videoDevice and adds the various
1026 // media subtypes to check.
1027 // ----------------------------------------------------------------------
1028
1029 videoInput::videoInput(){
1030     //start com
1031     comInit();
1032
1033     devicesFound         = 0;
1034     callbackSetCount     = 0;
1035     bCallback            = true;
1036
1037     //setup a max no of device objects
1038     for(int i=0; i<VI_MAX_CAMERAS; i++)  VDList[i] = new videoDevice();
1039
1040     if(verbose)printf("\n***** VIDEOINPUT LIBRARY - %2.04f - TFW07 *****\n\n",VI_VERSION);
1041
1042     //added for the pixelink firewire camera
1043      //MEDIASUBTYPE_Y800 = (GUID)FOURCCMap(FCC('Y800'));
1044      //MEDIASUBTYPE_Y8   = (GUID)FOURCCMap(FCC('Y8'));
1045      //MEDIASUBTYPE_GREY = (GUID)FOURCCMap(FCC('GREY'));
1046
1047     //The video types we support
1048     //in order of preference
1049
1050     mediaSubtypes[0]     = MEDIASUBTYPE_RGB24;
1051     mediaSubtypes[1]     = MEDIASUBTYPE_RGB32;
1052     mediaSubtypes[2]     = MEDIASUBTYPE_RGB555;
1053     mediaSubtypes[3]     = MEDIASUBTYPE_RGB565;
1054     mediaSubtypes[4]     = MEDIASUBTYPE_YUY2;
1055     mediaSubtypes[5]     = MEDIASUBTYPE_YVYU;
1056     mediaSubtypes[6]     = MEDIASUBTYPE_YUYV;
1057     mediaSubtypes[7]     = MEDIASUBTYPE_IYUV;
1058     mediaSubtypes[8]     = MEDIASUBTYPE_UYVY;
1059     mediaSubtypes[9]     = MEDIASUBTYPE_YV12;
1060     mediaSubtypes[10]    = MEDIASUBTYPE_YVU9;
1061     mediaSubtypes[11]    = MEDIASUBTYPE_Y411;
1062     mediaSubtypes[12]    = MEDIASUBTYPE_Y41P;
1063     mediaSubtypes[13]    = MEDIASUBTYPE_Y211;
1064     mediaSubtypes[14]    = MEDIASUBTYPE_AYUV;
1065     mediaSubtypes[15]    = MEDIASUBTYPE_MJPG; // MGB
1066
1067     //non standard
1068     mediaSubtypes[16]    = MEDIASUBTYPE_Y800;
1069     mediaSubtypes[17]    = MEDIASUBTYPE_Y8;
1070     mediaSubtypes[18]    = MEDIASUBTYPE_GREY;
1071     mediaSubtypes[19]    = MEDIASUBTYPE_I420;
1072
1073     //The video formats we support
1074     formatTypes[VI_NTSC_M]      = AnalogVideo_NTSC_M;
1075     formatTypes[VI_NTSC_M_J]    = AnalogVideo_NTSC_M_J;
1076     formatTypes[VI_NTSC_433]    = AnalogVideo_NTSC_433;
1077
1078     formatTypes[VI_PAL_B]       = AnalogVideo_PAL_B;
1079     formatTypes[VI_PAL_D]       = AnalogVideo_PAL_D;
1080     formatTypes[VI_PAL_G]       = AnalogVideo_PAL_G;
1081     formatTypes[VI_PAL_H]       = AnalogVideo_PAL_H;
1082     formatTypes[VI_PAL_I]       = AnalogVideo_PAL_I;
1083     formatTypes[VI_PAL_M]       = AnalogVideo_PAL_M;
1084     formatTypes[VI_PAL_N]       = AnalogVideo_PAL_N;
1085     formatTypes[VI_PAL_NC]      = AnalogVideo_PAL_N_COMBO;
1086
1087     formatTypes[VI_SECAM_B]     = AnalogVideo_SECAM_B;
1088     formatTypes[VI_SECAM_D]     = AnalogVideo_SECAM_D;
1089     formatTypes[VI_SECAM_G]     = AnalogVideo_SECAM_G;
1090     formatTypes[VI_SECAM_H]     = AnalogVideo_SECAM_H;
1091     formatTypes[VI_SECAM_K]     = AnalogVideo_SECAM_K;
1092     formatTypes[VI_SECAM_K1]    = AnalogVideo_SECAM_K1;
1093     formatTypes[VI_SECAM_L]     = AnalogVideo_SECAM_L;
1094
1095 }
1096
1097
1098 // ----------------------------------------------------------------------
1099 // static - set whether messages get printed to console or not
1100 //
1101 // ----------------------------------------------------------------------
1102
1103 void videoInput::setVerbose(bool _verbose){
1104     verbose = _verbose;
1105 }
1106
1107 // ----------------------------------------------------------------------
1108 // change to use callback or regular capture
1109 // callback tells you when a new frame has arrived
1110 // but non-callback won't - but is single threaded
1111 // ----------------------------------------------------------------------
1112 void videoInput::setUseCallback(bool useCallback){
1113     if(callbackSetCount == 0){
1114         bCallback = useCallback;
1115         callbackSetCount = 1;
1116     }else{
1117         printf("ERROR: setUseCallback can only be called before setup\n");
1118     }
1119 }
1120
1121 // ----------------------------------------------------------------------
1122 // Set the requested framerate - no guarantee you will get this
1123 //
1124 // ----------------------------------------------------------------------
1125
1126 void videoInput::setIdealFramerate(int deviceNumber, int idealFramerate){
1127     if(deviceNumber >= VI_MAX_CAMERAS || VDList[deviceNumber]->readyToCapture) return;
1128
1129     if( idealFramerate > 0 ){
1130         VDList[deviceNumber]->requestedFrameTime = (unsigned long)(10000000 / idealFramerate);
1131     }
1132 }
1133
1134
1135 // ----------------------------------------------------------------------
1136 // Set the requested framerate - no guarantee you will get this
1137 //
1138 // ----------------------------------------------------------------------
1139
1140 void videoInput::setAutoReconnectOnFreeze(int deviceNumber, bool doReconnect, int numMissedFramesBeforeReconnect){
1141     if(deviceNumber >= VI_MAX_CAMERAS) return;
1142
1143     VDList[deviceNumber]->autoReconnect            = doReconnect;
1144     VDList[deviceNumber]->nFramesForReconnect    = numMissedFramesBeforeReconnect;
1145
1146 }
1147
1148
1149 // ----------------------------------------------------------------------
1150 // Setup a device with the default settings
1151 //
1152 // ----------------------------------------------------------------------
1153
1154 bool videoInput::setupDevice(int deviceNumber){
1155     if(deviceNumber >= VI_MAX_CAMERAS || VDList[deviceNumber]->readyToCapture) return false;
1156
1157     if(setup(deviceNumber))return true;
1158     return false;
1159 }
1160
1161
1162 // ----------------------------------------------------------------------
1163 // Setup a device with the default size but specify input type
1164 //
1165 // ----------------------------------------------------------------------
1166
1167 bool videoInput::setupDevice(int deviceNumber, int _connection){
1168     if(deviceNumber >= VI_MAX_CAMERAS || VDList[deviceNumber]->readyToCapture) return false;
1169
1170     setPhyCon(deviceNumber, _connection);
1171     if(setup(deviceNumber))return true;
1172     return false;
1173 }
1174
1175
1176 // ----------------------------------------------------------------------
1177 // Setup a device with the default connection but specify size
1178 //
1179 // ----------------------------------------------------------------------
1180
1181 bool videoInput::setupDevice(int deviceNumber, int w, int h){
1182     if(deviceNumber >= VI_MAX_CAMERAS || VDList[deviceNumber]->readyToCapture) return false;
1183
1184     setAttemptCaptureSize(deviceNumber,w,h);
1185     if(setup(deviceNumber))return true;
1186     return false;
1187 }
1188
1189 // ----------------------------------------------------------------------
1190 // Setup a device with the default connection but specify size and image format
1191 //
1192 // Note:
1193 // Need a new name for this since signature clashes with ",int connection)"
1194 // ----------------------------------------------------------------------
1195
1196 bool videoInput::setupDeviceFourcc(int deviceNumber, int w, int h,int fourcc){
1197     if(deviceNumber >= VI_MAX_CAMERAS || VDList[deviceNumber]->readyToCapture) return false;
1198
1199     if ( fourcc != -1 ) {
1200         GUID *mediaType = getMediaSubtypeFromFourcc(fourcc);
1201         if ( mediaType ) {
1202             setAttemptCaptureSize(deviceNumber,w,h,*mediaType);
1203         }
1204     } else {
1205         setAttemptCaptureSize(deviceNumber,w,h);
1206     }
1207     if(setup(deviceNumber))return true;
1208     return false;
1209 }
1210
1211
1212 // ----------------------------------------------------------------------
1213 // Setup a device with specific size and connection
1214 //
1215 // ----------------------------------------------------------------------
1216
1217 bool videoInput::setupDevice(int deviceNumber, int w, int h, int _connection){
1218     if(deviceNumber >= VI_MAX_CAMERAS || VDList[deviceNumber]->readyToCapture) return false;
1219
1220     setAttemptCaptureSize(deviceNumber,w,h);
1221     setPhyCon(deviceNumber, _connection);
1222     if(setup(deviceNumber))return true;
1223     return false;
1224 }
1225
1226
1227 // ----------------------------------------------------------------------
1228 // Setup the default video format of the device
1229 // Must be called after setup!
1230 // See #define formats in header file (eg VI_NTSC_M )
1231 //
1232 // ----------------------------------------------------------------------
1233
1234 bool videoInput::setFormat(int deviceNumber, int format){
1235     if(deviceNumber >= VI_MAX_CAMERAS || !VDList[deviceNumber]->readyToCapture) return false;
1236
1237     bool returnVal = false;
1238
1239     if(format >= 0 && format < VI_NUM_FORMATS){
1240         VDList[deviceNumber]->formatType = formatTypes[format];
1241         VDList[deviceNumber]->specificFormat = true;
1242
1243         if(VDList[deviceNumber]->specificFormat){
1244
1245             HRESULT hr = getDevice(&VDList[deviceNumber]->pVideoInputFilter, deviceNumber, VDList[deviceNumber]->wDeviceName, VDList[deviceNumber]->nDeviceName);
1246             if(hr != S_OK){
1247                 return false;
1248             }
1249
1250             IAMAnalogVideoDecoder *pVideoDec = NULL;
1251                hr = VDList[deviceNumber]->pCaptureGraph->FindInterface(NULL, &MEDIATYPE_Video, VDList[deviceNumber]->pVideoInputFilter, IID_IAMAnalogVideoDecoder, (void **)&pVideoDec);
1252
1253
1254             //in case the settings window some how freed them first
1255             if(VDList[deviceNumber]->pVideoInputFilter)VDList[deviceNumber]->pVideoInputFilter->Release();
1256             if(VDList[deviceNumber]->pVideoInputFilter)VDList[deviceNumber]->pVideoInputFilter = NULL;
1257
1258             if(FAILED(hr)){
1259                 printf("SETUP: couldn't set requested format\n");
1260             }else{
1261                 long lValue = 0;
1262                 hr = pVideoDec->get_AvailableTVFormats(&lValue);
1263                 if( SUCCEEDED(hr) && (lValue & VDList[deviceNumber]->formatType) )
1264                    {
1265                        hr = pVideoDec->put_TVFormat(VDList[deviceNumber]->formatType);
1266                     if( FAILED(hr) ){
1267                         printf("SETUP: couldn't set requested format\n");
1268                     }else{
1269                         returnVal = true;
1270                     }
1271                    }
1272
1273                 pVideoDec->Release();
1274                 pVideoDec = NULL;
1275             }
1276         }
1277     }
1278
1279     return returnVal;
1280 }
1281
1282 // ----------------------------------------------------------------------
1283 // Our static function for returning device names - thanks Peter!
1284 // Must call listDevices first.
1285 //
1286 // ----------------------------------------------------------------------
1287 char videoInput::deviceNames[VI_MAX_CAMERAS][255]={{0}};
1288
1289 char * videoInput::getDeviceName(int deviceID){
1290     if( deviceID >= VI_MAX_CAMERAS ){
1291         return NULL;
1292     }
1293     return deviceNames[deviceID];
1294 }
1295
1296
1297 // ----------------------------------------------------------------------
1298 // Our static function for finding num devices available etc
1299 //
1300 // ----------------------------------------------------------------------
1301
1302 int videoInput::listDevices(bool silent){
1303
1304     //COM Library Intialization
1305     comInit();
1306
1307     if(!silent)printf("\nVIDEOINPUT SPY MODE!\n\n");
1308
1309
1310     ICreateDevEnum *pDevEnum = NULL;
1311     IEnumMoniker *pEnum = NULL;
1312     int deviceCounter = 0;
1313
1314     HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL,
1315         CLSCTX_INPROC_SERVER, IID_ICreateDevEnum,
1316         reinterpret_cast<void**>(&pDevEnum));
1317
1318
1319     if (SUCCEEDED(hr))
1320     {
1321         // Create an enumerator for the video capture category.
1322         hr = pDevEnum->CreateClassEnumerator(
1323             CLSID_VideoInputDeviceCategory,
1324             &pEnum, 0);
1325
1326        if(hr == S_OK){
1327
1328              if(!silent)printf("SETUP: Looking For Capture Devices\n");
1329             IMoniker *pMoniker = NULL;
1330
1331             while (pEnum->Next(1, &pMoniker, NULL) == S_OK){
1332
1333                 IPropertyBag *pPropBag;
1334                 hr = pMoniker->BindToStorage(0, 0, IID_IPropertyBag,
1335                     (void**)(&pPropBag));
1336
1337                 if (FAILED(hr)){
1338                     pMoniker->Release();
1339                     continue;  // Skip this one, maybe the next one will work.
1340                 }
1341
1342
1343                  // Find the description or friendly name.
1344                 VARIANT varName;
1345                 VariantInit(&varName);
1346                 hr = pPropBag->Read(L"Description", &varName, 0);
1347
1348                 if (FAILED(hr)) hr = pPropBag->Read(L"FriendlyName", &varName, 0);
1349
1350                 if (SUCCEEDED(hr)){
1351
1352                     hr = pPropBag->Read(L"FriendlyName", &varName, 0);
1353
1354                     int count = 0;
1355                     int maxLen = sizeof(deviceNames[0])/sizeof(deviceNames[0][0]) - 2;
1356                     while( varName.bstrVal[count] != 0x00 && count < maxLen) {
1357                         deviceNames[deviceCounter][count] = (char)varName.bstrVal[count];
1358                         count++;
1359                     }
1360                     deviceNames[deviceCounter][count] = 0;
1361
1362                     if(!silent)printf("SETUP: %i) %s \n",deviceCounter, deviceNames[deviceCounter]);
1363                 }
1364
1365                 pPropBag->Release();
1366                 pPropBag = NULL;
1367
1368                 pMoniker->Release();
1369                 pMoniker = NULL;
1370
1371                 deviceCounter++;
1372             }
1373
1374             pDevEnum->Release();
1375             pDevEnum = NULL;
1376
1377             pEnum->Release();
1378             pEnum = NULL;
1379         }
1380
1381          if(!silent)printf("SETUP: %i Device(s) found\n\n", deviceCounter);
1382     }
1383
1384     comUnInit();
1385
1386     return deviceCounter;
1387 }
1388
1389
1390 // ----------------------------------------------------------------------
1391 //
1392 //
1393 // ----------------------------------------------------------------------
1394
1395 int videoInput::getWidth(int id){
1396
1397     if(isDeviceSetup(id))
1398     {
1399         return VDList[id] ->width;
1400     }
1401
1402     return 0;
1403
1404 }
1405
1406
1407 // ----------------------------------------------------------------------
1408 //
1409 //
1410 // ----------------------------------------------------------------------
1411
1412 int videoInput::getHeight(int id){
1413
1414     if(isDeviceSetup(id))
1415     {
1416         return VDList[id] ->height;
1417     }
1418
1419     return 0;
1420
1421 }
1422
1423 // ----------------------------------------------------------------------
1424 //
1425 //
1426 // ----------------------------------------------------------------------
1427 int videoInput::getFourcc(int id){
1428
1429     if(isDeviceSetup(id))
1430     {
1431         return getFourccFromMediaSubtype(VDList[id]->videoType);
1432     }
1433
1434     return 0;
1435
1436 }
1437
1438 double videoInput::getFPS(int id){
1439
1440     if(isDeviceSetup(id))
1441     {
1442         double frameTime= VDList[id]->requestedFrameTime;
1443         if (frameTime>0) {
1444             return (10000000.0 / frameTime);
1445         }
1446     }
1447
1448     return 0;
1449
1450 }
1451
1452
1453 // ----------------------------------------------------------------------
1454 //
1455 //
1456 // ----------------------------------------------------------------------
1457
1458 int videoInput::getSize(int id){
1459
1460     if(isDeviceSetup(id))
1461     {
1462         return VDList[id] ->videoSize;
1463     }
1464
1465     return 0;
1466
1467 }
1468
1469
1470 // ----------------------------------------------------------------------
1471 // Uses a supplied buffer
1472 // ----------------------------------------------------------------------
1473
1474 bool videoInput::getPixels(int id, unsigned char * dstBuffer, bool flipRedAndBlue, bool flipImage){
1475
1476     bool success = false;
1477
1478     if(isDeviceSetup(id)){
1479         if(bCallback){
1480             //callback capture
1481
1482             DWORD result = WaitForSingleObject(VDList[id]->sgCallback->hEvent, 1000);
1483             if( result != WAIT_OBJECT_0) return false;
1484
1485             //double paranoia - mutexing with both event and critical section
1486             EnterCriticalSection(&VDList[id]->sgCallback->critSection);
1487
1488                 unsigned char * src = VDList[id]->sgCallback->pixels;
1489                 unsigned char * dst = dstBuffer;
1490                 int height             = VDList[id]->height;
1491                 int width              = VDList[id]->width;
1492
1493                 processPixels(src, dst, width, height, flipRedAndBlue, flipImage);
1494                 VDList[id]->sgCallback->newFrame = false;
1495
1496             LeaveCriticalSection(&VDList[id]->sgCallback->critSection);
1497
1498             ResetEvent(VDList[id]->sgCallback->hEvent);
1499
1500             success = true;
1501
1502         }
1503         else{
1504             //regular capture method
1505             long bufferSize = VDList[id]->videoSize;
1506             HRESULT hr = VDList[id]->pGrabber->GetCurrentBuffer(&bufferSize, (long *)VDList[id]->pBuffer);
1507             if(hr==S_OK){
1508                 int numBytes = VDList[id]->videoSize;
1509                 if (numBytes == bufferSize){
1510
1511                     unsigned char * src = (unsigned char * )VDList[id]->pBuffer;
1512                     unsigned char * dst = dstBuffer;
1513                     int height             = VDList[id]->height;
1514                     int width             = VDList[id]->width;
1515
1516                     processPixels(src, dst, width, height, flipRedAndBlue, flipImage);
1517                     success = true;
1518                 }else{
1519                     if(verbose)printf("ERROR: GetPixels() - bufferSizes do not match!\n");
1520                 }
1521             }else{
1522                 if(verbose)printf("ERROR: GetPixels() - Unable to grab frame for device %i\n", id);
1523             }
1524         }
1525     }
1526
1527     return success;
1528 }
1529
1530
1531 // ----------------------------------------------------------------------
1532 // Returns a buffer
1533 // ----------------------------------------------------------------------
1534 unsigned char * videoInput::getPixels(int id, bool flipRedAndBlue, bool flipImage){
1535
1536     if(isDeviceSetup(id)){
1537            getPixels(id, VDList[id]->pixels, flipRedAndBlue, flipImage);
1538     }
1539
1540     return VDList[id]->pixels;
1541 }
1542
1543
1544
1545 // ----------------------------------------------------------------------
1546 //
1547 //
1548 // ----------------------------------------------------------------------
1549 bool videoInput::isFrameNew(int id){
1550     if(!isDeviceSetup(id)) return false;
1551     if(!bCallback)return true;
1552
1553     bool result = false;
1554     bool freeze = false;
1555
1556     //again super paranoia!
1557     EnterCriticalSection(&VDList[id]->sgCallback->critSection);
1558         result = VDList[id]->sgCallback->newFrame;
1559
1560         //we need to give it some time at the begining to start up so lets check after 400 frames
1561         if(VDList[id]->nFramesRunning > 400 && VDList[id]->sgCallback->freezeCheck > VDList[id]->nFramesForReconnect ){
1562             freeze = true;
1563         }
1564
1565         //we increment the freezeCheck var here - the callback resets it to 1
1566         //so as long as the callback is running this var should never get too high.
1567         //if the callback is not running then this number will get high and trigger the freeze action below
1568         VDList[id]->sgCallback->freezeCheck++;
1569     LeaveCriticalSection(&VDList[id]->sgCallback->critSection);
1570
1571     VDList[id]->nFramesRunning++;
1572
1573     if(freeze && VDList[id]->autoReconnect){
1574         if(verbose)printf("ERROR: Device seems frozen - attempting to reconnect\n");
1575         if( !restartDevice(VDList[id]->myID) ){
1576             if(verbose)printf("ERROR: Unable to reconnect to device\n");
1577         }else{
1578             if(verbose)printf("SUCCESS: Able to reconnect to device\n");
1579         }
1580     }
1581
1582     return result;
1583 }
1584
1585
1586 // ----------------------------------------------------------------------
1587 //
1588 //
1589 // ----------------------------------------------------------------------
1590
1591 bool videoInput::isDeviceSetup(int id){
1592
1593     if(id<devicesFound && VDList[id]->readyToCapture)return true;
1594     else return false;
1595
1596 }
1597
1598
1599 // ----------------------------------------------------------------------
1600 // Gives us a little pop up window to adjust settings
1601 // We do this in a seperate thread now!
1602 // ----------------------------------------------------------------------
1603
1604
1605 void __cdecl videoInput::basicThread(void * objPtr){
1606
1607     //get a reference to the video device
1608     //not a copy as we need to free the filter
1609     videoDevice * vd = *( (videoDevice **)(objPtr) );
1610     ShowFilterPropertyPages(vd->pVideoInputFilter);
1611
1612
1613
1614     //now we free the filter and make sure it set to NULL
1615     if(vd->pVideoInputFilter)vd->pVideoInputFilter->Release();
1616     if(vd->pVideoInputFilter)vd->pVideoInputFilter = NULL;
1617
1618     return;
1619 }
1620
1621 void videoInput::showSettingsWindow(int id){
1622
1623     if(isDeviceSetup(id)){
1624         //HANDLE myTempThread;
1625
1626         //we reconnect to the device as we have freed our reference to it
1627         //why have we freed our reference? because there seemed to be an issue
1628         //with some mpeg devices if we didn't
1629         HRESULT hr = getDevice(&VDList[id]->pVideoInputFilter, id, VDList[id]->wDeviceName, VDList[id]->nDeviceName);
1630         if(hr == S_OK){
1631             //myTempThread = (HANDLE)
1632                 _beginthread(basicThread, 0, (void *)&VDList[id]);
1633         }
1634     }
1635 }
1636
1637
1638 // Set a video signal setting using IAMVideoProcAmp
1639 bool videoInput::getVideoSettingFilter(int deviceID, long Property, long &min, long &max, long &SteppingDelta, long &currentValue, long &flags, long &defaultValue){
1640     if( !isDeviceSetup(deviceID) )return false;
1641
1642     HRESULT hr;
1643     //bool isSuccessful = false;
1644
1645     videoDevice * VD = VDList[deviceID];
1646
1647     hr = getDevice(&VD->pVideoInputFilter, deviceID, VD->wDeviceName, VD->nDeviceName);
1648     if (FAILED(hr)){
1649         printf("setVideoSetting - getDevice Error\n");
1650         return false;
1651     }
1652
1653     IAMVideoProcAmp *pAMVideoProcAmp = NULL;
1654
1655     hr = VD->pVideoInputFilter->QueryInterface(IID_IAMVideoProcAmp, (void**)&pAMVideoProcAmp);
1656     if(FAILED(hr)){
1657         printf("setVideoSetting - QueryInterface Error\n");
1658         if(VD->pVideoInputFilter)VD->pVideoInputFilter->Release();
1659         if(VD->pVideoInputFilter)VD->pVideoInputFilter = NULL;
1660         return false;
1661     }
1662
1663     char propStr[16];
1664     getVideoPropertyAsString(Property,propStr);
1665
1666     if (verbose) printf("Setting video setting %s.\n", propStr);
1667
1668     pAMVideoProcAmp->GetRange(Property, &min, &max, &SteppingDelta, &defaultValue, &flags);
1669     if (verbose) printf("Range for video setting %s: Min:%ld Max:%ld SteppingDelta:%ld Default:%ld Flags:%ld\n", propStr, min, max, SteppingDelta, defaultValue, flags);
1670     pAMVideoProcAmp->Get(Property, &currentValue, &flags);
1671
1672     if(pAMVideoProcAmp)pAMVideoProcAmp->Release();
1673     if(VD->pVideoInputFilter)VD->pVideoInputFilter->Release();
1674     if(VD->pVideoInputFilter)VD->pVideoInputFilter = NULL;
1675
1676     return true;
1677
1678 }
1679
1680
1681 // Set a video signal setting using IAMVideoProcAmp
1682 bool videoInput::setVideoSettingFilterPct(int deviceID, long Property, float pctValue, long Flags){
1683     if( !isDeviceSetup(deviceID) )return false;
1684
1685     long min, max, currentValue, flags, defaultValue, stepAmnt;
1686
1687     if( !getVideoSettingFilter(deviceID, Property, min, max, stepAmnt, currentValue, flags, defaultValue) )return false;
1688
1689     if(pctValue > 1.0)pctValue = 1.0;
1690     else if(pctValue < 0)pctValue = 0.0;
1691
1692     float range = (float)max - (float)min;
1693     if(range <= 0)return false;
1694     if(stepAmnt == 0) return false;
1695
1696     long value     = (long)( (float)min + range * pctValue );
1697     long rasterValue = value;
1698
1699     //if the range is the stepAmnt then it is just a switch
1700     //so we either set the value to low or high
1701     if( range == stepAmnt ){
1702         if( pctValue < 0.5)rasterValue = min;
1703         else rasterValue = max;
1704     }else{
1705         //we need to rasterize the value to the stepping amnt
1706         long mod         = value % stepAmnt;
1707         float halfStep     = (float)stepAmnt * 0.5f;
1708         if( mod < halfStep ) rasterValue -= mod;
1709         else rasterValue += stepAmnt - mod;
1710         printf("RASTER - pctValue is %f - value is %li - step is %li - mod is %li - rasterValue is %li\n", pctValue, value, stepAmnt, mod, rasterValue);
1711     }
1712
1713     return setVideoSettingFilter(deviceID, Property, rasterValue, Flags, false);
1714 }
1715
1716
1717 // Set a video signal setting using IAMVideoProcAmp
1718 bool videoInput::setVideoSettingFilter(int deviceID, long Property, long lValue, long Flags, bool useDefaultValue){
1719     if( !isDeviceSetup(deviceID) )return false;
1720
1721     HRESULT hr;
1722     //bool isSuccessful = false;
1723
1724     char propStr[16];
1725     getVideoPropertyAsString(Property,propStr);
1726
1727     videoDevice * VD = VDList[deviceID];
1728
1729     hr = getDevice(&VD->pVideoInputFilter, deviceID, VD->wDeviceName, VD->nDeviceName);
1730     if (FAILED(hr)){
1731         printf("setVideoSetting - getDevice Error\n");
1732         return false;
1733     }
1734
1735     IAMVideoProcAmp *pAMVideoProcAmp = NULL;
1736
1737     hr = VD->pVideoInputFilter->QueryInterface(IID_IAMVideoProcAmp, (void**)&pAMVideoProcAmp);
1738     if(FAILED(hr)){
1739         printf("setVideoSetting - QueryInterface Error\n");
1740         if(VD->pVideoInputFilter)VD->pVideoInputFilter->Release();
1741         if(VD->pVideoInputFilter)VD->pVideoInputFilter = NULL;
1742         return false;
1743     }
1744
1745     if (verbose) printf("Setting video setting %s.\n", propStr);
1746     long CurrVal, Min, Max, SteppingDelta, Default, CapsFlags, AvailableCapsFlags = 0;
1747
1748
1749     pAMVideoProcAmp->GetRange(Property, &Min, &Max, &SteppingDelta, &Default, &AvailableCapsFlags);
1750     if (verbose) printf("Range for video setting %s: Min:%ld Max:%ld SteppingDelta:%ld Default:%ld Flags:%ld\n", propStr, Min, Max, SteppingDelta, Default, AvailableCapsFlags);
1751     pAMVideoProcAmp->Get(Property, &CurrVal, &CapsFlags);
1752
1753     if (verbose) printf("Current value: %ld Flags %ld (%s)\n", CurrVal, CapsFlags, (CapsFlags == 1 ? "Auto" : (CapsFlags == 2 ? "Manual" : "Unknown")));
1754
1755     if (useDefaultValue) {
1756         pAMVideoProcAmp->Set(Property, Default, VideoProcAmp_Flags_Auto);
1757     }
1758     else{
1759         // Perhaps add a check that lValue and Flags are within the range aquired from GetRange above
1760         pAMVideoProcAmp->Set(Property, lValue, Flags);
1761     }
1762
1763     if(pAMVideoProcAmp)pAMVideoProcAmp->Release();
1764     if(VD->pVideoInputFilter)VD->pVideoInputFilter->Release();
1765     if(VD->pVideoInputFilter)VD->pVideoInputFilter = NULL;
1766
1767     return true;
1768
1769 }
1770
1771
1772 bool videoInput::setVideoSettingCameraPct(int deviceID, long Property, float pctValue, long Flags){
1773     if( !isDeviceSetup(deviceID) )return false;
1774
1775     long min, max, currentValue, flags, defaultValue, stepAmnt;
1776
1777     if( !getVideoSettingCamera(deviceID, Property, min, max, stepAmnt, currentValue, flags, defaultValue) )return false;
1778
1779     if(pctValue > 1.0)pctValue = 1.0;
1780     else if(pctValue < 0)pctValue = 0.0;
1781
1782     float range = (float)max - (float)min;
1783     if(range <= 0)return false;
1784     if(stepAmnt == 0) return false;
1785
1786     long value     = (long)( (float)min + range * pctValue );
1787     long rasterValue = value;
1788
1789     //if the range is the stepAmnt then it is just a switch
1790     //so we either set the value to low or high
1791     if( range == stepAmnt ){
1792         if( pctValue < 0.5)rasterValue = min;
1793         else rasterValue = max;
1794     }else{
1795         //we need to rasterize the value to the stepping amnt
1796         long mod         = value % stepAmnt;
1797         float halfStep     = (float)stepAmnt * 0.5f;
1798         if( mod < halfStep ) rasterValue -= mod;
1799         else rasterValue += stepAmnt - mod;
1800         printf("RASTER - pctValue is %f - value is %li - step is %li - mod is %li - rasterValue is %li\n", pctValue, value, stepAmnt, mod, rasterValue);
1801     }
1802
1803     return setVideoSettingCamera(deviceID, Property, rasterValue, Flags, false);
1804 }
1805
1806
1807 bool videoInput::setVideoSettingCamera(int deviceID, long Property, long lValue, long Flags, bool useDefaultValue){
1808     IAMCameraControl *pIAMCameraControl;
1809     if(isDeviceSetup(deviceID))
1810     {
1811         HRESULT hr;
1812         hr = getDevice(&VDList[deviceID]->pVideoInputFilter, deviceID, VDList[deviceID]->wDeviceName, VDList[deviceID]->nDeviceName);
1813
1814         char propStr[16];
1815         getCameraPropertyAsString(Property,propStr);
1816
1817         if (verbose) printf("Setting video setting %s.\n", propStr);
1818         hr = VDList[deviceID]->pVideoInputFilter->QueryInterface(IID_IAMCameraControl, (void**)&pIAMCameraControl);
1819         if (FAILED(hr)) {
1820             printf("Error\n");
1821             return false;
1822         }
1823         else
1824         {
1825             long CurrVal, Min, Max, SteppingDelta, Default, CapsFlags, AvailableCapsFlags;
1826             pIAMCameraControl->GetRange(Property, &Min, &Max, &SteppingDelta, &Default, &AvailableCapsFlags);
1827             if (verbose) printf("Range for video setting %s: Min:%ld Max:%ld SteppingDelta:%ld Default:%ld Flags:%ld\n", propStr, Min, Max, SteppingDelta, Default, AvailableCapsFlags);
1828             pIAMCameraControl->Get(Property, &CurrVal, &CapsFlags);
1829             if (verbose) printf("Current value: %ld Flags %ld (%s)\n", CurrVal, CapsFlags, (CapsFlags == 1 ? "Auto" : (CapsFlags == 2 ? "Manual" : "Unknown")));
1830             if (useDefaultValue) {
1831                 pIAMCameraControl->Set(Property, Default, CameraControl_Flags_Auto);
1832             }
1833             else
1834             {
1835                 // Perhaps add a check that lValue and Flags are within the range aquired from GetRange above
1836                 pIAMCameraControl->Set(Property, lValue, Flags);
1837             }
1838             pIAMCameraControl->Release();
1839             return true;
1840         }
1841     }
1842     return false;
1843 }
1844
1845
1846
1847 bool videoInput::getVideoSettingCamera(int deviceID, long Property, long &min, long &max, long &SteppingDelta, long &currentValue, long &flags, long &defaultValue){
1848     if( !isDeviceSetup(deviceID) )return false;
1849
1850     HRESULT hr;
1851     //bool isSuccessful = false;
1852
1853     videoDevice * VD = VDList[deviceID];
1854
1855     hr = getDevice(&VD->pVideoInputFilter, deviceID, VD->wDeviceName, VD->nDeviceName);
1856     if (FAILED(hr)){
1857         printf("setVideoSetting - getDevice Error\n");
1858         return false;
1859     }
1860
1861     IAMCameraControl *pIAMCameraControl = NULL;
1862
1863     hr = VD->pVideoInputFilter->QueryInterface(IID_IAMCameraControl, (void**)&pIAMCameraControl);
1864     if(FAILED(hr)){
1865         printf("setVideoSetting - QueryInterface Error\n");
1866         if(VD->pVideoInputFilter)VD->pVideoInputFilter->Release();
1867         if(VD->pVideoInputFilter)VD->pVideoInputFilter = NULL;
1868         return false;
1869     }
1870
1871     char propStr[16];
1872     getCameraPropertyAsString(Property,propStr);
1873     if (verbose) printf("Setting video setting %s.\n", propStr);
1874
1875     pIAMCameraControl->GetRange(Property, &min, &max, &SteppingDelta, &defaultValue, &flags);
1876     if (verbose) printf("Range for video setting %s: Min:%ld Max:%ld SteppingDelta:%ld Default:%ld Flags:%ld\n", propStr, min, max, SteppingDelta, defaultValue, flags);
1877     pIAMCameraControl->Get(Property, &currentValue, &flags);
1878
1879     if(pIAMCameraControl)pIAMCameraControl->Release();
1880     if(VD->pVideoInputFilter)VD->pVideoInputFilter->Release();
1881     if(VD->pVideoInputFilter)VD->pVideoInputFilter = NULL;
1882
1883     return true;
1884
1885 }
1886
1887
1888 // ----------------------------------------------------------------------
1889 // Shutsdown the device, deletes the object and creates a new object
1890 // so it is ready to be setup again
1891 // ----------------------------------------------------------------------
1892
1893 void videoInput::stopDevice(int id){
1894     if(id < VI_MAX_CAMERAS)
1895     {
1896         delete VDList[id];
1897         VDList[id] = new videoDevice();
1898     }
1899
1900 }
1901
1902 // ----------------------------------------------------------------------
1903 // Restarts the device with the same settings it was using
1904 //
1905 // ----------------------------------------------------------------------
1906
1907 bool videoInput::restartDevice(int id){
1908     if(isDeviceSetup(id))
1909     {
1910         int conn         = VDList[id]->storeConn;
1911         int tmpW           = VDList[id]->width;
1912         int tmpH           = VDList[id]->height;
1913
1914         bool bFormat    = VDList[id]->specificFormat;
1915         long format     = VDList[id]->formatType;
1916
1917         int nReconnect    = VDList[id]->nFramesForReconnect;
1918         bool bReconnect = VDList[id]->autoReconnect;
1919
1920         unsigned long avgFrameTime = VDList[id]->requestedFrameTime;
1921
1922         stopDevice(id);
1923
1924         //set our fps if needed
1925         if( avgFrameTime != (unsigned long)-1){
1926             VDList[id]->requestedFrameTime = avgFrameTime;
1927         }
1928
1929         if( setupDevice(id, tmpW, tmpH, conn) ){
1930             //reapply the format - ntsc / pal etc
1931             if( bFormat ){
1932                 setFormat(id, format);
1933             }
1934             if( bReconnect ){
1935                 setAutoReconnectOnFreeze(id, true, nReconnect);
1936             }
1937             return true;
1938         }
1939     }
1940     return false;
1941 }
1942
1943 // ----------------------------------------------------------------------
1944 // Shuts down all devices, deletes objects and unitializes com if needed
1945 //
1946 // ----------------------------------------------------------------------
1947 videoInput::~videoInput(){
1948
1949     for(int i = 0; i < VI_MAX_CAMERAS; i++)
1950     {
1951         delete VDList[i];
1952     }
1953     //Unitialize com
1954     comUnInit();
1955 }
1956
1957
1958 //////////////////////////////  VIDEO INPUT  ////////////////////////////////
1959 ////////////////////////////  PRIVATE METHODS  //////////////////////////////
1960
1961 // ----------------------------------------------------------------------
1962 // We only should init com if it hasn't been done so by our apps thread
1963 // Use a static counter to keep track of other times it has been inited
1964 // (do we need to worry about multithreaded apps?)
1965 // ----------------------------------------------------------------------
1966
1967 bool videoInput::comInit(){
1968     /*HRESULT hr = NOERROR;
1969
1970     //no need for us to start com more than once
1971     if(comInitCount == 0 ){
1972
1973         // Initialize the COM library.
1974         //CoInitializeEx so videoInput can run in another thread
1975     #ifdef VI_COM_MULTI_THREADED
1976         hr = CoInitializeEx(NULL,COINIT_MULTITHREADED);
1977     #else
1978         hr = CoInitialize(NULL);
1979     #endif
1980         //this is the only case where there might be a problem
1981         //if another library has started com as single threaded
1982         //and we need it multi-threaded - send warning but don't fail
1983         if( hr == RPC_E_CHANGED_MODE){
1984              if(verbose)printf("SETUP - COM already setup - threaded VI might not be possible\n");
1985         }
1986     }
1987
1988     comInitCount++;*/
1989     return true;
1990 }
1991
1992
1993 // ----------------------------------------------------------------------
1994 // Same as above but to unitialize com, decreases counter and frees com
1995 // if no one else is using it
1996 // ----------------------------------------------------------------------
1997
1998 bool videoInput::comUnInit(){
1999     /*if(comInitCount > 0)comInitCount--;        //decrease the count of instances using com
2000
2001        if(comInitCount == 0){
2002            CoUninitialize();    //if there are no instances left - uninitialize com
2003         return true;
2004     }
2005
2006     return false;*/
2007     return true;
2008 }
2009
2010
2011 // ----------------------------------------------------------------------
2012 // This is the size we ask for - we might not get it though :)
2013 //
2014 // ----------------------------------------------------------------------
2015
2016 void videoInput::setAttemptCaptureSize(int id, int w, int h,GUID mediaType){
2017
2018     VDList[id]->tryWidth    = w;
2019     VDList[id]->tryHeight   = h;
2020     VDList[id]->tryDiffSize = true;
2021     VDList[id]->tryVideoType = mediaType;
2022
2023 }
2024
2025 // ----------------------------------------------------------------------
2026 // Set the connection type
2027 // (maybe move to private?)
2028 // ----------------------------------------------------------------------
2029
2030 void videoInput::setPhyCon(int id, int conn){
2031
2032     switch(conn){
2033
2034         case 0:
2035             VDList[id]->connection = PhysConn_Video_Composite;
2036             break;
2037         case 1:
2038             VDList[id]->connection = PhysConn_Video_SVideo;
2039             break;
2040         case 2:
2041             VDList[id]->connection = PhysConn_Video_Tuner;
2042             break;
2043         case 3:
2044             VDList[id]->connection = PhysConn_Video_USB;
2045             break;
2046         case 4:
2047             VDList[id]->connection = PhysConn_Video_1394;
2048             break;
2049         default:
2050             return; //if it is not these types don't set crossbar
2051         break;
2052     }
2053
2054     VDList[id]->storeConn    = conn;
2055     VDList[id]->useCrossbar    = true;
2056 }
2057
2058
2059 // ----------------------------------------------------------------------
2060 // Check that we are not trying to setup a non-existant device
2061 // Then start the graph building!
2062 // ----------------------------------------------------------------------
2063
2064 bool videoInput::setup(int deviceNumber){
2065     devicesFound = getDeviceCount();
2066
2067      if(deviceNumber>devicesFound-1)
2068     {
2069         if(verbose)printf("SETUP: device[%i] not found - you have %i devices available\n", deviceNumber, devicesFound);
2070         if(devicesFound>=0) if(verbose)printf("SETUP: this means that the last device you can use is device[%i] \n",  devicesFound-1);
2071         return false;
2072     }
2073
2074     if(VDList[deviceNumber]->readyToCapture)
2075     {
2076         if(verbose)printf("SETUP: can't setup, device %i is currently being used\n",VDList[deviceNumber]->myID);
2077         return false;
2078     }
2079
2080     HRESULT hr = start(deviceNumber, VDList[deviceNumber]);
2081     if(hr == S_OK)return true;
2082     else return false;
2083 }
2084
2085
2086 // ----------------------------------------------------------------------
2087 // Does both vertical buffer flipping and bgr to rgb swapping
2088 // You have any combination of those.
2089 // ----------------------------------------------------------------------
2090
2091 void videoInput::processPixels(unsigned char * src, unsigned char * dst, int width, int height, bool bRGB, bool bFlip){
2092
2093     int widthInBytes = width * 3;
2094     int numBytes = widthInBytes * height;
2095
2096     if(!bRGB){
2097
2098         //int x = 0;
2099         //int y = 0;
2100
2101         if(bFlip){
2102             for(int y = 0; y < height; y++){
2103                 memcpy(dst + (y * widthInBytes), src + ( (height -y -1) * widthInBytes), widthInBytes);
2104             }
2105
2106         }else{
2107             memcpy(dst, src, numBytes);
2108         }
2109     }else{
2110         if(bFlip){
2111
2112             int x = 0;
2113             int y = (height - 1) * widthInBytes;
2114             src += y;
2115
2116             for(int i = 0; i < numBytes; i+=3){
2117                 if(x >= width){
2118                     x = 0;
2119                     src -= widthInBytes*2;
2120                 }
2121
2122                 *dst = *(src+2);
2123                 dst++;
2124
2125                 *dst = *(src+1);
2126                 dst++;
2127
2128                 *dst = *src;
2129                 dst++;
2130
2131                 src+=3;
2132                 x++;
2133             }
2134         }
2135         else{
2136             for(int i = 0; i < numBytes; i+=3){
2137                 *dst = *(src+2);
2138                 dst++;
2139
2140                 *dst = *(src+1);
2141                 dst++;
2142
2143                 *dst = *src;
2144                 dst++;
2145
2146                 src+=3;
2147             }
2148         }
2149     }
2150 }
2151
2152
2153 //------------------------------------------------------------------------------------------
2154 void videoInput::getMediaSubtypeAsString(GUID type, char * typeAsString){
2155
2156     char tmpStr[8];
2157     if( type == MEDIASUBTYPE_RGB24)     sprintf(tmpStr, "RGB24");
2158     else if(type == MEDIASUBTYPE_RGB32) sprintf(tmpStr, "RGB32");
2159     else if(type == MEDIASUBTYPE_RGB555)sprintf(tmpStr, "RGB555");
2160     else if(type == MEDIASUBTYPE_RGB565)sprintf(tmpStr, "RGB565");
2161     else if(type == MEDIASUBTYPE_YUY2)  sprintf(tmpStr, "YUY2");
2162     else if(type == MEDIASUBTYPE_YVYU)  sprintf(tmpStr, "YVYU");
2163     else if(type == MEDIASUBTYPE_YUYV)  sprintf(tmpStr, "YUYV");
2164     else if(type == MEDIASUBTYPE_IYUV)  sprintf(tmpStr, "IYUV");
2165     else if(type == MEDIASUBTYPE_UYVY)  sprintf(tmpStr, "UYVY");
2166     else if(type == MEDIASUBTYPE_YV12)  sprintf(tmpStr, "YV12");
2167     else if(type == MEDIASUBTYPE_YVU9)  sprintf(tmpStr, "YVU9");
2168     else if(type == MEDIASUBTYPE_Y411)  sprintf(tmpStr, "Y411");
2169     else if(type == MEDIASUBTYPE_Y41P)  sprintf(tmpStr, "Y41P");
2170     else if(type == MEDIASUBTYPE_Y211)  sprintf(tmpStr, "Y211");
2171     else if(type == MEDIASUBTYPE_AYUV)  sprintf(tmpStr, "AYUV");
2172     else if(type == MEDIASUBTYPE_MJPG)  sprintf(tmpStr, "MJPG");
2173     else if(type == MEDIASUBTYPE_Y800)  sprintf(tmpStr, "Y800");
2174     else if(type == MEDIASUBTYPE_Y8)    sprintf(tmpStr, "Y8");
2175     else if(type == MEDIASUBTYPE_GREY)  sprintf(tmpStr, "GREY");
2176     else if(type == MEDIASUBTYPE_I420)  sprintf(tmpStr, "I420");
2177     else sprintf(tmpStr, "OTHER");
2178
2179     memcpy(typeAsString, tmpStr, sizeof(char)*8);
2180 }
2181
2182 int videoInput::getFourccFromMediaSubtype(GUID type) {
2183     return type.Data1;
2184 }
2185
2186 GUID *videoInput::getMediaSubtypeFromFourcc(int fourcc){
2187
2188     for (int i=0;i<VI_NUM_TYPES;i++) {
2189         if ( (unsigned long)(unsigned)fourcc == mediaSubtypes[i].Data1 ) {
2190             return &mediaSubtypes[i];
2191         }
2192     }
2193
2194     return NULL;
2195 }
2196
2197
2198 void videoInput::getVideoPropertyAsString(int prop, char * propertyAsString){
2199
2200     char tmpStr[16];
2201
2202     if ( prop==VideoProcAmp_Brightness) sprintf(tmpStr, "Brightness");
2203     else if ( prop==VideoProcAmp_Contrast) sprintf(tmpStr, "Contrast");
2204     else if ( prop==VideoProcAmp_Saturation) sprintf(tmpStr, "Saturation");
2205     else if ( prop==VideoProcAmp_Hue) sprintf(tmpStr, "Hue");
2206     else if ( prop==VideoProcAmp_Gain) sprintf(tmpStr, "Gain");
2207     else if ( prop==VideoProcAmp_Gamma) sprintf(tmpStr, "Gamma");
2208     else if ( prop==VideoProcAmp_ColorEnable) sprintf(tmpStr, "ColorEnable");
2209     else if ( prop==VideoProcAmp_Sharpness) sprintf(tmpStr, "Sharpness");
2210     else sprintf(tmpStr, "%u",prop);
2211
2212     memcpy(propertyAsString, tmpStr, sizeof(char)*16);
2213 }
2214
2215
2216 int videoInput::getVideoPropertyFromCV(int cv_property){
2217
2218     // see VideoProcAmpProperty in strmif.h
2219     switch (cv_property) {
2220         case CV_CAP_PROP_BRIGHTNESS:
2221             return VideoProcAmp_Brightness;
2222
2223         case CV_CAP_PROP_CONTRAST:
2224             return VideoProcAmp_Contrast;
2225
2226         case CV_CAP_PROP_HUE:
2227             return VideoProcAmp_Hue;
2228
2229         case CV_CAP_PROP_SATURATION:
2230             return VideoProcAmp_Saturation;
2231
2232         case CV_CAP_PROP_SHARPNESS:
2233             return VideoProcAmp_Sharpness;
2234
2235         case CV_CAP_PROP_GAMMA:
2236             return VideoProcAmp_Gamma;
2237
2238         case CV_CAP_PROP_MONOCROME:
2239             return VideoProcAmp_ColorEnable;
2240
2241         case CV_CAP_PROP_WHITE_BALANCE_BLUE_U:
2242             return VideoProcAmp_WhiteBalance;
2243
2244         case  CV_CAP_PROP_BACKLIGHT:
2245             return VideoProcAmp_BacklightCompensation;
2246
2247         case CV_CAP_PROP_GAIN:
2248             return VideoProcAmp_Gain;
2249     }
2250     return -1;
2251 }
2252
2253 int videoInput::getCameraPropertyFromCV(int cv_property){
2254
2255     // see CameraControlProperty in strmif.h
2256     switch (cv_property) {
2257         case CV_CAP_PROP_PAN:
2258             return CameraControl_Pan;
2259
2260         case CV_CAP_PROP_TILT:
2261             return CameraControl_Tilt;
2262
2263         case CV_CAP_PROP_ROLL:
2264             return CameraControl_Roll;
2265
2266         case CV_CAP_PROP_ZOOM:
2267             return CameraControl_Zoom;
2268
2269         case CV_CAP_PROP_EXPOSURE:
2270             return CameraControl_Exposure;
2271
2272         case CV_CAP_PROP_IRIS:
2273             return CameraControl_Iris;
2274
2275         case CV_CAP_PROP_FOCUS:
2276             return CameraControl_Focus;
2277     }
2278     return -1;
2279 }
2280
2281 void videoInput::getCameraPropertyAsString(int prop, char * propertyAsString){
2282
2283     char tmpStr[16];
2284
2285     if ( prop==CameraControl_Pan) sprintf(tmpStr, "Pan");
2286     else if ( prop==CameraControl_Tilt) sprintf(tmpStr, "Tilt");
2287     else if ( prop==CameraControl_Roll) sprintf(tmpStr, "Roll");
2288     else if ( prop==CameraControl_Zoom) sprintf(tmpStr, "Zoom");
2289     else if ( prop==CameraControl_Exposure) sprintf(tmpStr, "Exposure");
2290     else if ( prop==CameraControl_Iris) sprintf(tmpStr, "Iris");
2291     else if ( prop==CameraControl_Focus) sprintf(tmpStr, "Focus");
2292     else sprintf(tmpStr, "%u",prop);
2293
2294     memcpy(propertyAsString, tmpStr, sizeof(char)*16);
2295 }
2296
2297
2298 //-------------------------------------------------------------------------------------------
2299 static void findClosestSizeAndSubtype(videoDevice * VD, int widthIn, int heightIn, int &widthOut, int &heightOut, GUID & mediatypeOut){
2300     HRESULT hr;
2301
2302     //find perfect match or closest size
2303     int nearW                = 9999999;
2304     int nearH                = 9999999;
2305     //bool foundClosestMatch     = true;
2306
2307     int iCount = 0;
2308     int iSize = 0;
2309     hr = VD->streamConf->GetNumberOfCapabilities(&iCount, &iSize);
2310
2311     if (iSize == sizeof(VIDEO_STREAM_CONFIG_CAPS))
2312     {
2313         //For each format type RGB24 YUV2 etc
2314         for (int iFormat = 0; iFormat < iCount; iFormat++)
2315         {
2316             VIDEO_STREAM_CONFIG_CAPS scc;
2317             AM_MEDIA_TYPE *pmtConfig;
2318             hr =  VD->streamConf->GetStreamCaps(iFormat, &pmtConfig, (BYTE*)&scc);
2319
2320             if (SUCCEEDED(hr)){
2321
2322                 //his is how many diff sizes are available for the format
2323                 int stepX = scc.OutputGranularityX;
2324                 int stepY = scc.OutputGranularityY;
2325
2326                    int tempW = 999999;
2327                    int tempH = 999999;
2328
2329                    //Don't want to get stuck in a loop
2330                    if(stepX < 1 || stepY < 1) continue;
2331
2332                    //if(verbose)printf("min is %i %i max is %i %i - res is %i %i \n", scc.MinOutputSize.cx, scc.MinOutputSize.cy,  scc.MaxOutputSize.cx,  scc.MaxOutputSize.cy, stepX, stepY);
2333                    //if(verbose)printf("min frame duration is %i  max duration is %i\n", scc.MinFrameInterval, scc.MaxFrameInterval);
2334
2335                    bool exactMatch     = false;
2336                    bool exactMatchX    = false;
2337                 bool exactMatchY    = false;
2338
2339                 for(int x = scc.MinOutputSize.cx; x <= scc.MaxOutputSize.cx; x+= stepX){
2340                     //If we find an exact match
2341                     if( widthIn == x ){
2342                         exactMatchX = true;
2343                         tempW = x;
2344                     }
2345                     //Otherwise lets find the closest match based on width
2346                     else if( abs(widthIn-x) < abs(widthIn-tempW) ){
2347                         tempW = x;
2348                     }
2349                 }
2350
2351                 for(int y = scc.MinOutputSize.cy; y <= scc.MaxOutputSize.cy; y+= stepY){
2352                     //If we find an exact match
2353                     if( heightIn == y){
2354                         exactMatchY = true;
2355                         tempH = y;
2356                     }
2357                     //Otherwise lets find the closest match based on height
2358                     else if( abs(heightIn-y) < abs(heightIn-tempH) ){
2359                         tempH = y;
2360                     }
2361                 }
2362
2363                 //see if we have an exact match!
2364                 if(exactMatchX && exactMatchY){
2365                     //foundClosestMatch = false;
2366                     exactMatch = true;
2367
2368                     widthOut        = widthIn;
2369                     heightOut        = heightIn;
2370                     mediatypeOut    = pmtConfig->subtype;
2371                 }
2372
2373                   //otherwise lets see if this filters closest size is the closest
2374                   //available. the closest size is determined by the sum difference
2375                 //of the widths and heights
2376                   else if( abs(widthIn - tempW) + abs(heightIn - tempH)  < abs(widthIn - nearW) + abs(heightIn - nearH) )
2377                   {
2378                       nearW = tempW;
2379                       nearH = tempH;
2380
2381                     widthOut        = nearW;
2382                     heightOut        = nearH;
2383                     mediatypeOut    = pmtConfig->subtype;
2384                   }
2385
2386                 MyDeleteMediaType(pmtConfig);
2387
2388                 //If we have found an exact match no need to search anymore
2389                 if(exactMatch)break;
2390             }
2391          }
2392     }
2393
2394 }
2395
2396
2397 //---------------------------------------------------------------------------------------------------
2398 static bool setSizeAndSubtype(videoDevice * VD, int attemptWidth, int attemptHeight, GUID mediatype){
2399     VIDEOINFOHEADER *pVih =  reinterpret_cast<VIDEOINFOHEADER*>(VD->pAmMediaType->pbFormat);
2400
2401     //store current size
2402     //int tmpWidth  = HEADER(pVih)->biWidth;
2403     //int tmpHeight = HEADER(pVih)->biHeight;
2404     AM_MEDIA_TYPE * tmpType = NULL;
2405
2406     HRESULT    hr = VD->streamConf->GetFormat(&tmpType);
2407     if(hr != S_OK)return false;
2408
2409     //set new size:
2410     //width and height
2411     HEADER(pVih)->biWidth  = attemptWidth;
2412     HEADER(pVih)->biHeight = attemptHeight;
2413
2414     VD->pAmMediaType->formattype = FORMAT_VideoInfo;
2415     VD->pAmMediaType->majortype  = MEDIATYPE_Video;
2416     VD->pAmMediaType->subtype     = mediatype;
2417
2418     //buffer size
2419     VD->pAmMediaType->lSampleSize = attemptWidth*attemptHeight*3;
2420
2421     //set fps if requested
2422     if( VD->requestedFrameTime != -1){
2423         pVih->AvgTimePerFrame = VD->requestedFrameTime;
2424     }
2425
2426     //okay lets try new size
2427     hr = VD->streamConf->SetFormat(VD->pAmMediaType);
2428     if(hr == S_OK){
2429         if( tmpType != NULL )MyDeleteMediaType(tmpType);
2430         return true;
2431     }else{
2432         VD->streamConf->SetFormat(tmpType);
2433         if( tmpType != NULL )MyDeleteMediaType(tmpType);
2434     }
2435
2436     return false;
2437 }
2438
2439 // ----------------------------------------------------------------------
2440 // Where all the work happens!
2441 // Attempts to build a graph for the specified device
2442 // ----------------------------------------------------------------------
2443
2444 int videoInput::start(int deviceID, videoDevice *VD){
2445
2446     HRESULT hr             = NOERROR;
2447     VD->myID             = deviceID;
2448     VD->setupStarted    = true;
2449     CAPTURE_MODE           = PIN_CATEGORY_CAPTURE; //Don't worry - it ends up being preview (which is faster)
2450     callbackSetCount     = 1;  //make sure callback method is not changed after setup called
2451
2452     if(verbose)printf("SETUP: Setting up device %i\n",deviceID);
2453
2454     // CREATE THE GRAPH BUILDER //
2455     // Create the filter graph manager and query for interfaces.
2456     hr = CoCreateInstance(CLSID_CaptureGraphBuilder2, NULL, CLSCTX_INPROC_SERVER, IID_ICaptureGraphBuilder2, (void **)&VD->pCaptureGraph);
2457     if (FAILED(hr))    // FAILED is a macro that tests the return value
2458     {
2459         if(verbose)printf("ERROR - Could not create the Filter Graph Manager\n");
2460         return hr;
2461     }
2462
2463     //FITLER GRAPH MANAGER//
2464     // Create the Filter Graph Manager.
2465     hr = CoCreateInstance(CLSID_FilterGraph, 0, CLSCTX_INPROC_SERVER,IID_IGraphBuilder, (void**)&VD->pGraph);
2466     if (FAILED(hr))
2467     {
2468         if(verbose)printf("ERROR - Could not add the graph builder!\n");
2469         stopDevice(deviceID);
2470         return hr;
2471     }
2472
2473     //SET THE FILTERGRAPH//
2474     hr = VD->pCaptureGraph->SetFiltergraph(VD->pGraph);
2475     if (FAILED(hr))
2476     {
2477         if(verbose)printf("ERROR - Could not set filtergraph\n");
2478         stopDevice(deviceID);
2479         return hr;
2480     }
2481
2482     //MEDIA CONTROL (START/STOPS STREAM)//
2483     // Using QueryInterface on the graph builder,
2484     // Get the Media Control object.
2485     hr = VD->pGraph->QueryInterface(IID_IMediaControl, (void **)&VD->pControl);
2486     if (FAILED(hr))
2487     {
2488         if(verbose)printf("ERROR - Could not create the Media Control object\n");
2489            stopDevice(deviceID);
2490         return hr;
2491     }
2492
2493
2494     //FIND VIDEO DEVICE AND ADD TO GRAPH//
2495     //gets the device specified by the second argument.
2496     hr = getDevice(&VD->pVideoInputFilter, deviceID, VD->wDeviceName, VD->nDeviceName);
2497
2498     if (SUCCEEDED(hr)){
2499         if(verbose)printf("SETUP: %s\n", VD->nDeviceName);
2500         hr = VD->pGraph->AddFilter(VD->pVideoInputFilter, VD->wDeviceName);
2501     }else{
2502         if(verbose)printf("ERROR - Could not find specified video device\n");
2503         stopDevice(deviceID);
2504         return hr;
2505     }
2506
2507     //LOOK FOR PREVIEW PIN IF THERE IS NONE THEN WE USE CAPTURE PIN AND THEN SMART TEE TO PREVIEW
2508     IAMStreamConfig *streamConfTest = NULL;
2509     hr = VD->pCaptureGraph->FindInterface(&PIN_CATEGORY_PREVIEW, &MEDIATYPE_Video, VD->pVideoInputFilter, IID_IAMStreamConfig, (void **)&streamConfTest);
2510     if(FAILED(hr)){
2511         if(verbose)printf("SETUP: Couldn't find preview pin using SmartTee\n");
2512     }else{
2513          CAPTURE_MODE = PIN_CATEGORY_PREVIEW;
2514          streamConfTest->Release();
2515          streamConfTest = NULL;
2516     }
2517
2518     //CROSSBAR (SELECT PHYSICAL INPUT TYPE)//
2519     //my own function that checks to see if the device can support a crossbar and if so it routes it.
2520     //webcams tend not to have a crossbar so this function will also detect a webcams and not apply the crossbar
2521     if(VD->useCrossbar)
2522     {
2523         if(verbose)printf("SETUP: Checking crossbar\n");
2524         routeCrossbar(&VD->pCaptureGraph, &VD->pVideoInputFilter, VD->connection, CAPTURE_MODE);
2525     }
2526
2527
2528     //we do this because webcams don't have a preview mode
2529     hr = VD->pCaptureGraph->FindInterface(&CAPTURE_MODE, &MEDIATYPE_Video, VD->pVideoInputFilter, IID_IAMStreamConfig, (void **)&VD->streamConf);
2530     if(FAILED(hr)){
2531         if(verbose)printf("ERROR: Couldn't config the stream!\n");
2532         stopDevice(deviceID);
2533         return hr;
2534     }
2535
2536     //NOW LETS DEAL WITH GETTING THE RIGHT SIZE
2537     hr = VD->streamConf->GetFormat(&VD->pAmMediaType);
2538     if(FAILED(hr)){
2539         if(verbose)printf("ERROR: Couldn't getFormat for pAmMediaType!\n");
2540         stopDevice(deviceID);
2541         return hr;
2542     }
2543
2544     VIDEOINFOHEADER *pVih =  reinterpret_cast<VIDEOINFOHEADER*>(VD->pAmMediaType->pbFormat);
2545     int currentWidth    =  HEADER(pVih)->biWidth;
2546     int currentHeight    =  HEADER(pVih)->biHeight;
2547
2548     bool customSize = VD->tryDiffSize;
2549
2550     bool foundSize  = false;
2551
2552     if(customSize){
2553         if(verbose)    printf("SETUP: Default Format is set to %i by %i \n", currentWidth, currentHeight);
2554
2555         char guidStr[8];
2556             // try specified format and size
2557             getMediaSubtypeAsString(VD->tryVideoType, guidStr);
2558             if(verbose)printf("SETUP: trying specified format %s @ %i by %i\n", guidStr, VD->tryWidth, VD->tryHeight);
2559
2560             if( setSizeAndSubtype(VD, VD->tryWidth, VD->tryHeight, VD->tryVideoType) ){
2561                 VD->setSize(VD->tryWidth, VD->tryHeight);
2562                 foundSize = true;
2563             } else {
2564                 // try specified size with all formats
2565                 for(int i = 0; i < VI_NUM_TYPES; i++){
2566
2567                     getMediaSubtypeAsString(mediaSubtypes[i], guidStr);
2568
2569                     if(verbose)printf("SETUP: trying format %s @ %i by %i\n", guidStr, VD->tryWidth, VD->tryHeight);
2570                     if( setSizeAndSubtype(VD, VD->tryWidth, VD->tryHeight, mediaSubtypes[i]) ){
2571                         VD->setSize(VD->tryWidth, VD->tryHeight);
2572                         foundSize = true;
2573                         break;
2574                     }
2575                 }
2576             }
2577
2578
2579         //if we didn't find the requested size - lets try and find the closest matching size
2580         if( foundSize == false ){
2581             if( verbose )printf("SETUP: couldn't find requested size - searching for closest matching size\n");
2582
2583             int closestWidth        = -1;
2584             int closestHeight        = -1;
2585             GUID newMediaSubtype;
2586
2587             findClosestSizeAndSubtype(VD, VD->tryWidth, VD->tryHeight, closestWidth, closestHeight, newMediaSubtype);
2588
2589             if( closestWidth != -1 && closestHeight != -1){
2590                 getMediaSubtypeAsString(newMediaSubtype, guidStr);
2591
2592                 if(verbose)printf("SETUP: closest supported size is %s @ %i %i\n", guidStr, closestWidth, closestHeight);
2593                 if( setSizeAndSubtype(VD, closestWidth, closestHeight, newMediaSubtype) ){
2594                     VD->setSize(closestWidth, closestHeight);
2595                     foundSize = true;
2596                 }
2597             }
2598         }
2599     }
2600
2601     //if we didn't specify a custom size or if we did but couldn't find it lets setup with the default settings
2602     if(customSize == false || foundSize == false){
2603         if( VD->requestedFrameTime != -1 ){
2604             pVih->AvgTimePerFrame  = VD->requestedFrameTime;
2605             hr = VD->streamConf->SetFormat(VD->pAmMediaType);
2606         }
2607         VD->setSize(currentWidth, currentHeight);
2608     }
2609
2610     //SAMPLE GRABBER (ALLOWS US TO GRAB THE BUFFER)//
2611     // Create the Sample Grabber.
2612     hr = CoCreateInstance(CLSID_SampleGrabber, NULL, CLSCTX_INPROC_SERVER,IID_IBaseFilter, (void**)&VD->pGrabberF);
2613     if (FAILED(hr)){
2614         if(verbose)printf("Could not Create Sample Grabber - CoCreateInstance()\n");
2615         stopDevice(deviceID);
2616         return hr;
2617     }
2618
2619     hr = VD->pGraph->AddFilter(VD->pGrabberF, L"Sample Grabber");
2620     if (FAILED(hr)){
2621         if(verbose)printf("Could not add Sample Grabber - AddFilter()\n");
2622         stopDevice(deviceID);
2623         return hr;
2624     }
2625
2626     hr = VD->pGrabberF->QueryInterface(IID_ISampleGrabber, (void**)&VD->pGrabber);
2627     if (FAILED(hr)){
2628         if(verbose)printf("ERROR: Could not query SampleGrabber\n");
2629         stopDevice(deviceID);
2630         return hr;
2631     }
2632
2633
2634     //Set Params - One Shot should be false unless you want to capture just one buffer
2635     hr = VD->pGrabber->SetOneShot(FALSE);
2636     if(bCallback){
2637         hr = VD->pGrabber->SetBufferSamples(FALSE);
2638     }else{
2639         hr = VD->pGrabber->SetBufferSamples(TRUE);
2640     }
2641
2642     if(bCallback){
2643         //Tell the grabber to use our callback function - 0 is for SampleCB and 1 for BufferCB
2644         //We use SampleCB
2645         hr = VD->pGrabber->SetCallback(VD->sgCallback, 0);
2646         if (FAILED(hr)){
2647             if(verbose)printf("ERROR: problem setting callback\n");
2648             stopDevice(deviceID);
2649             return hr;
2650         }else{
2651             if(verbose)printf("SETUP: Capture callback set\n");
2652         }
2653     }
2654
2655     //MEDIA CONVERSION
2656     //Get video properties from the stream's mediatype and apply to the grabber (otherwise we don't get an RGB image)
2657     //zero the media type - lets try this :) - maybe this works?
2658     AM_MEDIA_TYPE mt;
2659     ZeroMemory(&mt,sizeof(AM_MEDIA_TYPE));
2660
2661     mt.majortype     = MEDIATYPE_Video;
2662     mt.subtype         = MEDIASUBTYPE_RGB24;
2663     mt.formattype     = FORMAT_VideoInfo;
2664
2665     //VD->pAmMediaType->subtype = VD->videoType;
2666     hr = VD->pGrabber->SetMediaType(&mt);
2667
2668     //lets try freeing our stream conf here too
2669     //this will fail if the device is already running
2670     if(VD->streamConf){
2671         VD->streamConf->Release();
2672         VD->streamConf = NULL;
2673     }else{
2674         if(verbose)printf("ERROR: connecting device - prehaps it is already being used?\n");
2675         stopDevice(deviceID);
2676         return S_FALSE;
2677     }
2678
2679
2680     //NULL RENDERER//
2681     //used to give the video stream somewhere to go to.
2682     hr = CoCreateInstance(CLSID_NullRenderer, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void**)(&VD->pDestFilter));
2683     if (FAILED(hr)){
2684         if(verbose)printf("ERROR: Could not create filter - NullRenderer\n");
2685         stopDevice(deviceID);
2686         return hr;
2687     }
2688
2689     hr = VD->pGraph->AddFilter(VD->pDestFilter, L"NullRenderer");
2690     if (FAILED(hr)){
2691         if(verbose)printf("ERROR: Could not add filter - NullRenderer\n");
2692         stopDevice(deviceID);
2693         return hr;
2694     }
2695
2696     //RENDER STREAM//
2697     //This is where the stream gets put together.
2698     hr = VD->pCaptureGraph->RenderStream(&PIN_CATEGORY_PREVIEW, &MEDIATYPE_Video, VD->pVideoInputFilter, VD->pGrabberF, VD->pDestFilter);
2699
2700     if (FAILED(hr)){
2701         if(verbose)printf("ERROR: Could not connect pins - RenderStream()\n");
2702         stopDevice(deviceID);
2703         return hr;
2704     }
2705
2706
2707     //EXP - lets try setting the sync source to null - and make it run as fast as possible
2708     {
2709         IMediaFilter *pMediaFilter = 0;
2710         hr = VD->pGraph->QueryInterface(IID_IMediaFilter, (void**)&pMediaFilter);
2711         if (FAILED(hr)){
2712             if(verbose)printf("ERROR: Could not get IID_IMediaFilter interface\n");
2713         }else{
2714             pMediaFilter->SetSyncSource(NULL);
2715             pMediaFilter->Release();
2716         }
2717     }
2718
2719
2720     //LETS RUN THE STREAM!
2721     hr = VD->pControl->Run();
2722
2723     if (FAILED(hr)){
2724          if(verbose)printf("ERROR: Could not start graph\n");
2725          stopDevice(deviceID);
2726          return hr;
2727     }
2728
2729
2730     //MAKE SURE THE DEVICE IS SENDING VIDEO BEFORE WE FINISH
2731     if(!bCallback){
2732
2733         long bufferSize = VD->videoSize;
2734
2735         while( hr != S_OK){
2736             hr = VD->pGrabber->GetCurrentBuffer(&bufferSize, (long *)VD->pBuffer);
2737             Sleep(10);
2738         }
2739
2740     }
2741
2742     if(verbose)printf("SETUP: Device is setup and ready to capture.\n\n");
2743     VD->readyToCapture = true;
2744
2745     //Release filters - seen someone else do this
2746     //looks like it solved the freezes
2747
2748     //if we release this then we don't have access to the settings
2749     //we release our video input filter but then reconnect with it
2750     //each time we need to use it
2751     VD->pVideoInputFilter->Release();
2752     VD->pVideoInputFilter = NULL;
2753
2754     VD->pGrabberF->Release();
2755     VD->pGrabberF = NULL;
2756
2757     VD->pDestFilter->Release();
2758     VD->pDestFilter = NULL;
2759
2760     return S_OK;
2761 }
2762
2763
2764 // ----------------------------------------------------------------------
2765 // Returns number of good devices
2766 //
2767 // ----------------------------------------------------------------------
2768
2769 int videoInput::getDeviceCount(){
2770
2771
2772     ICreateDevEnum *pDevEnum = NULL;
2773     IEnumMoniker *pEnum = NULL;
2774     int deviceCounter = 0;
2775
2776     HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL,
2777         CLSCTX_INPROC_SERVER, IID_ICreateDevEnum,
2778         reinterpret_cast<void**>(&pDevEnum));
2779
2780
2781     if (SUCCEEDED(hr))
2782     {
2783         // Create an enumerator for the video capture category.
2784         hr = pDevEnum->CreateClassEnumerator(
2785             CLSID_VideoInputDeviceCategory,
2786             &pEnum, 0);
2787
2788        if(hr == S_OK){
2789             IMoniker *pMoniker = NULL;
2790             while (pEnum->Next(1, &pMoniker, NULL) == S_OK){
2791
2792                 IPropertyBag *pPropBag;
2793                 hr = pMoniker->BindToStorage(0, 0, IID_IPropertyBag,
2794                     (void**)(&pPropBag));
2795
2796                 if (FAILED(hr)){
2797                     pMoniker->Release();
2798                     continue;  // Skip this one, maybe the next one will work.
2799                 }
2800
2801                 pPropBag->Release();
2802                 pPropBag = NULL;
2803
2804                 pMoniker->Release();
2805                 pMoniker = NULL;
2806
2807                 deviceCounter++;
2808             }
2809
2810             pEnum->Release();
2811             pEnum = NULL;
2812         }
2813
2814         pDevEnum->Release();
2815         pDevEnum = NULL;
2816     }
2817     return deviceCounter;
2818 }
2819
2820
2821 // ----------------------------------------------------------------------
2822 // Do we need this?
2823 //
2824 // Enumerate all of the video input devices
2825 // Return the filter with a matching friendly name
2826 // ----------------------------------------------------------------------
2827
2828 HRESULT videoInput::getDevice(IBaseFilter** gottaFilter, int deviceId, WCHAR * wDeviceName, char * nDeviceName){
2829     BOOL done = false;
2830     int deviceCounter = 0;
2831
2832     // Create the System Device Enumerator.
2833     ICreateDevEnum *pSysDevEnum = NULL;
2834     HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, IID_ICreateDevEnum, (void **)&pSysDevEnum);
2835     if (FAILED(hr))
2836     {
2837         return hr;
2838     }
2839
2840     // Obtain a class enumerator for the video input category.
2841     IEnumMoniker *pEnumCat = NULL;
2842     hr = pSysDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &pEnumCat, 0);
2843
2844     if (hr == S_OK)
2845     {
2846         // Enumerate the monikers.
2847         IMoniker *pMoniker = NULL;
2848         ULONG cFetched;
2849         while ((pEnumCat->Next(1, &pMoniker, &cFetched) == S_OK) && (!done))
2850         {
2851             if(deviceCounter == deviceId)
2852             {
2853                 // Bind the first moniker to an object
2854                 IPropertyBag *pPropBag;
2855                 hr = pMoniker->BindToStorage(0, 0, IID_IPropertyBag, (void **)&pPropBag);
2856                 if (SUCCEEDED(hr))
2857                 {
2858                     // To retrieve the filter's friendly name, do the following:
2859                     VARIANT varName;
2860                     VariantInit(&varName);
2861                     hr = pPropBag->Read(L"FriendlyName", &varName, 0);
2862                     if (SUCCEEDED(hr))
2863                     {
2864
2865                         //copy the name to nDeviceName & wDeviceName
2866                         int count = 0;
2867                         while( varName.bstrVal[count] != 0x00 ) {
2868                                wDeviceName[count] = varName.bstrVal[count];
2869                                nDeviceName[count] = (char)varName.bstrVal[count];
2870                                count++;
2871                          }
2872
2873                         // We found it, so send it back to the caller
2874                         hr = pMoniker->BindToObject(NULL, NULL, IID_IBaseFilter, (void**)gottaFilter);
2875                         done = true;
2876                     }
2877                     VariantClear(&varName);
2878                     pPropBag->Release();
2879                     pPropBag = NULL;
2880                     pMoniker->Release();
2881                     pMoniker = NULL;
2882                 }
2883             }
2884             deviceCounter++;
2885         }
2886         pEnumCat->Release();
2887         pEnumCat = NULL;
2888     }
2889     pSysDevEnum->Release();
2890     pSysDevEnum = NULL;
2891
2892     if (done) {
2893         return hr;    // found it, return native error
2894     } else {
2895         return VFW_E_NOT_FOUND;    // didn't find it error
2896     }
2897 }
2898
2899
2900 // ----------------------------------------------------------------------
2901 // Show the property pages for a filter
2902 // This is stolen from the DX9 SDK
2903 // ----------------------------------------------------------------------
2904
2905 HRESULT videoInput::ShowFilterPropertyPages(IBaseFilter *pFilter){
2906
2907     ISpecifyPropertyPages *pProp;
2908
2909     HRESULT hr = pFilter->QueryInterface(IID_ISpecifyPropertyPages, (void **)&pProp);
2910     if (SUCCEEDED(hr))
2911     {
2912         // Get the filter's name and IUnknown pointer.
2913         FILTER_INFO FilterInfo;
2914         hr = pFilter->QueryFilterInfo(&FilterInfo);
2915         IUnknown *pFilterUnk;
2916         pFilter->QueryInterface(IID_IUnknown, (void **)&pFilterUnk);
2917
2918         // Show the page.
2919         CAUUID caGUID;
2920         pProp->GetPages(&caGUID);
2921         pProp->Release();
2922         OleCreatePropertyFrame(
2923             NULL,                   // Parent window
2924             0, 0,                   // Reserved
2925             FilterInfo.achName,     // Caption for the dialog box
2926             1,                      // Number of objects (just the filter)
2927             &pFilterUnk,            // Array of object pointers.
2928             caGUID.cElems,          // Number of property pages
2929             caGUID.pElems,          // Array of property page CLSIDs
2930             0,                      // Locale identifier
2931             0, NULL                 // Reserved
2932         );
2933
2934         // Clean up.
2935         if(pFilterUnk)pFilterUnk->Release();
2936         if(FilterInfo.pGraph)FilterInfo.pGraph->Release();
2937         CoTaskMemFree(caGUID.pElems);
2938     }
2939     return hr;
2940 }
2941
2942 HRESULT videoInput::ShowStreamPropertyPages(IAMStreamConfig  * /*pStream*/){
2943
2944     HRESULT hr             = NOERROR;
2945     return hr;
2946 }
2947
2948 // ----------------------------------------------------------------------
2949 // This code was also brazenly stolen from the DX9 SDK
2950 // Pass it a file name in wszPath, and it will save the filter graph to that file.
2951 // ----------------------------------------------------------------------
2952
2953 HRESULT videoInput::SaveGraphFile(IGraphBuilder *pGraph, WCHAR *wszPath) {
2954     const WCHAR wszStreamName[] = L"ActiveMovieGraph";
2955     HRESULT hr;
2956     IStorage *pStorage = NULL;
2957
2958     // First, create a document file which will hold the GRF file
2959     hr = StgCreateDocfile(
2960         wszPath,
2961         STGM_CREATE | STGM_TRANSACTED | STGM_READWRITE | STGM_SHARE_EXCLUSIVE,
2962         0, &pStorage);
2963     if(FAILED(hr))
2964     {
2965         return hr;
2966     }
2967
2968     // Next, create a stream to store.
2969     IStream *pStream;
2970     hr = pStorage->CreateStream(
2971         wszStreamName,
2972         STGM_WRITE | STGM_CREATE | STGM_SHARE_EXCLUSIVE,
2973         0, 0, &pStream);
2974     if (FAILED(hr))
2975     {
2976         pStorage->Release();
2977         return hr;
2978     }
2979
2980     // The IPersistStream converts a stream into a persistent object.
2981     IPersistStream *pPersist = NULL;
2982     pGraph->QueryInterface(IID_IPersistStream, reinterpret_cast<void**>(&pPersist));
2983     hr = pPersist->Save(pStream, TRUE);
2984     pStream->Release();
2985     pPersist->Release();
2986     if (SUCCEEDED(hr))
2987     {
2988         hr = pStorage->Commit(STGC_DEFAULT);
2989     }
2990     pStorage->Release();
2991     return hr;
2992 }
2993
2994
2995 // ----------------------------------------------------------------------
2996 // For changing the input types
2997 //
2998 // ----------------------------------------------------------------------
2999
3000 HRESULT videoInput::routeCrossbar(ICaptureGraphBuilder2 **ppBuild, IBaseFilter **pVidInFilter, int conType, GUID captureMode){
3001
3002     //create local ICaptureGraphBuilder2
3003     ICaptureGraphBuilder2 *pBuild = NULL;
3004      pBuild = *ppBuild;
3005
3006      //create local IBaseFilter
3007      IBaseFilter *pVidFilter = NULL;
3008      pVidFilter = * pVidInFilter;
3009
3010     // Search upstream for a crossbar.
3011     IAMCrossbar *pXBar1 = NULL;
3012     HRESULT hr = pBuild->FindInterface(&LOOK_UPSTREAM_ONLY, NULL, pVidFilter,
3013             IID_IAMCrossbar, (void**)&pXBar1);
3014     if (SUCCEEDED(hr))
3015     {
3016
3017         bool foundDevice = false;
3018
3019         if(verbose)printf("SETUP: You are not a webcam! Setting Crossbar\n");
3020         pXBar1->Release();
3021
3022         IAMCrossbar *Crossbar;
3023         hr = pBuild->FindInterface(&captureMode, &MEDIATYPE_Interleaved, pVidFilter, IID_IAMCrossbar, (void **)&Crossbar);
3024
3025         if(hr != NOERROR){
3026             hr = pBuild->FindInterface(&captureMode, &MEDIATYPE_Video, pVidFilter, IID_IAMCrossbar, (void **)&Crossbar);
3027         }
3028
3029         LONG lInpin, lOutpin;
3030         hr = Crossbar->get_PinCounts(&lOutpin , &lInpin);
3031
3032         BOOL iPin=TRUE; LONG pIndex=0 , pRIndex=0 , pType=0;
3033
3034         while( pIndex < lInpin)
3035         {
3036             hr = Crossbar->get_CrossbarPinInfo( iPin , pIndex , &pRIndex , &pType);
3037
3038             if( pType == conType){
3039                     if(verbose)printf("SETUP: Found Physical Interface");
3040
3041                     switch(conType){
3042
3043                         case PhysConn_Video_Composite:
3044                             if(verbose)printf(" - Composite\n");
3045                             break;
3046                         case PhysConn_Video_SVideo:
3047                             if(verbose)printf(" - S-Video\n");
3048                             break;
3049                         case PhysConn_Video_Tuner:
3050                             if(verbose)printf(" - Tuner\n");
3051                             break;
3052                         case PhysConn_Video_USB:
3053                              if(verbose)printf(" - USB\n");
3054                             break;
3055                         case PhysConn_Video_1394:
3056                             if(verbose)printf(" - Firewire\n");
3057                             break;
3058                     }
3059
3060                 foundDevice = true;
3061                 break;
3062             }
3063             pIndex++;
3064
3065         }
3066
3067         if(foundDevice){
3068             BOOL OPin=FALSE; LONG pOIndex=0 , pORIndex=0 , pOType=0;
3069             while( pOIndex < lOutpin)
3070             {
3071                 hr = Crossbar->get_CrossbarPinInfo( OPin , pOIndex , &pORIndex , &pOType);
3072                 if( pOType == PhysConn_Video_VideoDecoder)
3073                     break;
3074             }
3075             Crossbar->Route(pOIndex,pIndex);
3076         }else{
3077             if(verbose) printf("SETUP: Didn't find specified Physical Connection type. Using Defualt. \n");
3078         }
3079
3080         //we only free the crossbar when we close or restart the device
3081         //we were getting a crash otherwise
3082         //if(Crossbar)Crossbar->Release();
3083         //if(Crossbar)Crossbar = NULL;
3084
3085         if(pXBar1)pXBar1->Release();
3086         if(pXBar1)pXBar1 = NULL;
3087
3088     }else{
3089         if(verbose) printf("SETUP: You are a webcam or snazzy firewire cam! No Crossbar needed\n");
3090         return hr;
3091     }
3092
3093     return hr;
3094 }
3095
3096 struct SuppressVideoInputMessages
3097 {
3098     SuppressVideoInputMessages() { videoInput::setVerbose(false); }
3099 };
3100
3101 static SuppressVideoInputMessages do_it;
3102
3103 namespace cv
3104 {
3105 videoInput VideoCapture_DShow::g_VI;
3106
3107 VideoCapture_DShow::VideoCapture_DShow(int index)
3108     : m_index(-1)
3109     , m_width(-1)
3110     , m_height(-1)
3111     , m_fourcc(-1)
3112     , m_widthSet(-1)
3113     , m_heightSet(-1)
3114 {
3115     CoInitialize(0);
3116     open(index);
3117 }
3118 VideoCapture_DShow::~VideoCapture_DShow()
3119 {
3120     close();
3121     CoUninitialize();
3122 }
3123
3124 double VideoCapture_DShow::getProperty(int propIdx)
3125 {
3126
3127     long min_value, max_value, stepping_delta, current_value, flags, defaultValue;
3128
3129     switch (propIdx)
3130     {
3131     // image format properties
3132     case CV_CAP_PROP_FRAME_WIDTH:
3133         return g_VI.getWidth(m_index);
3134     case CV_CAP_PROP_FRAME_HEIGHT:
3135         return g_VI.getHeight(m_index);
3136     case CV_CAP_PROP_FOURCC:
3137         return g_VI.getFourcc(m_index);
3138     case CV_CAP_PROP_FPS:
3139         return g_VI.getFPS(m_index);
3140
3141     // video filter properties
3142     case CV_CAP_PROP_BRIGHTNESS:
3143     case CV_CAP_PROP_CONTRAST:
3144     case CV_CAP_PROP_HUE:
3145     case CV_CAP_PROP_SATURATION:
3146     case CV_CAP_PROP_SHARPNESS:
3147     case CV_CAP_PROP_GAMMA:
3148     case CV_CAP_PROP_MONOCROME:
3149     case CV_CAP_PROP_WHITE_BALANCE_BLUE_U:
3150     case CV_CAP_PROP_BACKLIGHT:
3151     case CV_CAP_PROP_GAIN:
3152         if (g_VI.getVideoSettingFilter(m_index, g_VI.getVideoPropertyFromCV(propIdx), min_value, max_value, stepping_delta, current_value, flags, defaultValue))
3153             return (double)current_value;
3154
3155     // camera properties
3156     case CV_CAP_PROP_PAN:
3157     case CV_CAP_PROP_TILT:
3158     case CV_CAP_PROP_ROLL:
3159     case CV_CAP_PROP_ZOOM:
3160     case CV_CAP_PROP_EXPOSURE:
3161     case CV_CAP_PROP_IRIS:
3162     case CV_CAP_PROP_FOCUS:
3163         if (g_VI.getVideoSettingCamera(m_index, g_VI.getCameraPropertyFromCV(propIdx), min_value, max_value, stepping_delta, current_value, flags, defaultValue))
3164             return (double)current_value;
3165     }
3166
3167     // unknown parameter or value not available
3168     return -1;
3169 }
3170 bool VideoCapture_DShow::setProperty(int propIdx, double propVal)
3171 {
3172     // image capture properties
3173     bool handled = false;
3174     switch (propIdx)
3175     {
3176     case CV_CAP_PROP_FRAME_WIDTH:
3177         m_width = cvRound(propVal);
3178         handled = true;
3179         break;
3180
3181     case CV_CAP_PROP_FRAME_HEIGHT:
3182         m_height = cvRound(propVal);
3183         handled = true;
3184         break;
3185
3186     case CV_CAP_PROP_FOURCC:
3187         m_fourcc = (int)(unsigned long)(propVal);
3188         if (-1 == m_fourcc)
3189         {
3190             // following cvCreateVideo usage will pop up caprturepindialog here if fourcc=-1
3191             // TODO - how to create a capture pin dialog
3192         }
3193         handled = true;
3194         break;
3195
3196     case CV_CAP_PROP_FPS:
3197         int fps = cvRound(propVal);
3198         if (fps != g_VI.getFPS(m_index))
3199         {
3200             g_VI.stopDevice(m_index);
3201             g_VI.setIdealFramerate(m_index, fps);
3202             if (m_widthSet > 0 && m_heightSet > 0)
3203                 g_VI.setupDevice(m_index, m_widthSet, m_heightSet);
3204             else
3205                 g_VI.setupDevice(m_index);
3206         }
3207         return g_VI.isDeviceSetup(m_index);
3208     }
3209
3210     if (handled)
3211     {
3212         // a stream setting
3213         if (m_width > 0 && m_height > 0)
3214         {
3215             if (m_width != g_VI.getWidth(m_index) || m_height != g_VI.getHeight(m_index) )//|| fourcc != VI.getFourcc(index) )
3216             {
3217                 int fps = static_cast<int>(g_VI.getFPS(m_index));
3218                 g_VI.stopDevice(m_index);
3219                 g_VI.setIdealFramerate(m_index, fps);
3220                 g_VI.setupDeviceFourcc(m_index, m_width, m_height, m_fourcc);
3221             }
3222
3223             bool success = g_VI.isDeviceSetup(m_index);
3224             if (success)
3225             {
3226                 m_widthSet = m_width;
3227                 m_heightSet = m_height;
3228                 m_width = m_height = m_fourcc = -1;
3229             }
3230             return success;
3231         }
3232         return true;
3233     }
3234
3235     // show video/camera filter dialog
3236     if (propIdx == CV_CAP_PROP_SETTINGS )
3237     {
3238         g_VI.showSettingsWindow(m_index);
3239         return true;
3240     }
3241
3242     //video Filter properties
3243     switch (propIdx)
3244     {
3245     case CV_CAP_PROP_BRIGHTNESS:
3246     case CV_CAP_PROP_CONTRAST:
3247     case CV_CAP_PROP_HUE:
3248     case CV_CAP_PROP_SATURATION:
3249     case CV_CAP_PROP_SHARPNESS:
3250     case CV_CAP_PROP_GAMMA:
3251     case CV_CAP_PROP_MONOCROME:
3252     case CV_CAP_PROP_WHITE_BALANCE_BLUE_U:
3253     case CV_CAP_PROP_BACKLIGHT:
3254     case CV_CAP_PROP_GAIN:
3255         return g_VI.setVideoSettingFilter(m_index, g_VI.getVideoPropertyFromCV(propIdx), (long)propVal);
3256     }
3257
3258     //camera properties
3259     switch (propIdx)
3260     {
3261     case CV_CAP_PROP_PAN:
3262     case CV_CAP_PROP_TILT:
3263     case CV_CAP_PROP_ROLL:
3264     case CV_CAP_PROP_ZOOM:
3265     case CV_CAP_PROP_EXPOSURE:
3266     case CV_CAP_PROP_IRIS:
3267     case CV_CAP_PROP_FOCUS:
3268         return g_VI.setVideoSettingCamera(m_index, g_VI.getCameraPropertyFromCV(propIdx), (long)propVal);
3269     }
3270
3271     return false;
3272 }
3273
3274 bool VideoCapture_DShow::grabFrame()
3275 {
3276     return true;
3277 }
3278 bool VideoCapture_DShow::retrieveFrame(int, OutputArray frame)
3279 {
3280     frame.create(Size(g_VI.getWidth(m_index), g_VI.getHeight(m_index)), CV_8UC3);
3281     cv::Mat mat = frame.getMat();
3282     return g_VI.getPixels(m_index, mat.ptr(), false, true );
3283 }
3284 int VideoCapture_DShow::getCaptureDomain()
3285 {
3286     return CV_CAP_DSHOW;
3287 }
3288 bool VideoCapture_DShow::isOpened() const
3289 {
3290     return (-1 != m_index);
3291 }
3292
3293 void VideoCapture_DShow::open(int index)
3294 {
3295     close();
3296     int devices = g_VI.listDevices(true);
3297     if (0 == devices)
3298         return;
3299     if (index < 0 || index > devices-1)
3300         return;
3301     g_VI.setupDevice(index);
3302     if (!g_VI.isDeviceSetup(index))
3303         return;
3304     m_index = index;
3305 }
3306
3307 void VideoCapture_DShow::close()
3308 {
3309     if (m_index >= 0)
3310     {
3311         g_VI.stopDevice(m_index);
3312         m_index = -1;
3313     }
3314     m_widthSet = m_heightSet = m_width = m_height = -1;
3315 }
3316
3317 }
3318
3319 #endif