1 /*------------------------------------------------------------------------
2  *  Copyright 2012 (c) Klaus Triendl <klaus@triendl.eu>
3  *  Copyright 2012 (c) Jarek Czekalski <jarekczek@poczta.onet.pl>
4  *
5  *  This file is part of the ZBar Bar Code Reader.
6  *
7  *  The ZBar Bar Code Reader is free software; you can redistribute it
8  *  and/or modify it under the terms of the GNU Lesser Public License as
9  *  published by the Free Software Foundation; either version 2.1 of
10  *  the License, or (at your option) any later version.
11  *
12  *  The ZBar Bar Code Reader is distributed in the hope that it will be
13  *  useful, but WITHOUT ANY WARRANTY; without even the implied warranty
14  *  of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU Lesser Public License for more details.
16  *
17  *  You should have received a copy of the GNU Lesser Public License
18  *  along with the ZBar Bar Code Reader; if not, write to the Free
19  *  Software Foundation, Inc., 51 Franklin St, Fifth Floor,
20  *  Boston, MA  02110-1301  USA
21  *
22  *  http://sourceforge.net/projects/zbarw
23  *------------------------------------------------------------------------*/
24 
25 #include "video.h"
26 #include "thread.h"
27 #include "misc.h"
28 #include <objbase.h>
29 #include <strmif.h>
30 #include <control.h>
31 #include <qedit.h>
32 #include <amvideo.h>    // include after ddraw.h has been included from any dshow header
33 #include <assert.h>
34 #include <initguid.h>
35 
36 #define ZBAR_DEFINE_STATIC_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
37         static const GUID name; \
38         static const GUID name \
39                 = { l, w1, w2, { b1, b2,  b3,  b4,  b5,  b6,  b7,  b8 } };
40 
41 // define a special guid that can be used for fourcc formats
42 // 00000000-0000-0010-8000-00AA00389B71   == MEDIASUBTYPE_FOURCC_PLACEHOLDER
43 ZBAR_DEFINE_STATIC_GUID(MEDIASUBTYPE_FOURCC_PLACEHOLDER,
44 0x00000000, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
45 
46 #define OUR_GUID_ENTRY(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
47     ZBAR_DEFINE_STATIC_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8);
48 
49 #include <uuids.h>
50 
51 DEFINE_GUID(IID_IUnknown, 0x00000000, 0x0000, 0x0000, 0xc0,0x00, 0x00,0x00,0x00,0x00,0x00,0x46);
52 DEFINE_GUID(IID_ISampleGrabber, 0x6b652fff, 0x11fe, 0x4fce, 0x92,0xad, 0x02,0x66,0xb5,0xd7,0xc7,0x8f);
53 DEFINE_GUID(IID_ISampleGrabberCB,0x0579154a,0x2b53,0x4994,0xb0,0xd0,0xe7,0x73,0x14,0x8e,0xff,0x85);
54 DEFINE_GUID(IID_IBaseFilter,0x56a86895,0x0ad4,0x11ce,0xb0,0x3a,0x00,0x20,0xaf,0x0b,0xa7,0x70);
55 DEFINE_GUID(IID_ICreateDevEnum, 0x29840822, 0x5b84, 0x11d0, 0xbd,0x3b, 0x00,0xa0,0xc9,0x11,0xce,0x86);
56 DEFINE_GUID(IID_IGraphBuilder, 0x56a868a9, 0x0ad4, 0x11ce, 0xb0,0x3a, 0x00,0x20,0xaf,0x0b,0xa7,0x70);
57 DEFINE_GUID(IID_IMediaControl, 0x56a868b1, 0x0ad4, 0x11ce, 0xb0,0x3a, 0x00,0x20,0xaf,0x0b,0xa7,0x70);
58 DEFINE_GUID(IID_IPropertyBag, 0x55272a00, 0x42cb, 0x11ce, 0x81,0x35, 0x00,0xaa,0x00,0x4b,0xb8,0x51);
59 DEFINE_GUID(IID_IAMStreamConfig, 0xc6e13340, 0x30ac, 0x11d0, 0xa1,0x8c, 0x00,0xa0,0xc9,0x11,0x89,0x56);
60 DEFINE_GUID(IID_ICaptureGraphBuilder2, 0x93e5a4e0, 0x2d50, 0x11d2, 0xab,0xfa, 0x00,0xa0,0xc9,0xc6,0xe3,0x8d);
61 DEFINE_GUID(CLSID_NullRenderer,0xc1f400a4,0x3f08,0x11d3,0x9f,0x0b,0x00,0x60,0x08,0x03,0x9e,0x37);
62 DEFINE_GUID(CLSID_SampleGrabber,0xc1f400a0,0x3f08,0x11d3,0x9f,0x0b,0x00,0x60,0x08,0x03,0x9e,0x37);
63 
64 #define BIH_FMT "%ldx%ld @%dbpp (%lx) cmp=%.4s(%08lx) res=%ldx%ld clr=%ld/%ld (%lx)"
65 #define BIH_FIELDS(bih)                                                 \
66     (bih)->biWidth, (bih)->biHeight, (bih)->biBitCount, (bih)->biSizeImage, \
67         (char*)&(bih)->biCompression, (bih)->biCompression,             \
68         (bih)->biXPelsPerMeter, (bih)->biYPelsPerMeter,                 \
69         (bih)->biClrImportant, (bih)->biClrUsed, (bih)->biSize
70 
71 // taken from Capturing an Image winapi sample
72 #define BMP_SIZE(cx, cy, bitsPerPix) \
73         ((((cx) * (bitsPerPix) + 31) / 32) * 4 * (cy))
74 
75 #define COM_SAFE_RELEASE(ppIface) \
76     if (*ppIface)\
77         (*ppIface)->lpVtbl->Release(*ppIface)
78 
79 #define CHECK_COM_ERROR(hr, msg, stmt)\
80     if (FAILED(hr))\
81     {\
82         LPSTR sysmsg = NULL; \
83         FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER \
84                       | FORMAT_MESSAGE_FROM_SYSTEM \
85                       | FORMAT_MESSAGE_IGNORE_INSERTS, \
86                       NULL, hr, \
87                       0 /* MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US) */, \
88                       (LPSTR)&sysmsg, 0, NULL); \
89         zprintf(6, "%s, hresult: 0x%lx %s", msg, hr, sysmsg); \
90         LocalFree(sysmsg); \
91         stmt; \
92     }
93 
94 static const struct uuid_desc_s {
95     const GUID *guid;
96     const char *name;
97 } known_uuids[] = {
98     #define OUR_GUID_ENTRY(m_name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8)\
99     { &m_name, #m_name },
100     #include <ksuuids.h>
101     { NULL, NULL }
102 };
103 
104 static const REFERENCE_TIME _100ns_unit = 1*1000*1000*1000 / 100;
105 
106 // format to which we convert MJPG streams
107 static const REFGUID mjpg_conversion_mediatype = &MEDIASUBTYPE_RGB32;
108 static const int mjpg_conversion_fmt = fourcc('B','G','R','4');
109 static const int mjpg_conversion_fmt_bpp = 32;
110 
111 static long grabbed_count = 0;
112 
113 // Destroy (Release) the format block for a media type.
DestroyMediaType(AM_MEDIA_TYPE * mt)114 static void DestroyMediaType(AM_MEDIA_TYPE* mt)
115 {
116     if (mt->cbFormat != 0)
117         CoTaskMemFree(mt->pbFormat);
118 
119     if (mt->pUnk)
120         IUnknown_Release(mt->pUnk);
121 }
122 
123 // Destroy the format block for a media type and free the media type
DeleteMediaType(AM_MEDIA_TYPE * mt)124 static void DeleteMediaType(AM_MEDIA_TYPE* mt)
125 {
126     if (!mt)
127         return;
128     DestroyMediaType(mt);
129     CoTaskMemFree(mt);
130 }
131 
make_fourcc_subtype(GUID * subtype,uint32_t fmt)132 static void make_fourcc_subtype(GUID* subtype, uint32_t fmt)
133 {
134     *subtype = MEDIASUBTYPE_FOURCC_PLACEHOLDER;
135     subtype->Data1 = fmt;
136 }
137 
dshow_is_fourcc_guid(REFGUID subtype)138 static int dshow_is_fourcc_guid(REFGUID subtype)
139 {
140     // make up a fourcc guid in spe
141     GUID clsid;
142     make_fourcc_subtype(&clsid, subtype->Data1);
143 
144     return IsEqualGUID(subtype, &clsid);
145 }
146 
147 /// Checks whether the given AM_MEDIA_TYPE contains a
148 /// VIDEOINFOHEADER block.
dshow_has_vih(AM_MEDIA_TYPE * mt)149 static inline int dshow_has_vih(AM_MEDIA_TYPE* mt)
150 {
151     // documentation for AM_MEDIA_TYPE emphasizes to do a thorough check
152     int isvih = (IsEqualGUID(&mt->formattype, &FORMAT_VideoInfo) &&
153                  mt->cbFormat >= sizeof(VIDEOINFOHEADER) &&
154                  mt->pbFormat);
155     return isvih;
156 }
157 
158 /// Access the BITMAPINFOHEADER in the given AM_MEDIA_TYPE,
159 /// access is non-const
dshow_access_bih(AM_MEDIA_TYPE * mt)160 static inline BITMAPINFOHEADER* dshow_access_bih(AM_MEDIA_TYPE* mt)
161 {
162     VIDEOINFOHEADER* vih = (VIDEOINFOHEADER*) mt->pbFormat;
163     return &vih->bmiHeader;
164 }
165 
166 /// Access the BITMAPINFOHEADER in the given AM_MEDIA_TYPE,
167 /// access is const
dshow_caccess_bih(const AM_MEDIA_TYPE * mt)168 static inline const BITMAPINFOHEADER* dshow_caccess_bih(const AM_MEDIA_TYPE* mt)
169 {
170     VIDEOINFOHEADER* vih = (VIDEOINFOHEADER*) mt->pbFormat;
171     return &vih->bmiHeader;
172 }
173 
174 /// Flips the image vertically copying it from srcBuf to img.
175 /** @param bpp Bits Per Pixel */
flip_vert(zbar_image_t * const img,void * const srcBuf,int bpp)176 static void flip_vert(zbar_image_t* const img, void* const srcBuf, int bpp)
177 {
178     // The formula below works only if bpp%8==0
179     long bytesPerLine = 1L * img->width * bpp / 8;
180     assert(img->datalen >= img->height * bytesPerLine);
181     void *dst = (void*)img->data;
182     void *src = srcBuf + (img->height - 1) * bytesPerLine;
183     int i, n;
184     for (i=0, n = img->height; i < n; i++) {
185         memcpy(dst, src, bytesPerLine);
186         dst += bytesPerLine;
187         src -= bytesPerLine;
188     }
189     assert(src + bytesPerLine == srcBuf);
190 }
191 
192 /// Internal format information
193 struct int_format_s
194 {
195     uint32_t fourcc;
196     /// index for IAMStreamConfig::GetStreamCaps
197     /// @note compression in bih structure may differ from one used by zbar
198     int idx_caps;
199     resolution_list_t resolutions;
200 };
201 typedef struct int_format_s int_format_t;
202 
203 struct video_state_s
204 {
205     zbar_thread_t thread;             /* capture message pump */
206     HANDLE captured;
207     HANDLE notify;                    /* capture thread status change */
208     int bi_size;                      /* size of bih */
209     BITMAPINFOHEADER* bih;            /* video format details of grabbed samples;
210                                          format might be among fourcc or BI_RGB */
211     int do_flip_bitmap;               /* whether uncompressed bitmap images are
212                                          bottom-up and need to be vertically
213                                          flipped */
214     zbar_image_t* image;              /* current capturing frame */
215 
216     IGraphBuilder* graph;             /* dshow graph manager */
217     IMediaControl* mediacontrol;      /* dshow graph control */
218     IBaseFilter* camera;              /* dshow source filter */
219     ISampleGrabber* samplegrabber;    /* dshow intermediate filter */
220     IBaseFilter* grabberbase;         /* samplegrabber's IBaseFilter interface */
221     IBaseFilter* nullrenderer;
222     ICaptureGraphBuilder2* builder;
223     IAMStreamConfig* camstreamconfig; /* dshow stream configuration interface */
224     int caps_size;                    /* length of stream config caps */
225     /// 0 terminated list of supported internal formats.
226     /** The size of this
227       * array matches the size of {@link zbar_video_s#formats} array and
228       * the consecutive entries correspond to each other, making a mapping
229       * between internal (camera) formats and zbar formats
230       * (presented to zbar processor). */
231     int_format_t *int_formats;
232     resolution_t def_resolution;    /* initial resolution read
233                                        from the camera */
234 };
235 
dshow_destroy_video_state_t(video_state_t * state)236 static void dshow_destroy_video_state_t(video_state_t* state)
237 {
238     COM_SAFE_RELEASE(&state->camstreamconfig);
239     COM_SAFE_RELEASE(&state->builder);
240     COM_SAFE_RELEASE(&state->nullrenderer);
241     COM_SAFE_RELEASE(&state->grabberbase);
242     COM_SAFE_RELEASE(&state->samplegrabber);
243     COM_SAFE_RELEASE(&state->camera);
244     COM_SAFE_RELEASE(&state->mediacontrol);
245     COM_SAFE_RELEASE(&state->graph);
246 
247     if (state->captured)
248         CloseHandle(state->captured);
249 
250     free(state->bih);
251 
252     if (state->int_formats)
253     {
254         int_format_t *fmt;
255         for (fmt = state->int_formats; !is_struct_null(fmt); fmt++) {
256             resolution_list_cleanup(&fmt->resolutions);
257         }
258     }
259     free(state->int_formats);
260 }
261 
262 /// Returns the index of the given format in formats array.
263 /** If not found, returns -1. The array is constructed like
264   * <code>vdo->formats</code>, with the terminating null. */
get_format_index(uint32_t * fmts,uint32_t fmt0)265 static int get_format_index(uint32_t* fmts, uint32_t fmt0)
266 {
267     uint32_t *fmt;
268     int i = 0;
269     for (fmt = fmts; *fmt; fmt++) {
270         if (*fmt == fmt0)
271             break;
272         i++;
273     }
274     if (*fmt)
275         return i;
276     else
277         return -1;
278 }
279 
280 /// Returns the index of the given format in internal formats array.
281 /** If not found, returns -1. The array is constructed like
282   * <code>vdo->state->int_formats</code>, with the terminating zeroed
283   * element. */
284 /** @param fmt0 A fourcc format code */
get_int_format_index(int_format_t * fmts,uint32_t fmt0)285 static int get_int_format_index(int_format_t *fmts, uint32_t fmt0)
286 {
287     int_format_t *fmt;
288     int i = 0;
289     for (fmt = fmts; !is_struct_null(fmt); fmt++) {
290         if (fmt->fourcc == fmt0)
291             break;
292         i++;
293     }
294     if (!is_struct_null(fmt))
295         return i;
296     else
297         return -1;
298 }
299 
300 /// Gets the fourcc code for the given media type.
301 /** This is necessary because of non-standard coding of video
302   * formats by Windows, for example BGR3/4 = BI_RGB . */
get_fourcc_for_mt(const AM_MEDIA_TYPE * mt)303 static uint32_t get_fourcc_for_mt(const AM_MEDIA_TYPE* mt)
304 {
305     if IsEqualGUID(&mt->subtype, &MEDIASUBTYPE_RGB24)
306         return fourcc('B','G','R','3');
307     else if IsEqualGUID(&mt->subtype, &MEDIASUBTYPE_RGB32)
308         return fourcc('B','G','R','4');
309     else if (dshow_is_fourcc_guid(&mt->subtype))
310         return mt->subtype.Data1;
311     else
312         return 0;
313 }
314 
315 /// Dumps the mapping of internal and external formats, if the debug level
316 /// is sufficient.
dump_formats(zbar_video_t * vdo)317 static void dump_formats(zbar_video_t* vdo)
318 {
319     video_state_t* state = vdo->state;
320     uint32_t *fmt;
321     int_format_t *int_fmt;
322     zprintf(8, "Detected formats: (internal) / (translated for zbar)\n");
323     fmt = vdo->formats;
324     int_fmt = state->int_formats;
325     while (*fmt) {
326         zprintf(8, "  %.4s / %.4s, resolutions: %lu\n",
327             (char*)&int_fmt->fourcc, (char*)fmt, int_fmt->resolutions.cnt);
328         fmt++; int_fmt++;
329     }
330 }
331 
332 /// Allocates a string containing given guid.
333 /** If it's among known CLSID's, its name is returned.
334   * Returned string should be freed with `free`. */
get_clsid_string(REFGUID guid)335 static char* get_clsid_string(REFGUID guid)
336 {
337     char *msg = NULL;
338 
339     // first try to determine guid name from the list
340     int i;
341     for (i = 0; known_uuids[i].name; i++)
342     {
343         if (IsEqualGUID(known_uuids[i].guid, guid))
344         {
345             msg = malloc(strlen(known_uuids[i].name) + 1);
346             strcpy(msg, known_uuids[i].name);
347             break;
348         }
349     }
350 
351     // if that failed, give the ugly string
352     if (msg == NULL)
353     {
354         LPOLESTR str = L"ERROR";
355         HRESULT hr = StringFromCLSID(guid, &str);
356         int c = WideCharToMultiByte(CP_UTF8, 0, str, -1, NULL, 0, NULL, NULL);
357         msg = malloc(c);
358         WideCharToMultiByte(CP_UTF8, 0, str, -1, msg, c, NULL, NULL);
359         if (!FAILED(hr))
360             CoTaskMemFree(str);
361     }
362 
363     return msg;
364 }
365 
366 /// Maps internal format MJPG (if found) to a format known to
367 /// zbar.
368 /** The conversion will be done by dshow mjpeg decompressor
369   * before passing the image to zbar. */
prepare_mjpg_format_mapping(zbar_video_t * vdo)370 static void prepare_mjpg_format_mapping(zbar_video_t* vdo)
371 {
372     video_state_t* state = vdo->state;
373     /// The format we will convert MJPG to
374     uint32_t fmtConv = mjpg_conversion_fmt;
375     int iMjpg = get_int_format_index(state->int_formats,
376                                      fourcc('M','J','P','G'));
377     if (iMjpg < 0)
378         return;
379     assert(vdo->formats[iMjpg] == fourcc('M','J','P','G'));
380 
381     // If we already have fmtConv, it will lead to duplicating it in
382     // external formats.
383     // We can't leave it this way, because when zbar wants to use fmtConv
384     // we must have only one internal format for that.
385     // It's better to drop MJPG then, as it's a compressed format and
386     // we prefer better quality images.
387 
388     // The index of fmtConv before mapping mjpg to fmtConv
389     int iMjpgConv = get_int_format_index(state->int_formats, fmtConv);
390 
391     if (iMjpgConv >= 0) {
392         // remove the iMjpgConv entry by moving the following entries by 1
393         int i;
394         for (i=iMjpgConv; vdo->formats[i]; i++) {
395             vdo->formats[i] = vdo->formats[i+1];
396             state->int_formats[i] = state->int_formats[i+1];
397         }
398         // The number of formats is reduced by 1 now, but to realloc just
399         // to save 2 times 4 bytes? Too much fuss.
400     }
401     else {
402         vdo->formats[iMjpg] = fmtConv;
403     }
404 }
405 
406 
407 /// sample grabber callback implementation (derived from ISampleGrabberCB)
408 typedef struct zbar_samplegrabber_cb
409 {
410     // baseclass
411     const struct ISampleGrabberCB _;
412     // COM refcount
413     ULONG refcount;
414 
415     zbar_video_t* vdo;
416 
417 } zbar_samplegrabber_cb;
418 
419 // ISampleGrabber methods (implementation below)
420 
421 HRESULT __stdcall zbar_samplegrabber_cb_QueryInterface(ISampleGrabberCB* _This, REFIID riid, void** ppvObject);
422 ULONG __stdcall zbar_samplegrabber_cb_AddRef(ISampleGrabberCB* _This);
423 ULONG __stdcall zbar_samplegrabber_cb_Release(ISampleGrabberCB* _This);
424 HRESULT __stdcall zbar_samplegrabber_cb_SampleCB(ISampleGrabberCB* _This, double sampletime, IMediaSample* sample);
425 // note: original MS version expects a long for the buffer length, wine version a LONG
426 //HRESULT __stdcall zbar_samplegrabber_cb_BufferCB(ISampleGrabberCB* _This, double sampletime, BYTE* buffer, long bufferlen);
427 HRESULT __stdcall zbar_samplegrabber_cb_BufferCB(ISampleGrabberCB* _This, double sampletime, BYTE* buffer, LONG bufferlen);
428 
429 static struct ISampleGrabberCBVtbl SampleGrabberCBVtbl =
430 {
431     &zbar_samplegrabber_cb_QueryInterface,
432     &zbar_samplegrabber_cb_AddRef,
433     &zbar_samplegrabber_cb_Release,
434     &zbar_samplegrabber_cb_SampleCB,
435     &zbar_samplegrabber_cb_BufferCB
436 };
437 
new_zbar_samplegrabber_cb(zbar_video_t * vdo)438 static zbar_samplegrabber_cb* new_zbar_samplegrabber_cb(zbar_video_t* vdo)
439 {
440     // allocate memory
441     zbar_samplegrabber_cb* o = calloc(1, sizeof(zbar_samplegrabber_cb));
442 
443     // construct parent
444     ISampleGrabberCB* base = (ISampleGrabberCB*)o;
445     base->lpVtbl = &SampleGrabberCBVtbl;
446 
447     // construct object
448     o->refcount = 1;
449     o->vdo = vdo;
450 
451     return o;
452 }
453 
delete_zbar_samplegrabber_cb(zbar_samplegrabber_cb * o)454 static void delete_zbar_samplegrabber_cb(zbar_samplegrabber_cb* o)
455 {
456     zprintf(16, "thr=%04lx\n", _zbar_thread_self());
457 
458     // no destruction necessary
459 
460     free(o);
461 }
462 
zbar_samplegrabber_cb_QueryInterface(ISampleGrabberCB * _This,REFIID riid,void ** ppvObject)463 HRESULT __stdcall zbar_samplegrabber_cb_QueryInterface(ISampleGrabberCB* _This, REFIID riid, void** ppvObject)
464 {
465     if (IsEqualIID(riid, &IID_IUnknown)            ||
466         IsEqualIID(riid, &IID_ISampleGrabberCB)    )
467     {
468         *ppvObject = _This;
469         return NOERROR;
470     }
471     else
472     {
473         *ppvObject = NULL;
474         return E_NOINTERFACE;
475     }
476 }
477 
zbar_samplegrabber_cb_AddRef(ISampleGrabberCB * _This)478 ULONG __stdcall zbar_samplegrabber_cb_AddRef(ISampleGrabberCB* _This)
479 {
480     zbar_samplegrabber_cb* This = (zbar_samplegrabber_cb*)_This;
481 
482     return ++This->refcount;
483 }
484 
zbar_samplegrabber_cb_Release(ISampleGrabberCB * _This)485 ULONG __stdcall zbar_samplegrabber_cb_Release(ISampleGrabberCB* _This)
486 {
487     zbar_samplegrabber_cb* This = (zbar_samplegrabber_cb*)_This;
488 
489     ULONG refcnt = --This->refcount;
490     if (!refcnt)
491         delete_zbar_samplegrabber_cb(This);
492 
493     return refcnt;
494 }
495 
zbar_samplegrabber_cb_SampleCB(ISampleGrabberCB * _This,double sampletime,IMediaSample * sample)496 HRESULT __stdcall zbar_samplegrabber_cb_SampleCB(ISampleGrabberCB* _This, double sampletime, IMediaSample* sample)
497 {
498     return E_NOTIMPL;
499 }
500 
501 //HRESULT __stdcall zbar_samplegrabber_cb_BufferCB(ISampleGrabberCB* _This, double sampletime, BYTE* buffer, long bufferlen)
zbar_samplegrabber_cb_BufferCB(ISampleGrabberCB * _This,double sampletime,BYTE * buffer,LONG bufferlen)502 HRESULT __stdcall zbar_samplegrabber_cb_BufferCB(ISampleGrabberCB* _This, double sampletime, BYTE* buffer, LONG bufferlen)
503 {
504     if (!buffer || !bufferlen)
505         return S_OK;
506 
507 
508     grabbed_count++;
509     zbar_samplegrabber_cb* This = (zbar_samplegrabber_cb*)_This;
510     zbar_video_t* vdo = This->vdo;
511 
512     _zbar_mutex_lock(&vdo->qlock);
513 
514     zprintf(16, "got sample no %ld: %p (%ld), thr=%04lx\n", grabbed_count,
515             buffer, bufferlen, _zbar_thread_self());
516 
517     zbar_image_t* img = vdo->state->image;
518     if (!img)
519     {
520         _zbar_mutex_lock(&vdo->qlock);
521         img = video_dq_image(vdo);
522         // note: video_dq_image() has unlocked the mutex
523     }
524     if (img)
525     {
526         zprintf(16, "copying into img: %p (srcidx: %d, data: %p, len: %ld)\n", img, img->srcidx, img->data, img->datalen);
527 
528         assert(img->datalen == bufferlen);
529 
530         // The image needs to be copied now. Usually memcpy is ok,
531         // but in case of MJPG the picture is upside-down.
532         if (vdo->state->do_flip_bitmap)
533             flip_vert(img, buffer, vdo->state->bih->biBitCount);
534         else
535             memcpy((void*)img->data, buffer, img->datalen);
536 
537         vdo->state->image = img;
538         SetEvent(vdo->state->captured);
539     }
540 
541     _zbar_mutex_unlock(&vdo->qlock);
542 
543     return S_OK;
544 }
545 
546 
dshow_capture_thread(void * arg)547 static ZTHREAD dshow_capture_thread(void* arg)
548 {
549     zbar_video_t* vdo = arg;
550     video_state_t* state = vdo->state;
551     zbar_thread_t* thr = &state->thread;
552 
553 
554     _zbar_mutex_lock(&vdo->qlock);
555 
556     _zbar_thread_init(thr);
557     zprintf(4, "spawned dshow capture thread (thr=%04lx)\n",
558             _zbar_thread_self());
559 
560     MSG msg;
561     int rc = 0;
562     while(thr->started && rc >= 0 && rc <= 1) {
563         _zbar_mutex_unlock(&vdo->qlock);
564 
565         rc = MsgWaitForMultipleObjects(1, &thr->notify, 0,
566                                        INFINITE, QS_ALLINPUT);
567         if (rc == 1)
568             while(PeekMessage(&msg, NULL, 0, 0, PM_NOYIELD | PM_REMOVE))
569                 if (rc > 0) {
570                     TranslateMessage(&msg);
571                     DispatchMessage(&msg);
572                 }
573 
574         _zbar_mutex_lock(&vdo->qlock);
575     }
576 
577 //done:
578     thr->running = 0;
579     _zbar_event_trigger(&thr->activity);
580     _zbar_mutex_unlock(&vdo->qlock);
581     return 0;
582 }
583 
dshow_nq(zbar_video_t * vdo,zbar_image_t * img)584 static int dshow_nq(zbar_video_t* vdo, zbar_image_t* img)
585 {
586     zprintf(16, "img: %p (srcidx: %d, data: %p, len: %ld), thr=%04lx\n", img, img->srcidx, img->data, img->datalen, _zbar_thread_self());
587     return video_nq_image(vdo, img);
588 }
589 
590 /// Platform dependent part of #zbar_video_next_image, which blocks
591 /// until an image is available.
592 /** Must be called with video lock held and returns
593   * with the lock released.
594   * <p>Waits for the image from `vdo->state->image`. If available,
595   * this field is nulled. Releases the lock temporarily when waiting for
596   * the `vdo->state->captured` signal. */
dshow_dq(zbar_video_t * vdo)597 static zbar_image_t* dshow_dq(zbar_video_t* vdo)
598 {
599     zbar_image_t* img = vdo->state->image;
600     if (!img) {
601         _zbar_mutex_unlock(&vdo->qlock);
602         DWORD rc = WaitForSingleObject(vdo->state->captured, INFINITE);
603             // note: until we get the lock again the grabber thread might
604             // already provide the next sample (which is fine)
605         _zbar_mutex_lock(&vdo->qlock);
606 
607         switch (rc)
608         {
609         case WAIT_OBJECT_0:
610             img = vdo->state->image;
611             break;
612         case WAIT_ABANDONED:
613             err_capture(vdo, SEV_ERROR, ZBAR_ERR_INVALID, __func__,
614                         "event handle abandoned");
615             break;
616         case WAIT_FAILED:
617             err_capture(vdo, SEV_ERROR, ZBAR_ERR_WINAPI, __func__,
618                         "Waiting for image failed");
619             break;
620         }
621     }
622 
623     zprintf(16, "img: %p (srcidx: %d, data: %p, len: %ld), thr=%04lx\n", img, img ? img->srcidx : 0, img ? img->data : NULL, img ? img->datalen : 0, _zbar_thread_self());
624 
625     vdo->state->image = NULL;
626     ResetEvent(vdo->state->captured);
627     video_unlock(vdo);
628 
629     return img;
630 }
631 
dshow_start(zbar_video_t * vdo)632 static int dshow_start(zbar_video_t* vdo)
633 {
634     video_state_t* state = vdo->state;
635     ResetEvent(state->captured);
636 
637     zprintf(16, "thr=%04lx\n", _zbar_thread_self());
638 
639     HRESULT hr = IMediaControl_Run(state->mediacontrol);
640     CHECK_COM_ERROR(hr, "couldn't start video stream", (void)0);
641     if (FAILED(hr))
642         return(err_capture(vdo, SEV_ERROR, ZBAR_ERR_INVALID, __func__,
643                            "starting video stream"));
644     return 0;
645 }
646 
dshow_stop(zbar_video_t * vdo)647 static int dshow_stop(zbar_video_t* vdo)
648 {
649     video_state_t* state = vdo->state;
650 
651     zprintf(16, "thr=%04lx\n", _zbar_thread_self());
652 
653     HRESULT hr = IMediaControl_Stop(state->mediacontrol);
654     CHECK_COM_ERROR(hr, "couldn't stop video stream", (void)0);
655     if (FAILED(hr))
656         return(err_capture(vdo, SEV_ERROR, ZBAR_ERR_INVALID, __func__,
657                            "stopping video stream"));
658 
659     _zbar_mutex_lock(&vdo->qlock);
660     if (state->image)
661         state->image = NULL;
662     SetEvent(state->captured);
663     _zbar_mutex_unlock(&vdo->qlock);
664     return 0;
665 }
666 
dshow_set_format(zbar_video_t * vdo,uint32_t fmt)667 static int dshow_set_format (zbar_video_t* vdo,
668                            uint32_t fmt)
669 {
670     int rc = 0; // return code
671 
672     const zbar_format_def_t* fmtdef = _zbar_format_lookup(fmt);
673     int fmt_ind = get_format_index(vdo->formats, fmt);
674     if (!fmtdef->format || fmt_ind < 0)
675         return(err_capture_int(vdo, SEV_ERROR, ZBAR_ERR_INVALID, __func__,
676                                "unsupported vfw format: %x", fmt));
677 
678     video_state_t* state = vdo->state;
679     int_format_t *int_fmt = &state->int_formats[fmt_ind];
680 
681 
682     // prepare media type structure as read from GetStreamCaps
683     BYTE* caps = malloc(state->caps_size);
684     if (!caps) err_capture(vdo, SEV_FATAL, ZBAR_ERR_NOMEM, __func__, "");
685     AM_MEDIA_TYPE* mt = NULL;
686     AM_MEDIA_TYPE* currentmt = NULL;  // used later, but must be here
687                                       // due to possible "goto cleanup"
688     HRESULT hr = IAMStreamConfig_GetStreamCaps(state->camstreamconfig,
689         int_fmt->idx_caps, &mt, caps);
690     free(caps);
691     CHECK_COM_ERROR(hr, "querying chosen stream caps failed", goto cleanup)
692     BITMAPINFOHEADER* bih = dshow_access_bih(mt);
693 
694     // then, adjust the format
695     if (!vdo->width || !vdo->height)
696     {
697         // video size not requested, take camera default
698         resolution_t resolution = vdo->state->def_resolution;
699         // the initial resolution may be not suitable for the current format
700         get_closest_resolution(&resolution, &int_fmt->resolutions);
701         vdo->width = resolution.cx;
702         vdo->height = resolution.cy;
703     }
704     bih->biWidth = vdo->width;
705     bih->biHeight = vdo->height;
706 
707     zprintf(4, "setting camera format: %.4s(%08x) " BIH_FMT "\n",
708             (char*)&int_fmt->fourcc, int_fmt->fourcc, BIH_FIELDS(bih));
709 
710     hr = IAMStreamConfig_SetFormat(state->camstreamconfig, mt);
711     CHECK_COM_ERROR(hr, "setting camera format failed", goto cleanup)
712 
713     // re-read format, image data size might have changed
714     hr = IAMStreamConfig_GetFormat(state->camstreamconfig, &currentmt);
715     CHECK_COM_ERROR(hr, "queried currentmt", goto cleanup);
716 
717     bih = dshow_access_bih(currentmt);
718 
719     if (get_fourcc_for_mt(currentmt) != int_fmt->fourcc)
720     {
721         rc = err_capture(vdo, SEV_ERROR, ZBAR_ERR_INVALID, __func__,
722                          "video format set ignored");
723         goto cleanup;
724     }
725 
726     vdo->format = fmt;
727     vdo->width = bih->biWidth;
728     vdo->height = bih->biHeight;
729     vdo->datalen = bih->biSizeImage;
730 
731     // datalen was set based on the internal format, but sometimes it's
732     // different from the format reported to zbar processor
733     if (vdo->formats[fmt_ind] != state->int_formats[fmt_ind].fourcc) {
734         // See prepare_mjpg_format_mapping for possible differences
735         // between internal and zbar format.
736         vdo->datalen = BMP_SIZE(vdo->width, vdo->height,
737                                 mjpg_conversion_fmt_bpp);
738     }
739 
740     zprintf(4, "set camera format: %.4s(%08x) " BIH_FMT "\n",
741             (char*)&int_fmt->fourcc, int_fmt->fourcc, BIH_FIELDS(bih));
742 
743 cleanup:
744     DeleteMediaType(mt);
745     DeleteMediaType(currentmt);
746 
747     if (FAILED(hr))
748         return err_capture(vdo, SEV_ERROR, ZBAR_ERR_INVALID, __func__,
749                            "setting dshow format failed");
750     else
751         // note: if an error happened it was already captured and reported
752         return rc;
753 }
754 
dshow_init(zbar_video_t * vdo,uint32_t fmt)755 static int dshow_init(zbar_video_t* vdo, uint32_t fmt)
756 {
757     if (dshow_set_format(vdo, fmt))
758         return -1;
759 
760 
761     HRESULT hr;
762     video_state_t* state = vdo->state;
763     int fmt_ind = get_format_index(vdo->formats, fmt);
764 
765 
766     // install sample grabber callback
767     ISampleGrabberCB* grabbercb = (ISampleGrabberCB*)new_zbar_samplegrabber_cb(vdo);
768     hr = ISampleGrabber_SetCallback(vdo->state->samplegrabber, grabbercb, 1);
769     ISampleGrabberCB_Release(grabbercb);
770     if (FAILED(hr))
771         return(err_capture(vdo, SEV_ERROR, ZBAR_ERR_BUSY, __func__,
772                            "setting capture callbacks"));
773 
774 
775 
776     // set up directshow graph
777 
778 
779     // special handling for MJPG streams:
780     // we use the stock mjpeg decompressor filter
781     if (state->int_formats[fmt_ind].fourcc == fourcc('M','J','P','G'))
782     {
783         IBaseFilter* mjpgdecompressor = NULL;
784         hr = CoCreateInstance(&CLSID_MjpegDec, NULL, CLSCTX_INPROC_SERVER, &IID_IBaseFilter, (void**)&mjpgdecompressor);
785         CHECK_COM_ERROR(hr, "failed to create mjpeg decompressor filter", (void)0)
786         if (FAILED(hr))
787         {
788             return err_capture(vdo, SEV_ERROR, ZBAR_ERR_INVALID, __func__,
789                                "failed to create mjpeg decompressor filter");
790         }
791 
792         // add mjpeg decompressor to graph
793         hr = IGraphBuilder_AddFilter(state->graph, mjpgdecompressor, L"MJPEG decompressor");
794         CHECK_COM_ERROR(hr, "adding MJPEG decompressor", goto mjpg_cleanup)
795 
796         // explicitly convert MJPG to RGB32
797         // (sample grabber will only accept this format)
798         //
799         // note: the mjpeg decompressor's output seems to be RGB32 (BGR4)
800         // by default.
801         // This fact has been a decisive factor to choosing the mapping
802         // (BGR4 [zbar input] -> MJPG [camera output]).
803         // Because zbar requests BGR4 we have to ensure that we really do
804         // provide it.
805         AM_MEDIA_TYPE conv_mt = {};
806         conv_mt.majortype = MEDIATYPE_Video;
807         conv_mt.subtype = *mjpg_conversion_mediatype;
808         conv_mt.formattype = FORMAT_VideoInfo;
809         hr = ISampleGrabber_SetMediaType(state->samplegrabber, &conv_mt);
810         CHECK_COM_ERROR(hr, "setting mjpg conversion media type", goto mjpg_cleanup)
811         // no need to destroy conv_mt
812 
813 
814         hr = ICaptureGraphBuilder2_RenderStream(state->builder, &PIN_CATEGORY_PREVIEW, &MEDIATYPE_Video, (IUnknown*)state->camera, NULL, mjpgdecompressor);
815         CHECK_COM_ERROR(hr, "rendering filter graph 1", goto mjpg_cleanup)
816 
817         hr = ICaptureGraphBuilder2_RenderStream(state->builder, NULL, &MEDIATYPE_Video, (IUnknown*)mjpgdecompressor, state->grabberbase, state->nullrenderer);
818         CHECK_COM_ERROR(hr, "rendering filter graph 2", goto mjpg_cleanup)
819 
820 mjpg_cleanup:
821         IBaseFilter_Release(mjpgdecompressor);
822 
823         if (FAILED(hr))
824         {
825             return err_capture(vdo, SEV_ERROR, ZBAR_ERR_INVALID, __func__,
826                                "rendering filter graph failed");
827         }
828     }
829     else
830     {
831         // ensure (again) that the sample grabber gets only
832         // video media types with VIDEOINFOHEADER
833         AM_MEDIA_TYPE grab_mt = {};
834         grab_mt.majortype = MEDIATYPE_Video;
835         grab_mt.formattype = FORMAT_VideoInfo;
836         hr = ISampleGrabber_SetMediaType(state->samplegrabber, &grab_mt);
837         CHECK_COM_ERROR(hr, "setting sample grabber media type", goto render_cleanup)
838         // no need to destroy grab_mt
839 
840 
841         hr = ICaptureGraphBuilder2_RenderStream(state->builder, &PIN_CATEGORY_PREVIEW, &MEDIATYPE_Video, (IUnknown*)state->camera, state->grabberbase, state->nullrenderer);
842         CHECK_COM_ERROR(hr, "rendering filter graph", goto render_cleanup)
843 
844 render_cleanup:
845         if (FAILED(hr))
846         {
847             return err_capture(vdo, SEV_ERROR, ZBAR_ERR_INVALID, __func__,
848                                "rendering filter graph failed");
849         }
850     }
851 
852     // scope: after the graph is built (and the pins connected) we query the
853     // final media type from sample grabber's input pin;
854     {
855         AM_MEDIA_TYPE input_mt = {};
856 
857         hr = ISampleGrabber_GetConnectedMediaType(state->samplegrabber, &input_mt);
858         CHECK_COM_ERROR(hr, "couldn't query input media type from sample grabber", goto cleanup1)
859 
860 
861         assert(dshow_has_vih(&input_mt));
862         const BITMAPINFOHEADER* bih = dshow_caccess_bih(&input_mt);
863 
864         // adjust state->bih
865         state->bi_size = bih->biSize;
866         state->bih = realloc(state->bih, state->bi_size);
867         memcpy(state->bih, bih, state->bi_size);
868 
869         if (bih->biCompression == BI_RGB)
870         {
871             // check bitmap orientation for native BI_RGB:
872             // positive biHeight: bottom-up bitmap -> flip
873             // negative biHeight: top-down bitmap
874             state->do_flip_bitmap = (bih->biHeight > 0);
875         }
876 
877 cleanup1:
878         DestroyMediaType(&input_mt);
879 
880         if (FAILED(hr))
881             return -1;
882     }
883 
884     // query camera stream parameters;
885     REFERENCE_TIME avgtime_perframe = 0;
886 
887     // scope: query avgtime_perframe from camera
888     {
889         AM_MEDIA_TYPE* currentmt = NULL;
890         hr = IAMStreamConfig_GetFormat(state->camstreamconfig, &currentmt);
891         CHECK_COM_ERROR(hr, "querying camera format failed", goto cleanup2)
892 
893         assert(dshow_has_vih(currentmt));
894         VIDEOINFOHEADER* vih = (VIDEOINFOHEADER*) currentmt->pbFormat;
895         avgtime_perframe = vih->AvgTimePerFrame;
896 
897 
898 cleanup2:
899         DeleteMediaType(currentmt);
900     }
901 
902     // directshow keeps ownership of the image passed to the callback,
903     // hence, keeping a pointer to the original data (iomode VIDEO_MMAP) is a bad idea
904     // in a multi-threaded environment.
905     // real case szenario on a windows 7 tablet with intel atom:
906     // with vdo->iomode=VIDEO_MMAP and vdo->num_images=1:
907     // 1) directshow graph provides a sample, sets the data pointer of vdo->state->img
908     // 2) dshow_dq (called from proc_video_thread()/zbar_video_next_image()) fetches the image and makes a copy of it
909     // 3) directshow graph provides the next sample, sets the data pointer of vdo->state->img
910     // 4) dshow_nq (called from ) resets the data pointer of vdo->state->img (nullptr)
911     // 5) dshow_dq returns vdo->state->img without data (nullptr)
912     //
913     // now, we could deal with this special case, but zbar_video_next_image() makes a copy of the sample anyway when
914     // vdo->num_images==1 (thus VIDEO_MMAP won't save us anything); therefore rather use image buffers provided
915     // by zbar (see video_init_images())
916     vdo->iomode = VIDEO_USERPTR;
917     // keep zbar's default
918     //vdo->num_images = ZBAR_VIDEO_IMAGES_MAX;
919 
920     // note: vdo->format and vdo->datalen have been set accordingly in dshow_set_format()
921 
922     zprintf(3, "initialized video capture: %.4s(%08x), %"PRId64" frames/s\n",
923             (char*)&fmt, fmt, _100ns_unit / avgtime_perframe);
924 
925 
926     return 0;
927 }
928 
dshow_cleanup(zbar_video_t * vdo)929 static int dshow_cleanup (zbar_video_t* vdo)
930 {
931     zprintf(16, "thr=%04lx\n", _zbar_thread_self());
932 
933     /* close open device */
934     video_state_t* state = vdo->state;
935 
936     _zbar_thread_stop(&state->thread, &vdo->qlock);
937 
938     dshow_destroy_video_state_t(state);
939     free(state);
940     vdo->state = NULL;
941 
942     CoUninitialize();
943 
944     return 0;
945 }
946 
dshow_determine_formats(zbar_video_t * vdo)947 static int dshow_determine_formats(zbar_video_t* vdo)
948 {
949     video_state_t* state = vdo->state;
950 
951     // collect formats
952     int resolutions;
953     HRESULT hr = IAMStreamConfig_GetNumberOfCapabilities(
954         state->camstreamconfig, &resolutions, &state->caps_size);
955     CHECK_COM_ERROR(hr, "couldn't query camera capabilities", return -1)
956 
957     zprintf(6, "number of formats/resolutions supported by the camera as reported by directshow: %d\n", resolutions);
958     zprintf(6, "list of those with a VIDEOINFOHEADER:\n");
959 
960     vdo->formats = calloc(resolutions+1, sizeof(uint32_t));
961     state->int_formats = calloc(resolutions+1, sizeof(int_format_t));
962 
963     // this is actually a VIDEO_STREAM_CONFIG_CAPS structure, which is mostly deprecated anyway,
964     // so we just reserve enough buffer but treat it as opaque otherwise
965     BYTE* caps = malloc(state->caps_size);
966     int n = 0, i;
967     for (i = 0; i < resolutions; ++i)
968     {
969         AM_MEDIA_TYPE* mt;
970         HRESULT hr = IAMStreamConfig_GetStreamCaps(state->camstreamconfig, i, &mt, caps);
971         CHECK_COM_ERROR(hr, "querying stream capability failed", continue)
972         int is_supported = 0;
973 
974         if (dshow_has_vih(mt))
975         {
976             const BITMAPINFOHEADER* bih = dshow_caccess_bih(mt);
977 
978             zprintf(6, BIH_FMT "\n",
979                     BIH_FIELDS(bih));
980             uint32_t fmt = get_fourcc_for_mt(mt);
981             // This is actually a check if the format is recognized.
982             // TODO: Check if the format is really supported by zbar.
983             is_supported = (fmt != 0);
984 
985             if (is_supported)
986             {
987                 // first search for existing fourcc format
988                 int j;
989                 for (j = 0; j < n; ++j)
990                 {
991                     if (state->int_formats[i].fourcc == fmt)
992                         break;
993                 }
994                 // push back if not found
995                 if (j == n)
996                 {
997                     state->int_formats[n].fourcc = fmt;
998                     state->int_formats[n].idx_caps = i;
999                     resolution_list_init(&state->int_formats[n].resolutions);
1000                     vdo->formats[n] = fmt;
1001                     ++n;
1002                 }
1003                 resolution_t resolution = { bih->biWidth, bih->biHeight };
1004                 resolution_list_add(&state->int_formats[j].resolutions,
1005                                     &resolution);
1006             }
1007         }
1008         // note: other format types could be possible, e.g. VIDEOINFOHEADER2 ...
1009 
1010         if (!is_supported)
1011         {
1012             char *formattype = get_clsid_string(&mt->formattype);
1013             char *subtype = get_clsid_string(&mt->subtype);
1014             zprintf(6, "unsupported format: %s / %s\n",
1015                 formattype, subtype);
1016             free(formattype);
1017             free(subtype);
1018         }
1019 
1020         DeleteMediaType(mt);
1021     }
1022     free(caps);
1023 
1024     zprintf(6, "number of supported fourcc formats: %d\n", n);
1025 
1026     vdo->formats = realloc(vdo->formats, (n + 1) * sizeof(uint32_t));
1027     state->int_formats = realloc(state->int_formats,
1028                                  (n + 1) * sizeof(int_format_t));
1029     prepare_mjpg_format_mapping(vdo);
1030     dump_formats(vdo);
1031 
1032     if (n == 0)
1033         return -1;
1034 
1035     return 0;
1036 }
1037 
dshow_probe(zbar_video_t * vdo)1038 static int dshow_probe(zbar_video_t* vdo)
1039 {
1040     if (dshow_determine_formats(vdo))
1041         return -1;
1042 
1043 
1044     video_state_t* state = vdo->state;
1045 
1046 
1047     // query current format
1048 
1049     AM_MEDIA_TYPE* currentmt;
1050     HRESULT hr = IAMStreamConfig_GetFormat(state->camstreamconfig, &currentmt);
1051     CHECK_COM_ERROR(hr, "couldn't query current camera format", return -1)
1052 
1053     if (!dshow_has_vih(currentmt))
1054     {
1055         char *formattype = get_clsid_string(&currentmt->formattype);
1056         char *subtype = get_clsid_string(&currentmt->subtype);
1057         zprintf(1, "encountered unsupported initial format, "
1058                    "no VIDEOINFOHEADER video format type: %s / %s\n",
1059                 formattype, subtype);
1060         free(formattype);
1061         free(subtype);
1062 
1063         // if we cannot read the current resolution, we need sane defaults
1064         vdo->state->def_resolution.cx = 640;
1065         vdo->state->def_resolution.cy = 480;
1066     }
1067     else
1068     {
1069         const BITMAPINFOHEADER* current_bih = dshow_caccess_bih(currentmt);
1070         assert(current_bih);
1071         zprintf(3, "initial format: %.4s(%08lx) " BIH_FMT "\n",
1072                (char*)&current_bih->biCompression, current_bih->biCompression,
1073                 BIH_FIELDS(current_bih));
1074 
1075         // store initial size
1076         vdo->state->def_resolution.cx = current_bih->biWidth;
1077         vdo->state->def_resolution.cy = current_bih->biHeight;
1078     }
1079 
1080     DeleteMediaType(currentmt);
1081 
1082 
1083     vdo->intf = VIDEO_DSHOW;
1084     vdo->init = dshow_init;
1085     vdo->start = dshow_start;
1086     vdo->stop = dshow_stop;
1087     vdo->cleanup = dshow_cleanup;
1088     vdo->nq = dshow_nq;
1089     vdo->dq = dshow_dq;
1090 
1091     return 0;
1092 }
1093 
1094 // search camera by index or by device path
dshow_search_camera(const char * dev)1095 static IBaseFilter* dshow_search_camera(const char* dev)
1096 {
1097     IBaseFilter* camera = NULL;
1098     ICreateDevEnum* devenumerator = NULL;
1099     IEnumMoniker* enummoniker = NULL;
1100 
1101     int reqid = -1;
1102     if ((!strncmp(dev, "/dev/video", 10) ||
1103         !strncmp(dev, "\\dev\\video", 10)) &&
1104        dev[10] >= '0' && dev[10] <= '9' && !dev[11])
1105         reqid = dev[10] - '0';
1106     else if (strlen(dev) == 1 &&
1107             dev[0] >= '0' && dev[0] <= '9')
1108         reqid = dev[0] - '0';
1109 
1110     zprintf(6, "searching for camera (#%d): %s\n", reqid, dev);
1111 
1112     HRESULT hr = CoCreateInstance(&CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, &IID_ICreateDevEnum, (void**)&devenumerator);
1113     CHECK_COM_ERROR(hr, "failed to create system device enumerator", goto done)
1114 
1115     hr = ICreateDevEnum_CreateClassEnumerator(devenumerator, &CLSID_VideoInputDeviceCategory, &enummoniker, 0);
1116     CHECK_COM_ERROR(hr, "failed to create enumerator moniker", goto done)
1117 
1118     if (hr != S_OK)
1119     {
1120         zprintf(6, "no video devices available");
1121         goto done;
1122     }
1123 
1124 
1125     // turn device name (the GUID) from char to wide char
1126     BSTR wdev = SysAllocStringLen(NULL, strlen(dev));
1127     if (!wdev)
1128         goto done;
1129     MultiByteToWideChar(CP_UTF8, 0, dev, -1, wdev, strlen(dev) + 1);
1130 
1131     // Go through and find capture device
1132     int docontinue;
1133     int devid;
1134     for (devid = 0, docontinue = 1; docontinue; ++devid)
1135     {
1136         IMoniker* moniker = NULL;
1137         IPropertyBag* propbag = NULL;
1138         VARIANT devpath_variant;
1139         VARIANT friendlyname_variant;
1140 
1141         hr = IEnumMoniker_Next(enummoniker, 1, &moniker, NULL);
1142         // end of monikers
1143         if (hr != S_OK)
1144             break;
1145 
1146         VariantInit(&devpath_variant);
1147         VariantInit(&friendlyname_variant);
1148 
1149         hr = IMoniker_BindToStorage(moniker, NULL, NULL, &IID_IPropertyBag, (void**)&propbag);
1150         CHECK_COM_ERROR(hr, "failed to get property bag from moniker", goto breakout)
1151 
1152         hr = IPropertyBag_Read(propbag, L"DevicePath", &devpath_variant, NULL);
1153         CHECK_COM_ERROR(hr, "failed to read DevicePath from camera device", goto breakout)
1154         hr = IPropertyBag_Read(propbag, L"FriendlyName", &friendlyname_variant, NULL);
1155         CHECK_COM_ERROR(hr, "failed to read FriendlyName from camera device", goto breakout)
1156 
1157         if ((reqid >= 0)
1158            ? devid == reqid
1159            : !wcscmp(wdev, V_BSTR(&devpath_variant)))
1160         {
1161             // create camera from moniker
1162             hr = IMoniker_BindToObject(moniker, NULL, NULL, &IID_IBaseFilter, (void**)&camera);
1163             CHECK_COM_ERROR(hr, "failed to get camera device", goto breakout)
1164         }
1165 
1166         if (camera)
1167         {
1168             zwprintf(1, L"using camera #%d: %s (%s)\n", devid, V_BSTR(&friendlyname_variant), V_BSTR(&devpath_variant));
1169             goto breakout;
1170         }
1171         else
1172             goto cleanup;
1173 
1174 
1175 breakout:
1176         docontinue = 0;
1177 cleanup:
1178         VariantClear(&friendlyname_variant);
1179         VariantClear(&devpath_variant);
1180         COM_SAFE_RELEASE(&propbag);
1181         COM_SAFE_RELEASE(&moniker);
1182     }
1183     SysFreeString(wdev);
1184 
1185 done:
1186     COM_SAFE_RELEASE(&enummoniker);
1187     COM_SAFE_RELEASE(&devenumerator);
1188 
1189     if (!camera)
1190         zprintf(6, "no camera found\n");
1191 
1192     return camera;
1193 }
1194 
_zbar_video_open(zbar_video_t * vdo,const char * dev)1195 int _zbar_video_open (zbar_video_t* vdo, const char* dev)
1196 {
1197     // assume failure
1198     int ret = -1;
1199 
1200 
1201     video_state_t* state;
1202     state = vdo->state = calloc(1, sizeof(video_state_t));
1203 
1204     state->camera = dshow_search_camera(dev);
1205     if (!state->camera)
1206         goto done;
1207 
1208 
1209     if (!state->captured)
1210         // create manual reset event
1211         state->captured = CreateEvent(NULL, TRUE, FALSE, NULL);
1212     else
1213         ResetEvent(state->captured);
1214 
1215     if (_zbar_thread_start(&state->thread, dshow_capture_thread, vdo, NULL))
1216         return -1;
1217 
1218 
1219     // create filter graph instance
1220     HRESULT hr = CoCreateInstance(&CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, &IID_IGraphBuilder, (void**)&state->graph);
1221     CHECK_COM_ERROR(hr, "graph builder creation", goto done)
1222 
1223     // query media control from filter graph
1224     hr = IGraphBuilder_QueryInterface(state->graph, &IID_IMediaControl, (void**)&state->mediacontrol);
1225     CHECK_COM_ERROR(hr, "querying media control", goto done)
1226 
1227     // create sample grabber instance
1228     hr = CoCreateInstance(&CLSID_SampleGrabber, NULL, CLSCTX_INPROC_SERVER, &IID_ISampleGrabber, (void**)&state->samplegrabber);
1229     CHECK_COM_ERROR(hr, "samplegrabber creation", goto done)
1230 
1231     // query base filter interface from sample grabber
1232     hr = ISampleGrabber_QueryInterface(state->samplegrabber, &IID_IBaseFilter, (void**)&state->grabberbase);
1233     CHECK_COM_ERROR(hr, "grabberbase query", goto done)
1234 
1235     // capture graph without preview window
1236     hr = CoCreateInstance(&CLSID_NullRenderer, NULL, CLSCTX_INPROC_SERVER, &IID_IBaseFilter, (void**)&state->nullrenderer);
1237     CHECK_COM_ERROR(hr, "null renderer creation", goto done)
1238 
1239 
1240     // add camera to graph
1241     hr = IGraphBuilder_AddFilter(state->graph, state->camera, L"Camera");
1242     CHECK_COM_ERROR(hr, "adding camera", goto done)
1243 
1244     // add sample grabber to graph
1245     hr = IGraphBuilder_AddFilter(state->graph, state->grabberbase, L"Sample Grabber");
1246     CHECK_COM_ERROR(hr, "adding samplegrabber", goto done)
1247 
1248     // add nullrenderer to graph
1249     hr = IGraphBuilder_AddFilter(state->graph, state->nullrenderer, L"Null Renderer");
1250     CHECK_COM_ERROR(hr, "adding null renderer", goto done)
1251 
1252 
1253     // Create the Capture Graph Builder.
1254     hr = CoCreateInstance(&CLSID_CaptureGraphBuilder2, NULL, CLSCTX_INPROC_SERVER, &IID_ICaptureGraphBuilder2, (void**)&state->builder);
1255     CHECK_COM_ERROR(hr, "capturegraph builder creation", goto done)
1256 
1257     // tell graph builder about the filter graph
1258     hr = ICaptureGraphBuilder2_SetFiltergraph(state->builder, state->graph);
1259     CHECK_COM_ERROR(hr, "setting filtergraph", goto done)
1260 
1261     // TODO:
1262     // finding the streamconfig interface on the camera's preview output pin (specifying PIN_CATEGORY_PREVIEW, MEDIATYPE_Video)
1263     // should work according to the documentation but it doesn't.
1264     // Because devices may have separate pins for capture and preview or have a video port pin (PIN_CATEGORY_VIDEOPORT)
1265     // instead of a preview pin, I do hope that we get the streamconfig interface for the correct pin
1266     hr = ICaptureGraphBuilder2_FindInterface(state->builder, &LOOK_DOWNSTREAM_ONLY, NULL, state->camera, &IID_IAMStreamConfig, (void**)&state->camstreamconfig);
1267     CHECK_COM_ERROR(hr, "querying camera's streamconfig interface", goto done)
1268 
1269 
1270     if (dshow_probe(vdo))
1271         goto done;
1272 
1273 
1274     // success
1275     ret = 0;
1276 
1277 done:
1278     if (ret)
1279     {
1280         if (state->camera)
1281             _zbar_thread_stop(&state->thread, NULL);
1282         dshow_destroy_video_state_t(state);
1283         free(state);
1284         vdo->state = NULL;
1285         CoUninitialize();
1286 
1287         return err_capture_str(vdo, SEV_ERROR, ZBAR_ERR_INVALID, __func__,
1288                                "failed to connect to camera '%s'", dev);
1289     }
1290 
1291     return ret;
1292 }
1293