| 1 | // This is a DirectShow interface. But maybe you'll find that it's easier to |
|---|
| 2 | // access the camera directly ;) |
|---|
| 3 | |
|---|
| 4 | // http://www.codeguru.com/cpp/g-m/multimedia/video/article.php/c9551/ |
|---|
| 5 | // http://lists.mplayerhq.hu/pipermail/ffmpeg-devel/2007-April/027965.html |
|---|
| 6 | // http://msdn2.microsoft.com/en-us/library/ms787594.aspx |
|---|
| 7 | // http://msdn2.microsoft.com/en-us/library/ms787867.aspx |
|---|
| 8 | // NullRenderer wih reference clock set to NULL |
|---|
| 9 | // http://www.videolan.org/ |
|---|
| 10 | // http://git.videolan.org/gitweb.cgi?p=vlc.git;f=modules/access/dshow;hb=0.8.6 |
|---|
| 11 | |
|---|
| 12 | // #include <wtypes.h> |
|---|
| 13 | // #include <unknwn.h> |
|---|
| 14 | // #include <ole2.h> |
|---|
| 15 | // #include <limits.h> |
|---|
| 16 | // #include <dshow.h> |
|---|
| 17 | |
|---|
| 18 | #include <iostream> |
|---|
| 19 | #include <iomanip> |
|---|
| 20 | #include <fstream> |
|---|
| 21 | #include <windows.h> |
|---|
| 22 | #include <winnls.h> |
|---|
| 23 | #include <errors.h> |
|---|
| 24 | #include <initguid.h> |
|---|
| 25 | #include <ocidl.h> |
|---|
| 26 | #include <malloc.h> |
|---|
| 27 | #include "comptr.hh" |
|---|
| 28 | #include "error.hh" |
|---|
| 29 | |
|---|
| 30 | #include <mediastreamer2/mswebcam.h> |
|---|
| 31 | #include <mediastreamer2/msfilter.h> |
|---|
| 32 | #include <mediastreamer2/msticker.h> |
|---|
| 33 | #include <mediastreamer2/msvideo.h> |
|---|
| 34 | |
|---|
| 35 | #define FILTER_NAME L"HornetsEye Capture Filter" |
|---|
| 36 | #define PIN_NAME L"Capture" |
|---|
| 37 | |
|---|
| 38 | using namespace Hornetseye; |
|---|
| 39 | |
|---|
| 40 | DEFINE_GUID( CLSID_VideoInputDeviceCategory, 0x860BB310, 0x5D01, |
|---|
| 41 | 0x11d0, 0xBD, 0x3B, 0x00, 0xA0, 0xC9, 0x11, 0xCE, 0x86); |
|---|
| 42 | DEFINE_GUID( CLSID_SystemDeviceEnum, 0x62BE5D10, 0x60EB, 0x11d0, |
|---|
| 43 | 0xBD, 0x3B, 0x00, 0xA0, 0xC9, 0x11, 0xCE, 0x86 ); |
|---|
| 44 | DEFINE_GUID( CLSID_FilterGraph, 0xe436ebb3, 0x524f, 0x11ce, |
|---|
| 45 | 0x9f, 0x53, 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70); |
|---|
| 46 | DEFINE_GUID( CLSID_SampleGrabber, 0xc1f400a0, 0x3f08, 0x11d3, |
|---|
| 47 | 0x9f, 0x0b, 0x00, 0x60, 0x08, 0x03, 0x9e, 0x37 ); |
|---|
| 48 | DEFINE_GUID( CLSID_NullRenderer,0xc1f400a4, 0x3f08, 0x11d3, |
|---|
| 49 | 0x9f, 0x0b, 0x00, 0x60, 0x08, 0x03, 0x9e, 0x37 ); |
|---|
| 50 | DEFINE_GUID( CLSID_VfwCapture, 0x1b544c22, 0xfd0b, 0x11ce, |
|---|
| 51 | 0x8c, 0x63, 0x0, 0xaa, 0x00, 0x44, 0xb5, 0x1e); |
|---|
| 52 | DEFINE_GUID( IID_IGraphBuilder, 0x56a868a9, 0x0ad4, 0x11ce, |
|---|
| 53 | 0xb0, 0x3a, 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70); |
|---|
| 54 | DEFINE_GUID( IID_IBaseFilter, 0x56a86895, 0x0ad4, 0x11ce, |
|---|
| 55 | 0xb0, 0x3a, 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70 ); |
|---|
| 56 | DEFINE_GUID( IID_ICreateDevEnum, 0x29840822, 0x5b84, 0x11d0, |
|---|
| 57 | 0xbd, 0x3b, 0x00, 0xa0, 0xc9, 0x11, 0xce, 0x86 ); |
|---|
| 58 | DEFINE_GUID( IID_IEnumFilters, 0x56a86893, 0xad4, 0x11ce, |
|---|
| 59 | 0xb0, 0x3a, 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70 ); |
|---|
| 60 | DEFINE_GUID( IID_IEnumPins, 0x56a86892, 0x0ad4, 0x11ce, |
|---|
| 61 | 0xb0, 0x3a, 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70 ); |
|---|
| 62 | DEFINE_GUID( IID_IMediaSample, 0x56a8689a, 0x0ad4, 0x11ce, |
|---|
| 63 | 0xb0, 0x3a, 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70 ); |
|---|
| 64 | DEFINE_GUID( IID_IMediaFilter, 0x56a86899, 0x0ad4, 0x11ce, |
|---|
| 65 | 0xb0, 0x3a, 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70 ); |
|---|
| 66 | DEFINE_GUID( IID_IPin, 0x56a86891, 0x0ad4, 0x11ce, |
|---|
| 67 | 0xb0, 0x3a, 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70 ); |
|---|
| 68 | DEFINE_GUID( IID_ISampleGrabber, 0x6b652fff, 0x11fe, 0x4fce, |
|---|
| 69 | 0x92, 0xad, 0x02, 0x66, 0xb5, 0xd7, 0xc7, 0x8f ); |
|---|
| 70 | DEFINE_GUID( IID_ISampleGrabberCB, 0x0579154a, 0x2b53, 0x4994, |
|---|
| 71 | 0xb0, 0xd0, 0xe7, 0x73, 0x14, 0x8e, 0xff, 0x85 ); |
|---|
| 72 | DEFINE_GUID( IID_IMediaEvent, 0x56a868b6, 0x0ad4, 0x11ce, |
|---|
| 73 | 0xb0, 0x3a, 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70 ); |
|---|
| 74 | DEFINE_GUID( IID_IMediaControl, 0x56a868b1, 0x0ad4, 0x11ce, |
|---|
| 75 | 0xb0, 0x3a, 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70 ); |
|---|
| 76 | DEFINE_GUID( IID_IMemInputPin, 0x56a8689d, 0x0ad4, 0x11ce, |
|---|
| 77 | 0xb0, 0x3a, 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70 ); |
|---|
| 78 | DEFINE_GUID( IID_IAMStreamConfig, 0xc6e13340, 0x30ac, 0x11d0, |
|---|
| 79 | 0xa1, 0x8c, 0x00, 0xa0, 0xc9, 0x11, 0x89, 0x56 ); |
|---|
| 80 | DEFINE_GUID( IID_IVideoProcAmp, 0x4050560e, 0x42a7, 0x413a, |
|---|
| 81 | 0x85, 0xc2, 0x09, 0x26, 0x9a, 0x2d, 0x0f, 0x44 ); |
|---|
| 82 | DEFINE_GUID( MEDIATYPE_Video, 0x73646976, 0x0000, 0x0010, |
|---|
| 83 | 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 ); |
|---|
| 84 | DEFINE_GUID( MEDIASUBTYPE_I420, 0x30323449, 0x0000, 0x0010, |
|---|
| 85 | 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71); |
|---|
| 86 | DEFINE_GUID( MEDIASUBTYPE_YV12, 0x32315659, 0x0000, 0x0010, |
|---|
| 87 | 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 ); |
|---|
| 88 | DEFINE_GUID( MEDIASUBTYPE_IYUV, 0x56555949, 0x0000, 0x0010, |
|---|
| 89 | 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 ); |
|---|
| 90 | DEFINE_GUID( MEDIASUBTYPE_YUYV, 0x56595559, 0x0000, 0x0010, |
|---|
| 91 | 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 ); |
|---|
| 92 | DEFINE_GUID( MEDIASUBTYPE_YUY2, 0x32595559, 0x0000, 0x0010, |
|---|
| 93 | 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 ); |
|---|
| 94 | DEFINE_GUID( MEDIASUBTYPE_UYVY, 0x59565955, 0x0000, 0x0010, |
|---|
| 95 | 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 ); |
|---|
| 96 | DEFINE_GUID( MEDIASUBTYPE_RGB24, 0xe436eb7d, 0x524f, 0x11ce, |
|---|
| 97 | 0x9f, 0x53, 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70 ); |
|---|
| 98 | |
|---|
| 99 | using namespace std; |
|---|
| 100 | |
|---|
| 101 | typedef LONGLONG REFERENCE_TIME; |
|---|
| 102 | |
|---|
| 103 | typedef struct tagVIDEOINFOHEADER { |
|---|
| 104 | RECT rcSource; |
|---|
| 105 | RECT rcTarget; |
|---|
| 106 | DWORD dwBitRate; |
|---|
| 107 | DWORD dwBitErrorRate; |
|---|
| 108 | REFERENCE_TIME AvgTimePerFrame; |
|---|
| 109 | BITMAPINFOHEADER bmiHeader; |
|---|
| 110 | } VIDEOINFOHEADER; |
|---|
| 111 | |
|---|
| 112 | typedef struct _AMMediaType { |
|---|
| 113 | GUID majortype; |
|---|
| 114 | GUID subtype; |
|---|
| 115 | BOOL bFixedSizeSamples; |
|---|
| 116 | BOOL bTemporalCompression; |
|---|
| 117 | ULONG lSampleSize; |
|---|
| 118 | GUID formattype; |
|---|
| 119 | IUnknown *pUnk; |
|---|
| 120 | ULONG cbFormat; |
|---|
| 121 | BYTE *pbFormat; |
|---|
| 122 | } AM_MEDIA_TYPE; |
|---|
| 123 | |
|---|
| 124 | DECLARE_ENUMERATOR_(IEnumMediaTypes,AM_MEDIA_TYPE*); |
|---|
| 125 | |
|---|
| 126 | typedef struct _VIDEO_STREAM_CONFIG_CAPS |
|---|
| 127 | { |
|---|
| 128 | GUID guid; |
|---|
| 129 | ULONG VideoStandard; |
|---|
| 130 | SIZE InputSize; |
|---|
| 131 | SIZE MinCroppingSize; |
|---|
| 132 | SIZE MaxCroppingSize; |
|---|
| 133 | int CropGranularityX; |
|---|
| 134 | int CropGranularityY; |
|---|
| 135 | int CropAlignX; |
|---|
| 136 | int CropAlignY; |
|---|
| 137 | SIZE MinOutputSize; |
|---|
| 138 | SIZE MaxOutputSize; |
|---|
| 139 | int OutputGranularityX; |
|---|
| 140 | int OutputGranularityY; |
|---|
| 141 | int StretchTapsX; |
|---|
| 142 | int StretchTapsY; |
|---|
| 143 | int ShrinkTapsX; |
|---|
| 144 | int ShrinkTapsY; |
|---|
| 145 | LONGLONG MinFrameInterval; |
|---|
| 146 | LONGLONG MaxFrameInterval; |
|---|
| 147 | LONG MinBitsPerSecond; |
|---|
| 148 | LONG MaxBitsPerSecond; |
|---|
| 149 | } VIDEO_STREAM_CONFIG_CAPS; |
|---|
| 150 | |
|---|
| 151 | typedef LONGLONG REFERENCE_TIME; |
|---|
| 152 | |
|---|
| 153 | typedef interface IBaseFilter IBaseFilter; |
|---|
| 154 | typedef interface IReferenceClock IReferenceClock; |
|---|
| 155 | typedef interface IFilterGraph IFilterGraph; |
|---|
| 156 | |
|---|
| 157 | typedef enum _FilterState { |
|---|
| 158 | State_Stopped, |
|---|
| 159 | State_Paused, |
|---|
| 160 | State_Running |
|---|
| 161 | } FILTER_STATE; |
|---|
| 162 | |
|---|
| 163 | #define MAX_FILTER_NAME 128 |
|---|
| 164 | typedef struct _FilterInfo { |
|---|
| 165 | WCHAR achName[MAX_FILTER_NAME]; |
|---|
| 166 | IFilterGraph *pGraph; |
|---|
| 167 | } FILTER_INFO; |
|---|
| 168 | |
|---|
| 169 | typedef enum _PinDirection { |
|---|
| 170 | PINDIR_INPUT, |
|---|
| 171 | PINDIR_OUTPUT |
|---|
| 172 | } PIN_DIRECTION; |
|---|
| 173 | |
|---|
| 174 | #define MAX_PIN_NAME 128 |
|---|
| 175 | typedef struct _PinInfo { |
|---|
| 176 | IBaseFilter *pFilter; |
|---|
| 177 | PIN_DIRECTION dir; |
|---|
| 178 | WCHAR achName[MAX_PIN_NAME]; |
|---|
| 179 | } PIN_INFO; |
|---|
| 180 | |
|---|
| 181 | #define INTERFACE IPin |
|---|
| 182 | DECLARE_INTERFACE_(IPin,IUnknown) |
|---|
| 183 | { |
|---|
| 184 | STDMETHOD(QueryInterface)(THIS_ REFIID,PVOID*) PURE; |
|---|
| 185 | STDMETHOD_(ULONG,AddRef)(THIS) PURE; |
|---|
| 186 | STDMETHOD_(ULONG,Release)(THIS) PURE; |
|---|
| 187 | STDMETHOD(Connect)(THIS_ IPin*,const AM_MEDIA_TYPE*) PURE; |
|---|
| 188 | STDMETHOD(ReceiveConnection)(THIS_ IPin*,const AM_MEDIA_TYPE*) PURE; |
|---|
| 189 | STDMETHOD(Disconnect)(THIS) PURE; |
|---|
| 190 | STDMETHOD(ConnectedTo)(THIS_ IPin**) PURE; |
|---|
| 191 | STDMETHOD(ConnectionMediaType)(THIS_ AM_MEDIA_TYPE*) PURE; |
|---|
| 192 | STDMETHOD(QueryPinInfo)(THIS_ PIN_INFO*) PURE; |
|---|
| 193 | STDMETHOD(QueryDirection)(THIS_ PIN_DIRECTION*) PURE; |
|---|
| 194 | }; |
|---|
| 195 | #undef INTERFACE |
|---|
| 196 | |
|---|
| 197 | DECLARE_ENUMERATOR_(IEnumPins,IPin*); |
|---|
| 198 | |
|---|
| 199 | typedef struct _AllocatorProperties { |
|---|
| 200 | long cBuffers; |
|---|
| 201 | long cbBuffer; |
|---|
| 202 | long cbAlign; |
|---|
| 203 | long cbPrefix; |
|---|
| 204 | } ALLOCATOR_PROPERTIES; |
|---|
| 205 | |
|---|
| 206 | typedef LONG_PTR OAEVENT; |
|---|
| 207 | |
|---|
| 208 | #define INTERFACE IMediaEvent |
|---|
| 209 | DECLARE_INTERFACE_(IMediaEvent,IDispatch) |
|---|
| 210 | { |
|---|
| 211 | STDMETHOD(QueryInterface)(THIS_ REFIID,PVOID*) PURE; |
|---|
| 212 | STDMETHOD_(ULONG,AddRef)(THIS) PURE; |
|---|
| 213 | STDMETHOD_(ULONG,Release)(THIS) PURE; |
|---|
| 214 | STDMETHOD(GetEventHandle)(THIS_ OAEVENT*) PURE; |
|---|
| 215 | STDMETHOD(GetEvent)(THIS_ long*,LONG_PTR,LONG_PTR,long) PURE; |
|---|
| 216 | STDMETHOD(WaitForCompletion)(THIS_ long,long*) PURE; |
|---|
| 217 | STDMETHOD(CancelDefaultHandling)(THIS_ long) PURE; |
|---|
| 218 | STDMETHOD(RestoreDefaultHandling)(THIS_ long) PURE; |
|---|
| 219 | STDMETHOD(FreeEventParams)(THIS_ long,LONG_PTR,LONG_PTR) PURE; |
|---|
| 220 | }; |
|---|
| 221 | #undef INTERFACE |
|---|
| 222 | |
|---|
| 223 | typedef long OAFilterState; |
|---|
| 224 | |
|---|
| 225 | #define INTERFACE IMediaControl |
|---|
| 226 | DECLARE_INTERFACE_(IMediaControl,IDispatch) |
|---|
| 227 | { |
|---|
| 228 | STDMETHOD(QueryInterface)(THIS_ REFIID,PVOID*) PURE; |
|---|
| 229 | STDMETHOD_(ULONG,AddRef)(THIS) PURE; |
|---|
| 230 | STDMETHOD_(ULONG,Release)(THIS) PURE; |
|---|
| 231 | STDMETHOD(Run)(THIS) PURE; |
|---|
| 232 | STDMETHOD(Pause)(THIS) PURE; |
|---|
| 233 | STDMETHOD(Stop)(THIS) PURE; |
|---|
| 234 | STDMETHOD(GetState)(THIS_ LONG,OAFilterState*) PURE; |
|---|
| 235 | STDMETHOD(RenderFile)(THIS_ BSTR) PURE; |
|---|
| 236 | STDMETHOD(AddSourceFilter)(THIS_ BSTR,IDispatch**) PURE; |
|---|
| 237 | STDMETHOD(get_FilterCollection)(THIS_ IDispatch**) PURE; |
|---|
| 238 | STDMETHOD(get_RegFilterCollection)(THIS_ IDispatch**) PURE; |
|---|
| 239 | STDMETHOD(StopWhenReady)(THIS) PURE; |
|---|
| 240 | }; |
|---|
| 241 | #undef INTERFACE |
|---|
| 242 | |
|---|
| 243 | #define INTERFACE IVideoProcAmp |
|---|
| 244 | DECLARE_INTERFACE_(IVideoProcAmp,IUnknown) |
|---|
| 245 | { |
|---|
| 246 | STDMETHOD(QueryInterface)(THIS_ REFIID,PVOID*) PURE; |
|---|
| 247 | STDMETHOD_(ULONG,AddRef)(THIS) PURE; |
|---|
| 248 | STDMETHOD_(ULONG,Release)(THIS) PURE; |
|---|
| 249 | }; |
|---|
| 250 | #undef INTERFACE |
|---|
| 251 | |
|---|
| 252 | #define INTERFACE IAMStreamConfig |
|---|
| 253 | DECLARE_INTERFACE_(IAMStreamConfig,IUnknown) |
|---|
| 254 | { |
|---|
| 255 | STDMETHOD(QueryInterface)(THIS_ REFIID,PVOID*) PURE; |
|---|
| 256 | STDMETHOD_(ULONG,AddRef)(THIS) PURE; |
|---|
| 257 | STDMETHOD_(ULONG,Release)(THIS) PURE; |
|---|
| 258 | STDMETHOD(SetFormat)(THIS_ AM_MEDIA_TYPE*) PURE; |
|---|
| 259 | STDMETHOD(GetFormat)(THIS_ AM_MEDIA_TYPE**) PURE; |
|---|
| 260 | STDMETHOD(GetNumberOfCapabilities)(THIS_ int*,int*) PURE; |
|---|
| 261 | STDMETHOD(GetStreamCaps)(THIS_ int,AM_MEDIA_TYPE**,BYTE*) PURE; |
|---|
| 262 | }; |
|---|
| 263 | #undef INTERFACE |
|---|
| 264 | |
|---|
| 265 | #define INTERFACE IMediaFilter |
|---|
| 266 | DECLARE_INTERFACE_(IMediaFilter,IPersist) |
|---|
| 267 | { |
|---|
| 268 | STDMETHOD(QueryInterface)(THIS_ REFIID,PVOID*) PURE; |
|---|
| 269 | STDMETHOD_(ULONG,AddRef)(THIS) PURE; |
|---|
| 270 | STDMETHOD_(ULONG,Release)(THIS) PURE; |
|---|
| 271 | STDMETHOD(Stop)(THIS) PURE; |
|---|
| 272 | STDMETHOD(Pause)(THIS) PURE; |
|---|
| 273 | STDMETHOD(Run)(THIS_ REFERENCE_TIME) PURE; |
|---|
| 274 | STDMETHOD(GetState)(THIS_ DWORD,FILTER_STATE*) PURE; |
|---|
| 275 | STDMETHOD(SetSyncSource)(THIS_ IReferenceClock*) PURE; |
|---|
| 276 | STDMETHOD(GetSyncSource)(THIS_ IReferenceClock**) PURE; |
|---|
| 277 | }; |
|---|
| 278 | #undef INTERFACE |
|---|
| 279 | |
|---|
| 280 | #define INTERFACE IBaseFilter |
|---|
| 281 | DECLARE_INTERFACE_(IBaseFilter,IMediaFilter) |
|---|
| 282 | { |
|---|
| 283 | STDMETHOD(QueryInterface)(THIS_ REFIID,PVOID*) PURE; |
|---|
| 284 | STDMETHOD_(ULONG,AddRef)(THIS) PURE; |
|---|
| 285 | STDMETHOD_(ULONG,Release)(THIS) PURE; |
|---|
| 286 | STDMETHOD(EnumPins)(THIS_ IEnumPins**) PURE; |
|---|
| 287 | STDMETHOD(FindPin)(THIS_ LPCWSTR,IPin**) PURE; |
|---|
| 288 | STDMETHOD(QueryFilterInfo)(THIS_ FILTER_INFO*) PURE; |
|---|
| 289 | STDMETHOD(JoinFilterGraph)(THIS_ IFilterGraph*,LPCWSTR) PURE; |
|---|
| 290 | STDMETHOD(QueryVendorInfo)(THIS_ LPWSTR*) PURE; |
|---|
| 291 | }; |
|---|
| 292 | #undef INTERFACE |
|---|
| 293 | |
|---|
| 294 | DECLARE_ENUMERATOR_(IEnumFilters,IBaseFilter*); |
|---|
| 295 | |
|---|
| 296 | // #define INTERFACE IEnumFilters |
|---|
| 297 | // DECLARE_INTERFACE_(IEnumFilters,IUnknown) |
|---|
| 298 | // { |
|---|
| 299 | // STDMETHOD(QueryInterface)(THIS_ REFIID,PVOID*) PURE; |
|---|
| 300 | // STDMETHOD_(ULONG,AddRef)(THIS) PURE; |
|---|
| 301 | // STDMETHOD_(ULONG,Release)(THIS) PURE; |
|---|
| 302 | // STDMETHOD(Next)(THIS_ ULONG,IBaseFilter**,ULONG*) PURE; |
|---|
| 303 | // STDMETHOD(Skip)(THIS_ ULONG) PURE; |
|---|
| 304 | // STDMETHOD(Reset)(THIS) PURE; |
|---|
| 305 | // STDMETHOD(Clone)(THIS_ IEnumFilters**) PURE; |
|---|
| 306 | // }; |
|---|
| 307 | // #undef INTERFACE |
|---|
| 308 | |
|---|
| 309 | #define INTERFACE IFilterGraph |
|---|
| 310 | DECLARE_INTERFACE_(IFilterGraph,IUnknown) |
|---|
| 311 | { |
|---|
| 312 | STDMETHOD(QueryInterface)(THIS_ REFIID,PVOID*) PURE; |
|---|
| 313 | STDMETHOD_(ULONG,AddRef)(THIS) PURE; |
|---|
| 314 | STDMETHOD_(ULONG,Release)(THIS) PURE; |
|---|
| 315 | STDMETHOD(AddFilter)(THIS_ IBaseFilter*,LPCWSTR) PURE; |
|---|
| 316 | STDMETHOD(RemoveFilter)(THIS_ IBaseFilter*) PURE; |
|---|
| 317 | STDMETHOD(EnumFilters)(THIS_ IEnumFilters**) PURE; |
|---|
| 318 | STDMETHOD(FindFilterByName)(THIS_ LPCWSTR,IBaseFilter**) PURE; |
|---|
| 319 | STDMETHOD(ConnectDirect)(THIS_ IPin*,IPin*,const AM_MEDIA_TYPE*) PURE; |
|---|
| 320 | STDMETHOD(Reconnect)(THIS_ IPin*) PURE; |
|---|
| 321 | STDMETHOD(Disconnect)(THIS_ IPin*) PURE; |
|---|
| 322 | STDMETHOD(SetDefaultSyncSource)(THIS) PURE; |
|---|
| 323 | }; |
|---|
| 324 | #undef INTERFACE |
|---|
| 325 | |
|---|
| 326 | #define INTERFACE IGraphBuilder |
|---|
| 327 | DECLARE_INTERFACE_(IGraphBuilder,IFilterGraph) |
|---|
| 328 | { |
|---|
| 329 | STDMETHOD(QueryInterface)(THIS_ REFIID,PVOID*) PURE; |
|---|
| 330 | STDMETHOD_(ULONG,AddRef)(THIS) PURE; |
|---|
| 331 | STDMETHOD_(ULONG,Release)(THIS) PURE; |
|---|
| 332 | STDMETHOD(Connect)(THIS_ IPin*,IPin*) PURE; |
|---|
| 333 | STDMETHOD(Render)(THIS_ IPin*) PURE; |
|---|
| 334 | STDMETHOD(RenderFile)(THIS_ LPCWSTR,LPCWSTR) PURE; |
|---|
| 335 | STDMETHOD(AddSourceFilter)(THIS_ LPCWSTR,LPCWSTR,IBaseFilter**) PURE; |
|---|
| 336 | STDMETHOD(SetLogFile)(THIS_ DWORD_PTR) PURE; |
|---|
| 337 | STDMETHOD(Abort)(THIS) PURE; |
|---|
| 338 | STDMETHOD(ShouldOperationContinue)(THIS) PURE; |
|---|
| 339 | }; |
|---|
| 340 | #undef INTERFACE |
|---|
| 341 | |
|---|
| 342 | #define INTERFACE ICreateDevEnum |
|---|
| 343 | DECLARE_INTERFACE_(ICreateDevEnum,IUnknown) |
|---|
| 344 | { |
|---|
| 345 | STDMETHOD(QueryInterface)(THIS_ REFIID,PVOID*) PURE; |
|---|
| 346 | STDMETHOD_(ULONG,AddRef)(THIS) PURE; |
|---|
| 347 | STDMETHOD_(ULONG,Release)(THIS) PURE; |
|---|
| 348 | STDMETHOD(CreateClassEnumerator)(THIS_ REFIID,IEnumMoniker**,DWORD) PURE; |
|---|
| 349 | }; |
|---|
| 350 | #undef INTERFACE |
|---|
| 351 | |
|---|
| 352 | #define INTERFACE IMediaSample |
|---|
| 353 | DECLARE_INTERFACE_(IMediaSample,IUnknown) |
|---|
| 354 | { |
|---|
| 355 | STDMETHOD(QueryInterface)(THIS_ REFIID,PVOID*) PURE; |
|---|
| 356 | STDMETHOD_(ULONG,AddRef)(THIS) PURE; |
|---|
| 357 | STDMETHOD_(ULONG,Release)(THIS) PURE; |
|---|
| 358 | STDMETHOD(GetPointer)(THIS_ BYTE **) PURE; |
|---|
| 359 | STDMETHOD_(long, GetSize)(THIS) PURE; |
|---|
| 360 | }; |
|---|
| 361 | /* |
|---|
| 362 | #define INTERFACE IMediaSample |
|---|
| 363 | DECLARE_INTERFACE_(IMediaSample, IUnknown) |
|---|
| 364 | { |
|---|
| 365 | STDMETHOD(GetPointer)(THIS_ BYTE **) PURE; |
|---|
| 366 | STDMETHOD_(long, GetSize)(THIS) PURE; |
|---|
| 367 | STDMETHOD(GetTime)(THIS_ REFERENCE_TIME *, REFERENCE_TIME *) PURE; |
|---|
| 368 | STDMETHOD(SetTime)(THIS_ REFERENCE_TIME *, REFERENCE_TIME *) PURE; |
|---|
| 369 | STDMETHOD(IsSyncPoint)(THIS) PURE; |
|---|
| 370 | STDMETHOD(SetSyncPoint)(THIS_ BOOL) PURE; |
|---|
| 371 | STDMETHOD(IsPreroll)(THIS) PURE; |
|---|
| 372 | STDMETHOD(SetPreroll)(THIS_ BOOL) PURE; |
|---|
| 373 | STDMETHOD_(long, GetActualDataLength)(THIS) PURE; |
|---|
| 374 | STDMETHOD(SetActualDataLength)(THIS_ long) PURE; |
|---|
| 375 | STDMETHOD(GetMediaType)(THIS_ AM_MEDIA_TYPE **) PURE; |
|---|
| 376 | STDMETHOD(SetMediaType)(THIS_ AM_MEDIA_TYPE *) PURE; |
|---|
| 377 | STDMETHOD(IsDiscontinuity)(THIS) PURE; |
|---|
| 378 | STDMETHOD(SetDiscontinuity)(THIS_ BOOL) PURE; |
|---|
| 379 | STDMETHOD(GetMediaTime)(THIS_ LONGLONG *, LONGLONG *) PURE; |
|---|
| 380 | STDMETHOD(SetMediaTime)(THIS_ LONGLONG *, LONGLONG *) PURE; |
|---|
| 381 | }; |
|---|
| 382 | */ |
|---|
| 383 | |
|---|
| 384 | |
|---|
| 385 | #undef INTERFACE |
|---|
| 386 | |
|---|
| 387 | #define INTERFACE IMemAllocator |
|---|
| 388 | DECLARE_INTERFACE_(IMemAllocator,IUnknown) |
|---|
| 389 | { |
|---|
| 390 | STDMETHOD(QueryInterface)(THIS_ REFIID,PVOID*) PURE; |
|---|
| 391 | STDMETHOD_(ULONG,AddRef)(THIS) PURE; |
|---|
| 392 | STDMETHOD_(ULONG,Release)(THIS) PURE; |
|---|
| 393 | STDMETHOD(SetProperties)(THIS_ ALLOCATOR_PROPERTIES*,ALLOCATOR_PROPERTIES*) PURE; |
|---|
| 394 | STDMETHOD(GetProperties)(THIS_ ALLOCATOR_PROPERTIES*) PURE; |
|---|
| 395 | STDMETHOD(Commit)(THIS) PURE; |
|---|
| 396 | STDMETHOD(Decommit)(THIS) PURE; |
|---|
| 397 | STDMETHOD(GetBuffer)(THIS_ IMediaSample **,REFERENCE_TIME*,REFERENCE_TIME*,DWORD) PURE; |
|---|
| 398 | STDMETHOD(ReleaseBuffer)(THIS_ IMediaSample*) PURE; |
|---|
| 399 | }; |
|---|
| 400 | #undef INTERFACE |
|---|
| 401 | |
|---|
| 402 | #define INTERFACE IMemInputPin |
|---|
| 403 | DECLARE_INTERFACE_(IMemInputPin,IUnknown) |
|---|
| 404 | { |
|---|
| 405 | STDMETHOD(QueryInterface)(THIS_ REFIID,PVOID*) PURE; |
|---|
| 406 | STDMETHOD_(ULONG,AddRef)(THIS) PURE; |
|---|
| 407 | STDMETHOD_(ULONG,Release)(THIS) PURE; |
|---|
| 408 | STDMETHOD(GetAllocator)(THIS_ IMemAllocator**) PURE; |
|---|
| 409 | STDMETHOD(NotifyAllocator)(THIS_ IMemAllocator*,BOOL) PURE; |
|---|
| 410 | STDMETHOD(GetAllocatorRequirements)(THIS_ ALLOCATOR_PROPERTIES*) PURE; |
|---|
| 411 | STDMETHOD(Receive)(THIS_ IMediaSample*) PURE; |
|---|
| 412 | STDMETHOD(ReceiveMultiple)(THIS_ IMediaSample**,LONG,LONG*) PURE; |
|---|
| 413 | STDMETHOD(ReceiveCanBlock)(THIS) PURE; |
|---|
| 414 | }; |
|---|
| 415 | #undef INTERFACE |
|---|
| 416 | |
|---|
| 417 | #define INTERFACE ISampleGrabberCB |
|---|
| 418 | DECLARE_INTERFACE_(ISampleGrabberCB,IUnknown) |
|---|
| 419 | { |
|---|
| 420 | STDMETHOD(QueryInterface)(THIS_ REFIID,PVOID*) PURE; |
|---|
| 421 | STDMETHOD_(ULONG,AddRef)(THIS) PURE; |
|---|
| 422 | STDMETHOD_(ULONG,Release)(THIS) PURE; |
|---|
| 423 | STDMETHOD(SampleCB)(THIS_ double,IMediaSample*) PURE; |
|---|
| 424 | STDMETHOD(BufferCB)(THIS_ double,BYTE*,long) PURE; |
|---|
| 425 | }; |
|---|
| 426 | #undef INTERFACE |
|---|
| 427 | |
|---|
| 428 | #define INTERFACE ISampleGrabber |
|---|
| 429 | DECLARE_INTERFACE_(ISampleGrabber,IUnknown) |
|---|
| 430 | { |
|---|
| 431 | STDMETHOD(QueryInterface)(THIS_ REFIID,PVOID*) PURE; |
|---|
| 432 | STDMETHOD_(ULONG,AddRef)(THIS) PURE; |
|---|
| 433 | STDMETHOD_(ULONG,Release)(THIS) PURE; |
|---|
| 434 | STDMETHOD(SetOneShot)(THIS_ BOOL) PURE; |
|---|
| 435 | STDMETHOD(SetMediaType)(THIS_ const AM_MEDIA_TYPE*) PURE; |
|---|
| 436 | STDMETHOD(GetConnectedMediaType)(THIS_ AM_MEDIA_TYPE*) PURE; |
|---|
| 437 | STDMETHOD(SetBufferSamples)(THIS_ BOOL) PURE; |
|---|
| 438 | STDMETHOD(GetCurrentBuffer)(THIS_ long*,long*) PURE; |
|---|
| 439 | STDMETHOD(GetCurrentSample)(THIS_ IMediaSample**) PURE; |
|---|
| 440 | STDMETHOD(SetCallBack)(THIS_ ISampleGrabberCB *,long) PURE; |
|---|
| 441 | }; |
|---|
| 442 | #undef INTERFACE |
|---|
| 443 | |
|---|
| 444 | ComPtr< IPin > getPin( IBaseFilter *filter, PIN_DIRECTION direction, int num ) |
|---|
| 445 | { |
|---|
| 446 | ComPtr< IPin > retVal; |
|---|
| 447 | ComPtr< IEnumPins > enumPins; |
|---|
| 448 | if (filter->EnumPins( &enumPins )!=S_OK){ |
|---|
| 449 | ms_error("Error getting pin enumerator" ); |
|---|
| 450 | return retVal; |
|---|
| 451 | } |
|---|
| 452 | ULONG found; |
|---|
| 453 | ComPtr< IPin > pin; |
|---|
| 454 | while ( enumPins->Next( 1, &pin, &found ) == S_OK ) { |
|---|
| 455 | PIN_DIRECTION pinDirection = (PIN_DIRECTION)( -1 ); |
|---|
| 456 | pin->QueryDirection( &pinDirection ); |
|---|
| 457 | if ( pinDirection == direction ) { |
|---|
| 458 | if ( num == 0 ) { |
|---|
| 459 | retVal = pin; |
|---|
| 460 | break; |
|---|
| 461 | }; |
|---|
| 462 | num--; |
|---|
| 463 | }; |
|---|
| 464 | }; |
|---|
| 465 | return retVal; |
|---|
| 466 | } |
|---|
| 467 | |
|---|
| 468 | |
|---|
| 469 | |
|---|
| 470 | class DSCapture : public ISampleGrabberCB{ |
|---|
| 471 | public: |
|---|
| 472 | DSCapture(){ |
|---|
| 473 | qinit(&_rq); |
|---|
| 474 | ms_mutex_init(&_mutex,NULL); |
|---|
| 475 | _vsize=MS_VIDEO_SIZE_CIF; |
|---|
| 476 | _fps=15; |
|---|
| 477 | _start_time=0; |
|---|
| 478 | _frame_count=0; |
|---|
| 479 | _pixfmt=MS_YUV420P; |
|---|
| 480 | } |
|---|
| 481 | virtual ~DSCapture(){ |
|---|
| 482 | flushq(&_rq,0); |
|---|
| 483 | ms_mutex_destroy(&_mutex); |
|---|
| 484 | } |
|---|
| 485 | STDMETHODIMP QueryInterface( REFIID riid, void **ppv ); |
|---|
| 486 | STDMETHODIMP_(ULONG) AddRef(void); |
|---|
| 487 | STDMETHODIMP_(ULONG) Release(void); |
|---|
| 488 | STDMETHODIMP SampleCB(double,IMediaSample*); |
|---|
| 489 | STDMETHODIMP BufferCB(double,BYTE*,long); |
|---|
| 490 | int startDshowGraph(); |
|---|
| 491 | void stopAndClean(); |
|---|
| 492 | mblk_t *readFrame(){ |
|---|
| 493 | mblk_t *ret=NULL; |
|---|
| 494 | ms_mutex_lock(&_mutex); |
|---|
| 495 | ret=getq(&_rq); |
|---|
| 496 | ms_mutex_unlock(&_mutex); |
|---|
| 497 | return ret; |
|---|
| 498 | } |
|---|
| 499 | bool isTimeToSend(uint64_t ticker_time); |
|---|
| 500 | MSVideoSize getVSize()const{ |
|---|
| 501 | return _vsize; |
|---|
| 502 | } |
|---|
| 503 | void setVSize(MSVideoSize vsize){ |
|---|
| 504 | _vsize=vsize; |
|---|
| 505 | } |
|---|
| 506 | void setFps(float fps){ |
|---|
| 507 | _fps=fps; |
|---|
| 508 | } |
|---|
| 509 | MSPixFmt getPixFmt()const{ |
|---|
| 510 | return _pixfmt; |
|---|
| 511 | } |
|---|
| 512 | void setDeviceIndex(int index){ |
|---|
| 513 | _devid=index; |
|---|
| 514 | } |
|---|
| 515 | protected: |
|---|
| 516 | long m_refCount; |
|---|
| 517 | private: |
|---|
| 518 | int selectBestFormat(ComPtr<IAMStreamConfig> streamConfig, int count); |
|---|
| 519 | int _devid; |
|---|
| 520 | MSVideoSize _vsize; |
|---|
| 521 | queue_t _rq; |
|---|
| 522 | ms_mutex_t _mutex; |
|---|
| 523 | float _fps; |
|---|
| 524 | float _start_time; |
|---|
| 525 | int _frame_count; |
|---|
| 526 | MSPixFmt _pixfmt; |
|---|
| 527 | ComPtr< IBaseFilter > _source; |
|---|
| 528 | ComPtr< IBaseFilter > _nullRenderer; |
|---|
| 529 | ComPtr< IBaseFilter > _grabberBase; |
|---|
| 530 | ComPtr< IMediaControl > _mediaControl; |
|---|
| 531 | ComPtr< IMediaEvent > _mediaEvent; |
|---|
| 532 | }; |
|---|
| 533 | |
|---|
| 534 | |
|---|
| 535 | STDMETHODIMP DSCapture::QueryInterface(REFIID riid, void **ppv) |
|---|
| 536 | { |
|---|
| 537 | HRESULT retval; |
|---|
| 538 | if ( ppv == NULL ) return E_POINTER; |
|---|
| 539 | /* |
|---|
| 540 | if ( riid == IID_IUnknown ) { |
|---|
| 541 | *ppv = static_cast< IUnknown * >( this ); |
|---|
| 542 | AddRef(); |
|---|
| 543 | retval = S_OK; |
|---|
| 544 | } else if ( riid == IID_ISampleGrabberCB ) { |
|---|
| 545 | *ppv = static_cast< ISampleGrabberCB * >( this ); |
|---|
| 546 | AddRef(); |
|---|
| 547 | retval = S_OK; |
|---|
| 548 | } else */ { |
|---|
| 549 | #ifndef NDEBUG |
|---|
| 550 | cerr << setbase( 16 ) << setfill('0') |
|---|
| 551 | << "DEFINE_GUID( ..., 0x" << setw(8) << (int)riid.Data1 << ", 0x" |
|---|
| 552 | << setw(4) << (int)riid.Data2 << "," << endl |
|---|
| 553 | << " 0x" |
|---|
| 554 | << setw(4) << (int)riid.Data3 << ", 0x" << setw(2) |
|---|
| 555 | << (int)riid.Data4[0] << ", 0x" |
|---|
| 556 | << (int)riid.Data4[1] << ", 0x" |
|---|
| 557 | << (int)riid.Data4[2] << ", 0x" |
|---|
| 558 | << (int)riid.Data4[3] << ", 0x" |
|---|
| 559 | << (int)riid.Data4[4] << ", 0x" |
|---|
| 560 | << (int)riid.Data4[5] << ", 0x" |
|---|
| 561 | << (int)riid.Data4[6] << ", 0x" |
|---|
| 562 | << (int)riid.Data4[7] << " ) ?" << endl |
|---|
| 563 | << setfill( ' ' ) << setw( 0 ) << setbase( 10 ); |
|---|
| 564 | #endif |
|---|
| 565 | retval = E_NOINTERFACE; |
|---|
| 566 | }; |
|---|
| 567 | return retval; |
|---|
| 568 | }; |
|---|
| 569 | |
|---|
| 570 | STDMETHODIMP_(ULONG) DSCapture::AddRef(){ |
|---|
| 571 | m_refCount++; |
|---|
| 572 | return m_refCount; |
|---|
| 573 | } |
|---|
| 574 | |
|---|
| 575 | STDMETHODIMP_(ULONG) DSCapture::Release() |
|---|
| 576 | { |
|---|
| 577 | ms_message("DSCapture::Release"); |
|---|
| 578 | if ( !InterlockedDecrement( &m_refCount ) ) { |
|---|
| 579 | int refcnt=m_refCount; |
|---|
| 580 | delete this; |
|---|
| 581 | return refcnt; |
|---|
| 582 | } |
|---|
| 583 | return m_refCount; |
|---|
| 584 | } |
|---|
| 585 | |
|---|
| 586 | static void dummy(void*p){ |
|---|
| 587 | } |
|---|
| 588 | |
|---|
| 589 | STDMETHODIMP DSCapture::SampleCB( double par1 , IMediaSample * sample) |
|---|
| 590 | { |
|---|
| 591 | uint8_t *p; |
|---|
| 592 | unsigned int size; |
|---|
| 593 | if (sample->GetPointer(&p)!=S_OK){ |
|---|
| 594 | ms_error("error in GetPointer()"); |
|---|
| 595 | return S_OK; |
|---|
| 596 | } |
|---|
| 597 | size=sample->GetSize(); |
|---|
| 598 | //ms_message( "DSCapture::SampleCB pointer=%p, size=%i",p,size); |
|---|
| 599 | mblk_t *m=esballoc(p,size,0,dummy); |
|---|
| 600 | m->b_wptr+=size; |
|---|
| 601 | ms_mutex_lock(&_mutex); |
|---|
| 602 | putq(&_rq,m); |
|---|
| 603 | ms_mutex_unlock(&_mutex); |
|---|
| 604 | return S_OK; |
|---|
| 605 | } |
|---|
| 606 | |
|---|
| 607 | |
|---|
| 608 | |
|---|
| 609 | STDMETHODIMP DSCapture::BufferCB( double, BYTE *b, long len) |
|---|
| 610 | { |
|---|
| 611 | ms_message("DSCapture::BufferCB"); |
|---|
| 612 | return S_OK; |
|---|
| 613 | } |
|---|
| 614 | |
|---|
| 615 | static void dscap_init(MSFilter *f){ |
|---|
| 616 | DSCapture *s=new DSCapture(); |
|---|
| 617 | f->data=s; |
|---|
| 618 | } |
|---|
| 619 | |
|---|
| 620 | |
|---|
| 621 | |
|---|
| 622 | static void dscap_uninit(MSFilter *f){ |
|---|
| 623 | DSCapture *s=(DSCapture*)f->data; |
|---|
| 624 | s->Release(); |
|---|
| 625 | } |
|---|
| 626 | |
|---|
| 627 | static char * fourcc_to_char(char *str, uint32_t fcc){ |
|---|
| 628 | memcpy(str,&fcc,4); |
|---|
| 629 | str[4]='\0'; |
|---|
| 630 | return str; |
|---|
| 631 | } |
|---|
| 632 | |
|---|
| 633 | static int find_best_format(ComPtr<IAMStreamConfig> streamConfig, int count, MSVideoSize *requested_size, MSPixFmt requested_fmt ){ |
|---|
| 634 | int i; |
|---|
| 635 | MSVideoSize best_found=(MSVideoSize){32768,32768}; |
|---|
| 636 | int best_index=-1; |
|---|
| 637 | char fccstr[5]; |
|---|
| 638 | char selected_fcc[5]; |
|---|
| 639 | for (i=0; i<count; i++ ) { |
|---|
| 640 | VIDEO_STREAM_CONFIG_CAPS videoConfig; |
|---|
| 641 | AM_MEDIA_TYPE *mediaType; |
|---|
| 642 | COERRORMACRO( streamConfig->GetStreamCaps( i, &mediaType, |
|---|
| 643 | (BYTE *)&videoConfig ), |
|---|
| 644 | Error, , "Error getting stream capabilities" ); |
|---|
| 645 | if ( mediaType->majortype == MEDIATYPE_Video && |
|---|
| 646 | mediaType->cbFormat != 0 ) { |
|---|
| 647 | VIDEOINFOHEADER *infoHeader = (VIDEOINFOHEADER*)mediaType->pbFormat; |
|---|
| 648 | ms_message("Seeing format %ix%i %s",infoHeader->bmiHeader.biWidth,infoHeader->bmiHeader.biHeight, |
|---|
| 649 | fourcc_to_char(fccstr,infoHeader->bmiHeader.biCompression)); |
|---|
| 650 | if (ms_fourcc_to_pix_fmt(infoHeader->bmiHeader.biCompression)==requested_fmt){ |
|---|
| 651 | MSVideoSize cur; |
|---|
| 652 | cur.width=infoHeader->bmiHeader.biWidth; |
|---|
| 653 | cur.height=infoHeader->bmiHeader.biHeight; |
|---|
| 654 | if (ms_video_size_greater_than(cur,*requested_size)){ |
|---|
| 655 | if (ms_video_size_greater_than(best_found,cur)){ |
|---|
| 656 | best_found=cur; |
|---|
| 657 | best_index=i; |
|---|
| 658 | fourcc_to_char(selected_fcc,infoHeader->bmiHeader.biCompression); |
|---|
| 659 | } |
|---|
| 660 | } |
|---|
| 661 | } |
|---|
| 662 | }; |
|---|
| 663 | if ( mediaType->cbFormat != 0 ) |
|---|
| 664 | CoTaskMemFree( (PVOID)mediaType->pbFormat ); |
|---|
| 665 | if ( mediaType->pUnk != NULL ) mediaType->pUnk->Release(); |
|---|
| 666 | CoTaskMemFree( (PVOID)mediaType ); |
|---|
| 667 | } |
|---|
| 668 | if (best_index!=-1) { |
|---|
| 669 | *requested_size=best_found; |
|---|
| 670 | ms_message("Best camera format is %s %ix%i",selected_fcc,best_found.width,best_found.height); |
|---|
| 671 | } |
|---|
| 672 | return best_index; |
|---|
| 673 | } |
|---|
| 674 | |
|---|
| 675 | int DSCapture::selectBestFormat(ComPtr<IAMStreamConfig> streamConfig, int count){ |
|---|
| 676 | int index; |
|---|
| 677 | _pixfmt=MS_YUV420P; |
|---|
| 678 | index=find_best_format(streamConfig, count, &_vsize, _pixfmt); |
|---|
| 679 | if (index!=-1) goto success; |
|---|
| 680 | _pixfmt=MS_YUY2; |
|---|
| 681 | index=find_best_format(streamConfig, count, &_vsize,_pixfmt); |
|---|
| 682 | if (index!=-1) goto success; |
|---|
| 683 | _pixfmt=MS_YUYV; |
|---|
| 684 | index=find_best_format(streamConfig, count, &_vsize, _pixfmt); |
|---|
| 685 | if (index!=-1) goto success; |
|---|
| 686 | _pixfmt=MS_RGB24; |
|---|
| 687 | index=find_best_format(streamConfig, count, &_vsize, _pixfmt); |
|---|
| 688 | if (index!=-1) { |
|---|
| 689 | _pixfmt=MS_RGB24_REV; |
|---|
| 690 | goto success; |
|---|
| 691 | } |
|---|
| 692 | ms_error("This camera does not support any of our pixel formats."); |
|---|
| 693 | return -1; |
|---|
| 694 | |
|---|
| 695 | success: |
|---|
| 696 | VIDEO_STREAM_CONFIG_CAPS videoConfig; |
|---|
| 697 | AM_MEDIA_TYPE *mediaType; |
|---|
| 698 | COERRORMACRO( streamConfig->GetStreamCaps( index, &mediaType, |
|---|
| 699 | (BYTE *)&videoConfig ), |
|---|
| 700 | Error, , "Error getting stream capabilities" ); |
|---|
| 701 | streamConfig->SetFormat( mediaType ); |
|---|
| 702 | return 0; |
|---|
| 703 | } |
|---|
| 704 | |
|---|
| 705 | int DSCapture::startDshowGraph(){ |
|---|
| 706 | ComPtr< ICreateDevEnum > createDevEnum; |
|---|
| 707 | CoInitialize(NULL); |
|---|
| 708 | createDevEnum.coCreateInstance( CLSID_SystemDeviceEnum, |
|---|
| 709 | IID_ICreateDevEnum, "Could not create " |
|---|
| 710 | "device enumerator" ); |
|---|
| 711 | ComPtr< IEnumMoniker > enumMoniker; |
|---|
| 712 | if (createDevEnum->CreateClassEnumerator( CLSID_VideoInputDeviceCategory, &enumMoniker, 0 )!=S_OK){ |
|---|
| 713 | ms_error("Fail to create class enumerator."); |
|---|
| 714 | return -1; |
|---|
| 715 | } |
|---|
| 716 | createDevEnum.reset(); |
|---|
| 717 | enumMoniker->Reset(); |
|---|
| 718 | |
|---|
| 719 | int index = 0; |
|---|
| 720 | ULONG fetched = 0; |
|---|
| 721 | ComPtr< IGraphBuilder > graphBuilder; |
|---|
| 722 | graphBuilder.coCreateInstance( CLSID_FilterGraph, IID_IGraphBuilder, |
|---|
| 723 | "Could not create graph builder " |
|---|
| 724 | "interface" ); |
|---|
| 725 | ComPtr< IMoniker > moniker; |
|---|
| 726 | for ( int i=0;enumMoniker->Next( 1, &moniker, &fetched )==S_OK;++i ) { |
|---|
| 727 | if (i==_devid){ |
|---|
| 728 | if (moniker->BindToObject( 0, 0, IID_IBaseFilter, (void **)&_source )!=S_OK){ |
|---|
| 729 | ms_error("Error binding moniker to base filter" ); |
|---|
| 730 | return -1; |
|---|
| 731 | } |
|---|
| 732 | } |
|---|
| 733 | } |
|---|
| 734 | if (_source.get()==0){ |
|---|
| 735 | ms_error("Could not interface with webcam devid=%i",_devid); |
|---|
| 736 | return -1; |
|---|
| 737 | } |
|---|
| 738 | moniker.reset(); |
|---|
| 739 | enumMoniker.reset(); |
|---|
| 740 | if (graphBuilder->AddFilter( _source.get(), L"Source" )!=S_OK){ |
|---|
| 741 | ms_error("Error adding camera source to filter graph" ); |
|---|
| 742 | return -1; |
|---|
| 743 | } |
|---|
| 744 | ComPtr< IPin > sourceOut = getPin( _source.get(), PINDIR_OUTPUT, 0 ); |
|---|
| 745 | if (sourceOut.get()==NULL){ |
|---|
| 746 | ms_error("Error getting output pin of camera source" ); |
|---|
| 747 | return -1; |
|---|
| 748 | } |
|---|
| 749 | ComPtr< IAMStreamConfig > streamConfig; |
|---|
| 750 | if (sourceOut->QueryInterface( IID_IAMStreamConfig, |
|---|
| 751 | (void **)&streamConfig )!=S_OK){ |
|---|
| 752 | ms_error("Error requesting stream configuration API" ); |
|---|
| 753 | return -1; |
|---|
| 754 | } |
|---|
| 755 | int count, size; |
|---|
| 756 | if (streamConfig->GetNumberOfCapabilities( &count, &size )!=S_OK){ |
|---|
| 757 | ms_error("Error getting number of capabilities" ); |
|---|
| 758 | return -1; |
|---|
| 759 | } |
|---|
| 760 | if (selectBestFormat(streamConfig,count)!=0){ |
|---|
| 761 | return -1; |
|---|
| 762 | } |
|---|
| 763 | streamConfig.reset(); |
|---|
| 764 | |
|---|
| 765 | if (CoCreateInstance( CLSID_SampleGrabber, NULL, |
|---|
| 766 | CLSCTX_INPROC, IID_IBaseFilter, |
|---|
| 767 | (void **)&_grabberBase )!=S_OK){ |
|---|
| 768 | ms_error("Error creating sample grabber" ); |
|---|
| 769 | return -1; |
|---|
| 770 | } |
|---|
| 771 | if (graphBuilder->AddFilter( _grabberBase.get(), L"Grabber" )!=S_OK){ |
|---|
| 772 | ms_error("Error adding sample grabber to filter graph"); |
|---|
| 773 | return -1; |
|---|
| 774 | } |
|---|
| 775 | ComPtr< ISampleGrabber > sampleGrabber; |
|---|
| 776 | if (_grabberBase->QueryInterface( IID_ISampleGrabber, |
|---|
| 777 | (void **)&sampleGrabber )!=S_OK){ |
|---|
| 778 | ms_error("Error requesting sample grabber interface"); |
|---|
| 779 | return -1; |
|---|
| 780 | } |
|---|
| 781 | if (sampleGrabber->SetOneShot( FALSE )!=S_OK){ |
|---|
| 782 | ms_error("Error disabling one-shot mode" ); |
|---|
| 783 | return -1; |
|---|
| 784 | } |
|---|
| 785 | if (sampleGrabber->SetBufferSamples( TRUE )!=S_OK){ |
|---|
| 786 | ms_error("Error enabling buffer sampling" ); |
|---|
| 787 | return -1; |
|---|
| 788 | } |
|---|
| 789 | if (sampleGrabber->SetCallBack(this, 0 )!=S_OK){ |
|---|
| 790 | ms_error("Error setting callback interface for grabbing" ); |
|---|
| 791 | return -1; |
|---|
| 792 | } |
|---|
| 793 | ComPtr< IPin > grabberIn = getPin( _grabberBase.get(), PINDIR_INPUT, 0 ); |
|---|
| 794 | if (grabberIn.get() == NULL){ |
|---|
| 795 | ms_error("Error getting input of sample grabber"); |
|---|
| 796 | return -1; |
|---|
| 797 | } |
|---|
| 798 | ComPtr< IPin > grabberOut = getPin( _grabberBase.get(), PINDIR_OUTPUT, 0 ); |
|---|
| 799 | if (grabberOut.get()==NULL){ |
|---|
| 800 | ms_error("Error getting output of sample grabber" ); |
|---|
| 801 | return -1; |
|---|
| 802 | } |
|---|
| 803 | if (CoCreateInstance( CLSID_NullRenderer, NULL, |
|---|
| 804 | CLSCTX_INPROC, IID_IBaseFilter, |
|---|
| 805 | (void **)&_nullRenderer )!=S_OK){ |
|---|
| 806 | ms_error("Error creating Null Renderer" ); |
|---|
| 807 | return -1; |
|---|
| 808 | } |
|---|
| 809 | if (graphBuilder->AddFilter( _nullRenderer.get(), L"Sink" )!=S_OK){ |
|---|
| 810 | ms_error("Error adding null renderer to filter graph" ); |
|---|
| 811 | return -1; |
|---|
| 812 | } |
|---|
| 813 | ComPtr< IPin > nullIn = getPin( _nullRenderer.get(), PINDIR_INPUT, 0 ); |
|---|
| 814 | if (graphBuilder->Connect( sourceOut.get(), grabberIn.get() )!=S_OK){ |
|---|
| 815 | ms_error("Error connecting source to sample grabber" ); |
|---|
| 816 | return -1; |
|---|
| 817 | } |
|---|
| 818 | if (graphBuilder->Connect( grabberOut.get(), nullIn.get() )!=S_OK){ |
|---|
| 819 | ms_error("Error connecting sample grabber to sink" ); |
|---|
| 820 | return -1; |
|---|
| 821 | } |
|---|
| 822 | ms_message("Directshow graph is now ready to run."); |
|---|
| 823 | |
|---|
| 824 | if (graphBuilder->QueryInterface( IID_IMediaControl, |
|---|
| 825 | (void **)&_mediaControl )!=S_OK){ |
|---|
| 826 | ms_error("Error requesting media control interface" ); |
|---|
| 827 | return -1; |
|---|
| 828 | } |
|---|
| 829 | HRESULT r=_mediaControl->Run(); |
|---|
| 830 | if (r!=S_OK && r!=S_FALSE){ |
|---|
| 831 | ms_error("Error starting graph (%i)",r); |
|---|
| 832 | } |
|---|
| 833 | ms_message("Graph started"); |
|---|
| 834 | if (graphBuilder->QueryInterface( IID_IMediaEvent, |
|---|
| 835 | (void **)&_mediaEvent )!=S_OK){ |
|---|
| 836 | ms_error("Error requesting event interface" ); |
|---|
| 837 | return -1; |
|---|
| 838 | } |
|---|
| 839 | } |
|---|
| 840 | |
|---|
| 841 | void DSCapture::stopAndClean(){ |
|---|
| 842 | if (_mediaControl.get()!=NULL){ |
|---|
| 843 | _mediaControl->Stop(); |
|---|
| 844 | long evCode = 0; |
|---|
| 845 | _mediaEvent->WaitForCompletion( INFINITE, &evCode ); |
|---|
| 846 | } |
|---|
| 847 | _source.reset(); |
|---|
| 848 | _grabberBase.reset(); |
|---|
| 849 | _nullRenderer.reset(); |
|---|
| 850 | _mediaControl.reset(); |
|---|
| 851 | _mediaEvent.reset(); |
|---|
| 852 | flushq(&_rq,0); |
|---|
| 853 | } |
|---|
| 854 | |
|---|
| 855 | bool DSCapture::isTimeToSend(uint64_t ticker_time){ |
|---|
| 856 | if (_frame_count==-1){ |
|---|
| 857 | _start_time=(float)ticker_time; |
|---|
| 858 | _frame_count=0; |
|---|
| 859 | } |
|---|
| 860 | int cur_frame=(int)(((float)ticker_time-_start_time)*_fps/1000.0); |
|---|
| 861 | if (cur_frame>_frame_count){ |
|---|
| 862 | _frame_count++; |
|---|
| 863 | return true; |
|---|
| 864 | } |
|---|
| 865 | return false; |
|---|
| 866 | } |
|---|
| 867 | |
|---|
| 868 | static void dscap_preprocess(MSFilter * obj){ |
|---|
| 869 | DSCapture *s=(DSCapture*)obj->data; |
|---|
| 870 | s->startDshowGraph(); |
|---|
| 871 | } |
|---|
| 872 | |
|---|
| 873 | static void dscap_postprocess(MSFilter * obj){ |
|---|
| 874 | DSCapture *s=(DSCapture*)obj->data; |
|---|
| 875 | s->stopAndClean(); |
|---|
| 876 | } |
|---|
| 877 | |
|---|
| 878 | static void dscap_process(MSFilter * obj){ |
|---|
| 879 | DSCapture *s=(DSCapture*)obj->data; |
|---|
| 880 | mblk_t *m; |
|---|
| 881 | uint32_t timestamp; |
|---|
| 882 | int cur_frame; |
|---|
| 883 | |
|---|
| 884 | if (s->isTimeToSend(obj->ticker->time)){ |
|---|
| 885 | mblk_t *om=NULL; |
|---|
| 886 | /*keep the most recent frame if several frames have been captured */ |
|---|
| 887 | while((m=s->readFrame())!=NULL){ |
|---|
| 888 | if (om!=NULL) freemsg(om); |
|---|
| 889 | om=m; |
|---|
| 890 | } |
|---|
| 891 | if (om!=NULL){ |
|---|
| 892 | timestamp=(uint32_t)(obj->ticker->time*90);/* rtp uses a 90000 Hz clockrate for video*/ |
|---|
| 893 | mblk_set_timestamp_info(om,timestamp); |
|---|
| 894 | ms_queue_put(obj->outputs[0],om); |
|---|
| 895 | } |
|---|
| 896 | } |
|---|
| 897 | } |
|---|
| 898 | |
|---|
| 899 | static int dscap_set_fps(MSFilter *f, void *arg){ |
|---|
| 900 | DSCapture *s=(DSCapture*)f->data; |
|---|
| 901 | s->setFps(*(float*)arg); |
|---|
| 902 | return 0; |
|---|
| 903 | } |
|---|
| 904 | |
|---|
| 905 | static int dscap_get_pix_fmt(MSFilter *f,void *arg){ |
|---|
| 906 | DSCapture *s=(DSCapture*)f->data; |
|---|
| 907 | *((MSPixFmt*)arg)=s->getPixFmt(); |
|---|
| 908 | return 0; |
|---|
| 909 | } |
|---|
| 910 | |
|---|
| 911 | static int dscap_set_vsize(MSFilter *f, void *arg){ |
|---|
| 912 | DSCapture *s=(DSCapture*)f->data; |
|---|
| 913 | s->setVSize(*((MSVideoSize*)arg)); |
|---|
| 914 | return 0; |
|---|
| 915 | } |
|---|
| 916 | |
|---|
| 917 | static int dscap_get_vsize(MSFilter *f, void *arg){ |
|---|
| 918 | DSCapture *s=(DSCapture*)f->data; |
|---|
| 919 | MSVideoSize *vs=(MSVideoSize*)arg; |
|---|
| 920 | *vs=s->getVSize(); |
|---|
| 921 | return 0; |
|---|
| 922 | } |
|---|
| 923 | |
|---|
| 924 | static MSFilterMethod methods[]={ |
|---|
| 925 | { MS_FILTER_SET_FPS , dscap_set_fps }, |
|---|
| 926 | { MS_FILTER_GET_PIX_FMT , dscap_get_pix_fmt }, |
|---|
| 927 | { MS_FILTER_SET_VIDEO_SIZE, dscap_set_vsize }, |
|---|
| 928 | { MS_FILTER_GET_VIDEO_SIZE, dscap_get_vsize }, |
|---|
| 929 | { 0 , NULL } |
|---|
| 930 | }; |
|---|
| 931 | |
|---|
| 932 | MSFilterDesc ms_dscap_desc={ |
|---|
| 933 | MS_FILTER_PLUGIN_ID, |
|---|
| 934 | "MSDsCap", |
|---|
| 935 | N_("A webcam grabber based on directshow."), |
|---|
| 936 | MS_FILTER_OTHER, |
|---|
| 937 | NULL, |
|---|
| 938 | 0, |
|---|
| 939 | 1, |
|---|
| 940 | dscap_init, |
|---|
| 941 | dscap_preprocess, |
|---|
| 942 | dscap_process, |
|---|
| 943 | dscap_postprocess, |
|---|
| 944 | dscap_uninit, |
|---|
| 945 | methods |
|---|
| 946 | }; |
|---|
| 947 | |
|---|
| 948 | |
|---|
| 949 | static void ms_dshow_detect(MSWebCamManager *obj); |
|---|
| 950 | static MSFilter * ms_dshow_create_reader(MSWebCam *obj){ |
|---|
| 951 | MSFilter *f=ms_filter_new_from_desc(&ms_dscap_desc); |
|---|
| 952 | DSCapture *s=(DSCapture*)f->data; |
|---|
| 953 | s->setDeviceIndex((int)obj->data); |
|---|
| 954 | return f; |
|---|
| 955 | } |
|---|
| 956 | |
|---|
| 957 | static MSWebCamDesc ms_dshow_cam_desc={ |
|---|
| 958 | "Directshow capture", |
|---|
| 959 | &ms_dshow_detect, |
|---|
| 960 | NULL, |
|---|
| 961 | &ms_dshow_create_reader, |
|---|
| 962 | NULL |
|---|
| 963 | }; |
|---|
| 964 | |
|---|
| 965 | static void ms_dshow_detect(MSWebCamManager *obj){ |
|---|
| 966 | int i; |
|---|
| 967 | MSWebCam *cam; |
|---|
| 968 | ComPtr<IPropertyBag> pBag; |
|---|
| 969 | |
|---|
| 970 | CoInitialize(NULL); |
|---|
| 971 | |
|---|
| 972 | ComPtr< ICreateDevEnum > createDevEnum; |
|---|
| 973 | createDevEnum.coCreateInstance( CLSID_SystemDeviceEnum, |
|---|
| 974 | IID_ICreateDevEnum, "Could not create " |
|---|
| 975 | "device enumerator" ); |
|---|
| 976 | ComPtr< IEnumMoniker > enumMoniker; |
|---|
| 977 | if (createDevEnum->CreateClassEnumerator( CLSID_VideoInputDeviceCategory, &enumMoniker, 0 )!=S_OK){ |
|---|
| 978 | ms_error("Fail to create class enumerator."); |
|---|
| 979 | return; |
|---|
| 980 | } |
|---|
| 981 | createDevEnum.reset(); |
|---|
| 982 | enumMoniker->Reset(); |
|---|
| 983 | |
|---|
| 984 | int index = 0; |
|---|
| 985 | ULONG fetched = 0; |
|---|
| 986 | ComPtr< IMoniker > moniker; |
|---|
| 987 | for ( int i=0;enumMoniker->Next( 1, &moniker, &fetched )==S_OK;++i ) { |
|---|
| 988 | VARIANT var; |
|---|
| 989 | if (moniker->BindToStorage( 0, 0, IID_IPropertyBag, (void**) &pBag )!=S_OK) |
|---|
| 990 | continue; |
|---|
| 991 | VariantInit(&var); |
|---|
| 992 | if (pBag->Read( L"FriendlyName", &var, NULL )!=S_OK) |
|---|
| 993 | continue; |
|---|
| 994 | char szName[256]; |
|---|
| 995 | WideCharToMultiByte(CP_ACP,0,var.bstrVal,-1,szName,256,0,0); |
|---|
| 996 | MSWebCam *cam=ms_web_cam_new(&ms_dshow_cam_desc); |
|---|
| 997 | cam->name=ms_strdup(szName); |
|---|
| 998 | cam->data=(void*)i; |
|---|
| 999 | ms_web_cam_manager_prepend_cam(obj,cam); |
|---|
| 1000 | VariantClear(&var); |
|---|
| 1001 | } |
|---|
| 1002 | enumMoniker.reset(); |
|---|
| 1003 | } |
|---|
| 1004 | |
|---|
| 1005 | extern "C" void libmsdscap_init(void){ |
|---|
| 1006 | ms_web_cam_manager_register_desc(ms_web_cam_manager_get(),&ms_dshow_cam_desc); |
|---|
| 1007 | } |
|---|