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, ¤tmt);
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, ¤tmt);
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, ¤tmt);
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(¤tmt->formattype);
1056 char *subtype = get_clsid_string(¤tmt->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*)¤t_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