GCC Code Coverage Report


Directory: src/gate/
File: src/gate/io/videosources.c
Date: 2026-09-21 06:26:40
Exec Total Coverage
Lines: 0 83 0.0%
Functions: 0 9 0.0%
Branches: 0 52 0.0%

Line Branch Exec Source
1 /* GATE PROJECT LICENSE:
2 +----------------------------------------------------------------------------+
3 | Copyright (c) 2018-2026, Stefan Meislinger <sm@opengate.at> |
4 | All rights reserved. |
5 | |
6 | Redistribution and use in source and binary forms, with or without |
7 | modification, are permitted provided that the following conditions are met:|
8 | |
9 | 1. Redistributions of source code must retain the above copyright notice, |
10 | this list of conditions and the following disclaimer. |
11 | 2. Redistributions in binary form must reproduce the above copyright |
12 | notice, this list of conditions and the following disclaimer in the |
13 | documentation and/or other materials provided with the distribution. |
14 | |
15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"|
16 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
17 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
18 | ARE DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
19 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
20 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
21 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
22 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
23 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
24 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
25 | THE POSSIBILITY OF SUCH DAMAGE. |
26 +----------------------------------------------------------------------------+
27 */
28 #include "gate/io/videosources.h"
29 #include "gate/results.h"
30 #include "gate/queues.h"
31 #include "gate/threading.h"
32 #include "gate/debugging.h"
33 #include "gate/mathematics.h"
34 #include "gate/synchronization.h"
35 #include "gate/platforms.h"
36 #include "gate/hashes.h"
37 #include "gate/graphics/jpegimages.h"
38
39 #if defined(GATE_SYS_WIN)
40 # if defined(GATE_SYS_WINSTORE) || defined(GATE_SYS_WIN16)
41 # define GATE_IO_VIDEO_NO_IMPL
42 # else
43 # define GATE_IO_VIDEO_WINAPI
44 # endif
45 #elif defined(GATE_SYS_ANDROID)
46 # define GATE_IO_VIDEO_ANDROID
47 #elif defined(GATE_SYS_LINUX) && defined(HAVE_LIBV4L2_HEADER)
48 # define GATE_IO_VIDEO_V4L
49 #else
50 # define GATE_IO_VIDEO_NO_IMPL
51 #endif
52
53
54 static gate_result_t mjpeg_export(gate_video_frame_t* target_frame, void const* data, gate_size_t data_length)
55 {
56 gate_result_t ret = GATE_RESULT_FAILED;
57 gate_memstream_impl_t strm_impl;
58 gate_memstream_t* strm = gate_memstream_create_static_unmanaged_readonly(&strm_impl, data, data_length, data_length);
59 if (strm)
60 {
61 ret = gate_jpegimage_load((gate_stream_t*)strm, &target_frame->image, 0);
62 gate_object_release(strm);
63 }
64 return ret;
65 }
66
67 gate_hash_code_t gate_video_format_hash(gate_video_format_t const* format)
68 {
69 gate_hash_generator_context_t gen;
70
71 gate_hash_generator_init(&gen);
72
73 gate_hash_generator_update(&gen, &format->encoding_id, sizeof(format->encoding_id));
74 gate_hash_generator_update(&gen, &format->compression, sizeof(format->compression));
75 gate_hash_generator_update(&gen, &format->frames_per_second, sizeof(format->frames_per_second));
76 gate_hash_generator_update(&gen, &format->width, sizeof(format->width));
77 gate_hash_generator_update(&gen, &format->height, sizeof(format->height));
78 gate_hash_generator_update(&gen, &format->bits_per_pixel, sizeof(format->bits_per_pixel));
79
80 return gate_hash_generator_finish(&gen);
81 }
82
83
84 gate_intptr_t gate_compare_video_format(void const* item1, void const* item2)
85 {
86 gate_video_format_t const* const format1 = (gate_video_format_t const*)item1;
87 gate_video_format_t const* const format2 = (gate_video_format_t const*)item2;
88
89 if (format1->encoding_id < format2->encoding_id) return -1;
90 if (format1->encoding_id > format2->encoding_id) return 1;
91
92 if (format1->compression < format2->compression) return -1;
93 if (format1->compression > format2->compression) return 1;
94
95 if (format1->frames_per_second < format2->frames_per_second) return -1;
96 if (format1->frames_per_second > format2->frames_per_second) return 1;
97
98 if (format1->width < format2->width) return -1;
99 if (format1->width > format2->width) return 1;
100
101 if (format1->height < format2->height) return -1;
102 if (format1->height > format2->height) return 1;
103
104 if (format1->bits_per_pixel < format2->bits_per_pixel) return -1;
105 if (format1->bits_per_pixel > format2->bits_per_pixel) return 1;
106
107 return 0;
108 }
109
110 gate_video_format_t const* gate_video_format_choose(
111 gate_video_format_t const* formats, gate_size_t format_count,
112 gate_uint32_t const* desired_width, gate_uint32_t const* desired_height,
113 gate_uint32_t const* desired_bit_per_pixel, gate_real32_t const* desired_fps)
114 {
115 gate_video_format_t const* ret = NULL;
116 gate_video_format_t const* format;
117 gate_size_t index;
118 gate_uint32_t scores[1024]; /* smaller is better */
119 gate_uint32_t qualities[1024]; /* greater is better */
120 gate_int32_t diff;
121 gate_uint32_t best_score = 0xffffffff;
122 gate_uint32_t best_quality = 0;
123
124 GATE_UNUSED_ARG(desired_fps);
125
126 if (format_count > 1024)
127 {
128 format_count = 1024;
129 }
130 for (index = 0; index != format_count; ++index)
131 {
132 format = &formats[index];
133 scores[index] = 0;
134 qualities[index] = format->width * format->height * format->bits_per_pixel * (gate_uint32_t)format->frames_per_second;
135 if (desired_width != NULL)
136 {
137 diff = (gate_int32_t)*desired_width - (gate_int32_t)format->width;
138 scores[index] += (gate_uint32_t)gate_math_abs_i32(diff);
139 }
140 if (desired_height != NULL)
141 {
142 diff = (gate_int32_t)*desired_height - (gate_int32_t)format->height;
143 scores[index] += (gate_uint32_t)gate_math_abs_i32(diff);
144 }
145 if (desired_bit_per_pixel != NULL)
146 {
147 diff = (gate_int32_t)*desired_bit_per_pixel - (gate_int32_t)format->bits_per_pixel;
148 scores[index] += (gate_uint32_t)gate_math_abs_i32(diff);
149 }
150 if (desired_width != NULL)
151 {
152 diff = (gate_int32_t)*desired_width - (gate_int32_t)format->width;
153 scores[index] += (gate_uint32_t)gate_math_abs_i32(diff);
154 }
155 }
156 for (index = 0; index != format_count; ++index)
157 {
158 if ((scores[index] < best_score) ||
159 ((scores[index] == best_score) && (qualities[index] > best_quality))
160 )
161 {
162 ret = &formats[index];
163 best_score = scores[index];
164 best_quality = qualities[index];
165 }
166 }
167 return ret;
168 }
169
170
171 void gate_video_frame_init(gate_video_frame_t* frame, gate_video_format_t const* format)
172 {
173 gate_mem_clear(frame, sizeof(gate_video_frame_t));
174 if (format != NULL)
175 {
176 gate_mem_copy(&frame->format, format, sizeof(gate_video_format_t));
177 }
178 }
179
180 gate_video_frame_t* gate_video_frame_copy(gate_video_frame_t* dest, gate_video_frame_t const* source)
181 {
182 gate_video_frame_t* ret = NULL;
183 if (dest)
184 {
185 gate_mem_clear(dest, sizeof(gate_video_frame_t));
186 gate_mem_copy(&dest->format, &source->format, sizeof(gate_video_frame_t));
187 dest->record_time = source->record_time;
188 if (NULL != gate_rasterimage_copy(&dest->image, &source->image))
189 {
190 ret = dest;
191 }
192 }
193 return ret;
194 }
195
196
197 void gate_video_frame_release(gate_video_frame_t* frame)
198 {
199 gate_rasterimage_release(&frame->image);
200 gate_mem_clear(frame, sizeof(gate_video_frame_t));
201 }
202
203
204 #if defined(GATE_IO_VIDEO_WINAPI)
205
206 #if defined(GATE_SYS_WINSTORE)
207
208 # include <windows.media.capture.h>
209 # include <windows.devices.enumeration.h>
210
211 class MediaCaptureDriver : public IVideoCaptureDriver
212 {
213 public:
214 static char const* const DriverName;
215 static char const* const DriverDescription;
216
217 MediaCaptureDriver();
218
219 virtual ~MediaCaptureDriver();
220
221 virtual String Name();
222 virtual String Description();
223 virtual Array<VideoDeviceInfo> getDevices();
224
225 virtual void open(VideoDeviceInfo const& info);
226 virtual void close();
227 virtual bool isOpened();
228
229 virtual void getFormats(VideoFormatList& supportedFormats);
230
231 virtual void start(VideoFormat& format, IVideoCaptureSink* pEventHandler);
232 virtual void stop();
233 virtual bool isStarted();
234
235 private:
236 };
237
238 char const* const MediaCaptureDriver::DriverName = "MediaCapture";
239 char const* const MediaCaptureDriver::DriverDescription = "UWP Media Capture";
240
241 MediaCaptureDriver::MediaCaptureDriver()
242 {
243 }
244 MediaCaptureDriver::~MediaCaptureDriver()
245 {
246 }
247 String MediaCaptureDriver::Name()
248 {
249 return DriverName;
250 }
251 String MediaCaptureDriver::Description()
252 {
253 return DriverDescription;
254 }
255 Array<VideoDeviceInfo> MediaCaptureDriver::getDevices()
256 {
257 ComPtr<ABI::Windows::Devices::Enumeration::IDeviceInformationStatics> devInfo;
258 winrt::ClassFactory::getClass(RuntimeClass_Windows_Devices_Enumeration_DeviceInformation, devInfo);
259
260 typedef ABI::Windows::Foundation::IAsyncOperation<ABI::Windows::Devices::Enumeration::DeviceInformationCollection*> async_op_devinfo_t;
261 ComPtr<async_op_devinfo_t> devInfoResult;
262 devInfo->FindAllAsyncDeviceClass(ABI::Windows::Devices::Enumeration::DeviceClass_VideoCapture, devInfoResult.address());
263
264 winrt::await_operation_completion(devInfoResult.get());
265
266 ABI::Windows::Foundation::Collections::IVectorView<ABI::Windows::Devices::Enumeration::DeviceInformation*>* results = 0;
267 HRESULT hr = devInfoResult->GetResults(&results);
268
269
270 /*
271 winrt::ActivationFactory factoryMediaCaptureSettings(RuntimeClass_Windows_Media_Capture_MediaCaptureInitializationSettings);
272 ComPtr<ABI::Windows::Media::Capture::IMediaCaptureInitializationSettings> settings;
273 factoryMediaCaptureSettings.activateInstance(settings);
274 settings->put_VideoDeviceId();
275
276 ActivationFactory factoryMediaCapture(RuntimeClass_Windows_Media_Capture_MediaCapture);
277 ComPtr<ABI::Windows::Media::Capture::IMediaCapture> mediaCapture;
278 factoryMediaCapture.activateInstance(mediaCapture);
279
280 //mediaCapture->InitializeWithSettingsAsync
281 */
282
283 Array<VideoDeviceInfo> ret;
284
285 return ret;
286
287 }
288 void MediaCaptureDriver::open(VideoDeviceInfo const& info)
289 {
290
291 }
292 void MediaCaptureDriver::close()
293 {
294 }
295 bool MediaCaptureDriver::isOpened()
296 {
297 return false;
298 }
299 void MediaCaptureDriver::getFormats(VideoFormatList& supportedFormats)
300 {
301 }
302 void MediaCaptureDriver::start(VideoFormat& format, IVideoCaptureSink* pEventHandler)
303 {
304 }
305 void MediaCaptureDriver::stop()
306 {
307 }
308 bool MediaCaptureDriver::isStarted()
309 {
310 return false;
311 }
312
313
314 Map<String, String> VideoDrivers::getCaptureDrivers()
315 {
316 Map<String, String> ret;
317 ret.add(MediaCaptureDriver::DriverName, MediaCaptureDriver::DriverDescription);
318 return ret;
319 }
320 VideoCaptureDriver VideoDrivers::openCaptureDriver(StrToken name)
321 {
322 static char const* const FuncName = "VideoDrivers::openCaptureDriver";
323
324 AutoPtr<IVideoCaptureDriver> ret;
325 if (name.equals(MediaCaptureDriver::DriverName))
326 {
327 ret.reset(new MediaCaptureDriver());
328 }
329
330 if (ret.empty())
331 {
332 GATEXX_RAISE_EXCEPTION(results::NoMatch, "Failed to open video driver", 0);
333 }
334 return VideoCaptureDriver(ret);
335 }
336
337 #elif defined(GATE_SYS_WINCE)
338 # if defined(GATE_COMPILER_MSVC)
339 # define GATE_IO_VIDEOSOURCES_USE_DSHOW 1
340 # endif
341 #else
342 # if !defined(GATE_COMPILER_MSVC98) && !defined(GATE_COMPILER_GCC_MINGW) && defined(GATE_WIN32_UNICODE)
343 # define GATE_IO_VIDEOSOURCES_USE_DSHOW 1
344 # define GATE_IO_VIDEOSOURCES_USE_V4W 1
345 # else
346 # define GATE_IO_VIDEOSOURCES_USE_V4W 1
347 # endif
348
349 #endif
350
351 #if defined(GATE_IO_VIDEOSOURCES_USE_V4W)
352 #include "gate/io/platform/videosources_v4w.h"
353
354 static void gate_video_vfw_release(void* self);
355 static int gate_video_vfw_retain(void* self);
356 static char const* gate_video_vfw_get_interface_name(void* self);
357
358 static char const* gate_video_vfw_get_id(void* self);
359 static char const* gate_video_vfw_get_name(void* self);
360 static gate_intptr_t gate_video_vfw_get_handle(void* self);
361 static gate_size_t gate_video_vfw_get_supported_formats(void* self, gate_video_format_t* format_buffer, gate_size_t format_buffer_count);
362
363 static gate_result_t gate_video_vfw_open(void* self, gate_video_format_t const* format);
364 static gate_result_t gate_video_vfw_close(void* self);
365
366 static gate_result_t gate_video_vfw_read(void* self, gate_video_frame_t* frame);
367
368 static GATE_INTERFACE_VTBL(gate_video_source) gate_video_vfw_vtbl_impl;
369 static void gate_init_video_vfw_vtbl_impl()
370 {
371 if (!gate_video_vfw_vtbl_impl.get_interface_name)
372 {
373 GATE_INTERFACE_VTBL(gate_video_source) const local_vtbl =
374 {
375 &gate_video_vfw_get_interface_name,
376 &gate_video_vfw_release,
377 &gate_video_vfw_retain,
378
379 &gate_video_vfw_read,
380
381 &gate_video_vfw_get_id,
382 &gate_video_vfw_get_name,
383 &gate_video_vfw_get_handle,
384 &gate_video_vfw_get_supported_formats,
385
386 &gate_video_vfw_open,
387 &gate_video_vfw_close
388 };
389 gate_video_vfw_vtbl_impl = local_vtbl;
390 }
391 }
392
393 #define GATE_VFW_QUEUE_STATE_OFFLINE 0
394 #define GATE_VFW_QUEUE_STATE_STARTING 1
395 #define GATE_VFW_QUEUE_STATE_ONLINE 2
396 #define GATE_VFW_QUEUE_STATE_STOPPING 3
397 #define GATE_VFW_QUEUE_STATE_ERROR 4
398
399 typedef struct
400 {
401 GATE_INTERFACE_VTBL(gate_video_source) const* vtbl;
402
403 gate_atomic_int_t ref_counter;
404 char id[64];
405 char name[256];
406 gate_intptr_t handle;
407
408 gate_thread_t thread;
409 DWORD thread_id;
410 gate_atomic_int_t queue_state;
411 gate_exequeue_t queue;
412 gate_syncevent_t sync_event;
413 HANDLE hwnd;
414 gate_video_format_t default_format;
415 gate_video_format_t format;
416 gate_memoryblock_t* current_frame_data;
417 CRITICAL_SECTION current_frame_lock;
418
419 HDRAWDIB draw_dib;
420 HDC draw_dc;
421 HBITMAP draw_bitmap;
422 HGDIOBJ draw_oldbmp;
423 void* draw_buffer;
424 union
425 {
426 char draw_bitmapinfo_buffer[1024];
427 BITMAPINFO draw_bitmapinfo;
428 };
429
430
431 } gate_video_vfw_impl_t;
432
433 static void gate_video_vfw_release(void* self)
434 {
435 gate_video_vfw_impl_t* impl = (gate_video_vfw_impl_t*)self;
436 if (gate_atomic_int_dec(&impl->ref_counter) == 0)
437 {
438 impl->vtbl->close(impl);
439 gate_win32_section_delete(&impl->current_frame_lock);
440 gate_mem_dealloc(impl);
441 }
442 }
443 static int gate_video_vfw_retain(void* self)
444 {
445 gate_video_vfw_impl_t* impl = (gate_video_vfw_impl_t*)self;
446 return gate_atomic_int_inc(&impl->ref_counter);
447 }
448 static char const* gate_video_vfw_get_interface_name(void* self)
449 {
450 GATE_UNUSED_ARG(self);
451 return GATE_INTERFACE_NAME_VIDEO_SOURCE;
452 }
453
454 static char const* gate_video_vfw_get_id(void* self)
455 {
456 gate_video_vfw_impl_t* impl = (gate_video_vfw_impl_t*)self;
457 return impl->id;
458 }
459 static char const* gate_video_vfw_get_name(void* self)
460 {
461 gate_video_vfw_impl_t* impl = (gate_video_vfw_impl_t*)self;
462 return impl->name;
463 }
464 static gate_intptr_t gate_video_vfw_get_handle(void* self)
465 {
466 gate_video_vfw_impl_t* impl = (gate_video_vfw_impl_t*)self;
467 return impl->handle;
468 }
469
470 static LRESULT CALLBACK gate_video_vfw_Control(HWND hWnd, int nState)
471 {
472 GATE_UNUSED_ARG(hWnd);
473 GATE_UNUSED_ARG(nState);
474 return 0;
475 }
476 static LRESULT CALLBACK gate_video_vfw_Error(HWND hWnd, int nID, LPCTSTR lpsz)
477 {
478 GATE_UNUSED_ARG(hWnd);
479 GATE_UNUSED_ARG(nID);
480 GATE_UNUSED_ARG(lpsz);
481 return 0;
482 }
483 static LRESULT CALLBACK gate_video_vfw_Status(HWND hWnd, int nID, LPCTSTR lpsz)
484 {
485 GATE_UNUSED_ARG(hWnd);
486 GATE_UNUSED_ARG(nID);
487 GATE_UNUSED_ARG(lpsz);
488 return 0;
489 }
490 static LRESULT CALLBACK gate_video_vfw_VideoStream(HWND hWnd, LPVIDEOHDR lpVHdr)
491 {
492 gate_win32_userapi_t const* const userapi = gate_win32_userapi();
493 LONG_PTR wnd_data = 0;
494 gate_video_vfw_impl_t* impl;
495 if (userapi->UserGetWindowLongPtr)
496 {
497 wnd_data = userapi->UserGetWindowLongPtr(hWnd, GWLP_USERDATA);
498 }
499 impl = (gate_video_vfw_impl_t*)(void*)wnd_data;
500
501 if (impl != NULL)
502 {
503 gate_win32_section_enter(&impl->current_frame_lock);
504
505 do
506 {
507 if (impl->current_frame_data == NULL)
508 {
509 impl->current_frame_data = gate_memoryblock_create(lpVHdr->dwBufferLength);
510 if (impl->current_frame_data == NULL)
511 {
512 /* allocation error */
513 break;
514 }
515 }
516 else
517 {
518 if (gate_memoryblock_get_size(impl->current_frame_data) < lpVHdr->dwBytesUsed)
519 {
520 gate_object_release(impl->current_frame_data);
521 impl->current_frame_data = gate_memoryblock_create(lpVHdr->dwBufferLength);
522 if (impl->current_frame_data == NULL)
523 {
524 /* allocation error */
525 break;
526 }
527 }
528 }
529 gate_mem_copy(gate_memoryblock_get_content(impl->current_frame_data), lpVHdr->lpData, lpVHdr->dwBytesUsed);
530 } while (0);
531 gate_win32_section_leave(&impl->current_frame_lock);
532 gate_syncevent_set(&impl->sync_event);
533 }
534 return 0;
535 }
536 static LRESULT CALLBACK gate_video_vfw_WaveStream(HWND hWnd, LPWAVEHDR lpWHdr)
537 {
538 GATE_UNUSED_ARG(hWnd);
539 GATE_UNUSED_ARG(lpWHdr);
540 return 0;
541 }
542
543 #define WM_GATE_VFW_START (WM_USER + 42)
544 #define WM_GATE_VFW_STOP (WM_USER + 24)
545
546 static LRESULT gate_AVICapSM(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
547 {
548 gate_win32_userapi_t const* const userapi = gate_win32_userapi();
549 if (userapi->UserIsWindow(hwnd))
550 {
551 return userapi->UserSendMessage(hwnd, msg, wparam, lparam);
552 }
553 return 0;
554 }
555
556 static DWORD gate_capGetVideoFormatSize(HWND hwnd)
557 {
558 return ((DWORD)gate_AVICapSM(hwnd, WM_CAP_GET_VIDEOFORMAT, 0, 0L));
559 }
560
561 static DWORD gate_capGetVideoFormat(HWND hwnd, BITMAPINFO* bitmap_info, DWORD dwSize)
562 {
563 return (DWORD)gate_AVICapSM(hwnd, WM_CAP_GET_VIDEOFORMAT, (WPARAM)dwSize, (LPARAM)(LPVOID)bitmap_info);
564 }
565 static BOOL gate_capCaptureStop(HWND hwnd)
566 {
567 return (BOOL)gate_AVICapSM(hwnd, WM_CAP_STOP, (WPARAM)0, (LPARAM)0L);
568 }
569 static BOOL gate_capCaptureGetSetup(HWND hwnd, LPCAPTUREPARMS params, DWORD wSize)
570 {
571 return (BOOL)gate_AVICapSM(hwnd, WM_CAP_GET_SEQUENCE_SETUP, (WPARAM)wSize, (LPARAM)(LPVOID)params);
572 }
573 static BOOL gate_capCaptureSetSetup(HWND hwnd, LPCAPTUREPARMS params, DWORD wSize)
574 {
575 return (BOOL)gate_AVICapSM(hwnd, WM_CAP_SET_SEQUENCE_SETUP, (WPARAM)wSize, (LPARAM)(LPVOID)params);
576 }
577 static BOOL gate_capCaptureSequenceNoFile(HWND hwnd)
578 {
579 return (BOOL)gate_AVICapSM(hwnd, WM_CAP_SEQUENCE_NOFILE, (WPARAM)0, (LPARAM)0L);
580 }
581 static BOOL gate_capDriverConnect(HWND hwnd, int i)
582 {
583 return (BOOL)gate_AVICapSM(hwnd, WM_CAP_DRIVER_CONNECT, (WPARAM)i, 0L);
584 }
585 static BOOL gate_capDriverDisconnect(HWND hwnd)
586 {
587 return (BOOL)gate_AVICapSM(hwnd, WM_CAP_DRIVER_DISCONNECT, (WPARAM)0, 0L);
588 }
589 static BOOL gate_capDriverGetCaps(HWND hwnd, LPCAPDRIVERCAPS s, DWORD wSize)
590 {
591 return (BOOL)gate_AVICapSM(hwnd, WM_CAP_DRIVER_GET_CAPS, (WPARAM)(wSize), (LPARAM)(LPVOID)(LPCAPDRIVERCAPS)(s));
592 }
593 static BOOL gate_capPreviewRate(HWND hwnd, DWORD wMS)
594 {
595 return (BOOL)gate_AVICapSM(hwnd, WM_CAP_SET_PREVIEWRATE, (WPARAM)(wMS), 0);
596 }
597 static BOOL gate_capPreview(HWND hwnd, BOOL f)
598 {
599 return (BOOL)gate_AVICapSM(hwnd, WM_CAP_SET_PREVIEW, (WPARAM)(BOOL)(f), 0L);
600 }
601 static BOOL gate_capSetCallbackOnError(HWND hwnd, void* fpProc)
602 {
603 return (BOOL)gate_AVICapSM(hwnd, WM_CAP_SET_CALLBACK_ERROR, 0, (LPARAM)(LPVOID)(fpProc));
604 }
605 static BOOL gate_capSetCallbackOnStatus(HWND hwnd, void* fpProc)
606 {
607 return (BOOL)gate_AVICapSM(hwnd, WM_CAP_SET_CALLBACK_STATUS, 0, (LPARAM)(LPVOID)(fpProc));
608 }
609 static BOOL gate_capSetCallbackOnYield(HWND hwnd, void* fpProc)
610 {
611 return (BOOL)gate_AVICapSM(hwnd, WM_CAP_SET_CALLBACK_YIELD, 0, (LPARAM)(LPVOID)(fpProc));
612 }
613 static BOOL gate_capSetCallbackOnFrame(HWND hwnd, void* fpProc)
614 {
615 return (BOOL)gate_AVICapSM(hwnd, WM_CAP_SET_CALLBACK_FRAME, 0, (LPARAM)(LPVOID)(fpProc));
616 }
617 static BOOL gate_capSetCallbackOnVideoStream(HWND hwnd, void* fpProc)
618 {
619 return (BOOL)gate_AVICapSM(hwnd, WM_CAP_SET_CALLBACK_VIDEOSTREAM, 0, (LPARAM)(LPVOID)(fpProc));
620 }
621 static BOOL gate_capSetCallbackOnWaveStream(HWND hwnd, void* fpProc)
622 {
623 return (BOOL)gate_AVICapSM(hwnd, WM_CAP_SET_CALLBACK_WAVESTREAM, 0, (LPARAM)(LPVOID)(fpProc));
624 }
625 static BOOL gate_capSetCallbackOnCapControl(HWND hwnd, void* fpProc)
626 {
627 return (BOOL)gate_AVICapSM(hwnd, WM_CAP_SET_CALLBACK_CAPCONTROL, 0, (LPARAM)(LPVOID)(fpProc));
628 }
629
630
631 BITMAPINFO* get_video_format(HWND hwnd, BITMAPINFO* bitmap_info, gate_size_t buffer_size)
632 {
633 DWORD dwSize = gate_capGetVideoFormatSize(hwnd);
634 if ((dwSize == 0) || (dwSize > buffer_size))
635 {
636 return NULL;
637 }
638 dwSize = gate_capGetVideoFormat(hwnd, bitmap_info, dwSize);
639 if (dwSize == 0)
640 {
641 return NULL;
642 }
643 return bitmap_info;
644 }
645
646 static void gate_vfw_stop_capture(gate_video_vfw_impl_t* impl)
647 {
648 if (impl->hwnd != NULL)
649 {
650 gate_capCaptureStop(impl->hwnd);
651 }
652
653 if (impl->draw_bitmap != NULL)
654 {
655 if (impl->draw_oldbmp != NULL)
656 {
657 gdi.GdiSelectObject(impl->draw_dc, impl->draw_oldbmp);
658 impl->draw_oldbmp = NULL;
659 }
660 gdi.GdiDeleteObject(impl->draw_bitmap);
661 impl->draw_bitmap = NULL;
662 }
663 if (impl->draw_dc != NULL)
664 {
665 gdi.GdiDeleteDC(impl->draw_dc);
666 impl->draw_dc = NULL;
667 }
668 if (impl->draw_dib != NULL)
669 {
670 DrawDibCloseFunc(impl->draw_dib);
671 impl->draw_dib = NULL;
672 }
673 }
674
675 static gate_result_t gate_vfw_start_capture(gate_video_vfw_impl_t* impl)
676 {
677 gate_result_t result = GATE_RESULT_OK;
678 CAPTUREPARMS params;
679 BOOL success;
680 char bmp_hdr_buffer[4096];
681 BITMAPINFO* ptr_bitmap_info = (BITMAPINFO*)&bmp_hdr_buffer[0];
682 BITMAPINFO bitmap_info_32;
683
684 do
685 {
686 gate_mem_clear(&params, sizeof(params));
687
688 success = gate_capCaptureGetSetup(impl->hwnd, &params, sizeof(params));
689 if (!success) { GATE_DEBUG_TRACE("VFW capCaptureGetSetup failed"); }
690 params.fYield = TRUE;
691 params.fAbortLeftMouse = FALSE;
692 params.fAbortRightMouse = FALSE;
693 params.fCaptureAudio = FALSE;
694 params.fDisableWriteCache = FALSE;
695 params.fLimitEnabled = FALSE;
696 params.fMakeUserHitOKToCapture = FALSE;
697 params.fMCIControl = FALSE;
698
699 params.dwRequestMicroSecPerFrame = 66667;
700 params.wPercentDropForError = 80;
701 params.vKeyAbort = 0;
702 params.wNumVideoRequested = 10;
703 params.wStepCaptureAverageFrames = 5;
704 #ifndef AVSTREAMMASTER_NONE
705 # define AVSTREAMMASTER_NONE 1
706 #endif
707 params.AVStreamMaster = AVSTREAMMASTER_NONE;
708 success = gate_capCaptureSetSetup(impl->hwnd, &params, sizeof(params));
709 if (!success)
710 {
711 GATE_DEBUG_TRACE("VFW capCaptureSetSetup failed");
712 result = GATE_RESULT_FAILED;
713 break;
714 }
715
716 if (NULL == get_video_format(impl->hwnd, ptr_bitmap_info, sizeof(bmp_hdr_buffer)))
717 {
718 result = GATE_RESULT_FAILED;
719 break;
720 }
721
722 ptr_bitmap_info->bmiHeader.biWidth = impl->format.width;
723 ptr_bitmap_info->bmiHeader.biHeight = impl->format.height;
724 ptr_bitmap_info->bmiHeader.biBitCount = (WORD)impl->format.bits_per_pixel;
725 ptr_bitmap_info->bmiHeader.biPlanes = 1;
726
727 if (NULL == get_video_format(impl->hwnd, &impl->draw_bitmapinfo, sizeof(impl->draw_bitmapinfo_buffer)))
728 {
729 result = GATE_RESULT_FAILED;
730 break;
731 }
732
733 if (NULL == (impl->draw_dib = DrawDibOpenFunc()))
734 {
735 GATE_DEBUG_TRACE("VFW DrawDibOpen failed");
736 }
737
738 if (NULL == (impl->draw_dc = gdi.GdiCreateCompatibleDC(NULL)))
739 {
740 GATE_DEBUG_TRACE("VFW CreateCompatibleDC failed");
741 }
742
743 bitmap_info_32.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
744 bitmap_info_32.bmiHeader.biWidth = ptr_bitmap_info->bmiHeader.biWidth;
745 bitmap_info_32.bmiHeader.biHeight = ptr_bitmap_info->bmiHeader.biHeight;
746 bitmap_info_32.bmiHeader.biPlanes = 1;
747 bitmap_info_32.bmiHeader.biBitCount = 32;
748 bitmap_info_32.bmiHeader.biCompression = BI_RGB;
749 bitmap_info_32.bmiHeader.biSizeImage = 0;
750 bitmap_info_32.bmiHeader.biXPelsPerMeter = 0;
751 bitmap_info_32.bmiHeader.biYPelsPerMeter = 0;
752 bitmap_info_32.bmiHeader.biClrImportant = 0;
753 bitmap_info_32.bmiHeader.biClrUsed = 0;
754
755 if (NULL == (impl->draw_bitmap = gdi.GdiCreateDIBSection(
756 impl->draw_dc, &bitmap_info_32, DIB_RGB_COLORS, &impl->draw_buffer, NULL, 0)))
757 {
758 GATE_DEBUG_TRACE("VFW CreateDIBSection failed");
759 }
760 impl->draw_oldbmp = gdi.GdiSelectObject(impl->draw_dc, (HGDIOBJ)impl->draw_bitmap);
761
762 success = DrawDibBeginFunc(impl->draw_dib, impl->draw_dc,
763 ptr_bitmap_info->bmiHeader.biWidth, ptr_bitmap_info->bmiHeader.biHeight,
764 &ptr_bitmap_info->bmiHeader, 0, 0, DDF_SAME_HDC);
765 if (!success)
766 {
767 GATE_DEBUG_TRACE("VFW DrawDibBegin failed");
768 //result = GATE_RESULT_FAILED;
769 //break;
770 }
771
772 success = gate_capCaptureSequenceNoFile(impl->hwnd);
773 if (!success)
774 {
775 GATE_DEBUG_TRACE("VFW capCaptureSequenceNoFile failed");
776 result = GATE_RESULT_FAILED;
777 }
778 else
779 {
780 result = GATE_RESULT_OK;
781 }
782
783 } while (0);
784
785 if (GATE_FAILED(result))
786 {
787 gate_vfw_stop_capture(impl);
788 }
789 return result;
790 }
791
792 static gate_result_t gate_vfw_thread_code(void* param)
793 {
794 gate_result_t result = GATE_RESULT_OK;
795 gate_video_vfw_impl_t* impl = (gate_video_vfw_impl_t*)param;
796 gate_win32_userapi_t const* const userapi = gate_win32_userapi();
797 MSG msg;
798 BOOL success = TRUE;
799 LRESULT lresult;
800 CAPDRIVERCAPS caps;
801 CAPTUREPARMS params;
802 DWORD dwStyle = 0;/*WS_VISIBLE;*/
803 HWND hwndParent = userapi->UserGetDesktopWindow();
804 HWND hwnd = capCreateCaptureWindowFunc(_T("Vfw Capture Window"), dwStyle, 0, 0, 700, 500, hwndParent, 0);
805 char bitmap_info_buffer[4096];
806 BITMAPINFO* ptr_bitmap_info = (BITMAPINFO*)&bitmap_info_buffer[0];
807
808 do
809 {
810 if (hwnd == NULL)
811 {
812 break;
813 }
814
815 userapi->UserSetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)(gate_intptr_t)(void*)impl);
816
817 success = gate_capDriverConnect(hwnd, (WORD)impl->handle); /*connect window with capture source*/
818 if (!success) { GATE_DEBUG_TRACE("VFW capDriverConnect failed"); }
819
820 success = gate_capDriverGetCaps(hwnd, &caps, sizeof(caps));
821 if (!success) { GATE_DEBUG_TRACE("VFW capDriverGetCaps failed"); }
822
823 success = gate_capPreviewRate(hwnd, 67);
824 if (!success) { GATE_DEBUG_TRACE("VFW capPreviewRate failed"); }
825
826 success = gate_capPreview(hwnd, FALSE);
827 if (!success) { GATE_DEBUG_TRACE("VFW capPreview failed"); }
828
829 success = gate_capSetCallbackOnError(hwnd, &gate_video_vfw_Error);
830 if (!success) { GATE_DEBUG_TRACE("VFW capSetCallbackOnError failed"); }
831
832 success = gate_capSetCallbackOnStatus(hwnd, &gate_video_vfw_Status);
833 if (!success) { GATE_DEBUG_TRACE("VFW capSetCallbackOnStatus failed"); }
834
835 success = gate_capSetCallbackOnVideoStream(hwnd, &gate_video_vfw_VideoStream);
836 if (!success) { GATE_DEBUG_TRACE("VFW capSetCallbackOnVideoStream failed"); }
837
838 gate_mem_clear(&impl->default_format, sizeof(impl->default_format));
839 if (gate_capCaptureGetSetup(hwnd, &params, sizeof(params)))
840 {
841 impl->default_format.frames_per_second = (gate_real32_t)(((10000000 / params.dwRequestMicroSecPerFrame) + 5) / 10);
842 }
843 else
844 {
845 GATE_DEBUG_TRACE("VFW capCaptureGetSetup failed");
846 }
847 if (NULL != get_video_format(hwnd, ptr_bitmap_info, sizeof(bitmap_info_buffer)))
848 {
849 impl->default_format.bits_per_pixel = ptr_bitmap_info->bmiHeader.biBitCount;
850 impl->default_format.compression = ptr_bitmap_info->bmiHeader.biCompression;
851 impl->default_format.encoding_id = 1;
852 impl->default_format.width = ptr_bitmap_info->bmiHeader.biWidth;
853 impl->default_format.height = ptr_bitmap_info->bmiHeader.biHeight;
854 }
855 else
856 {
857 GATE_DEBUG_TRACE("VFW get_video_format failed");
858 }
859
860 impl->hwnd = hwnd;
861 impl->thread_id = gate_win32_get_thread_id();
862
863 gate_atomic_int_set(&impl->queue_state, GATE_VFW_QUEUE_STATE_ONLINE);
864 gate_syncevent_set(&impl->sync_event);
865
866 while ((success = userapi->UserGetMessage(&msg, NULL, 0, 0)) > 0)
867 {
868 if (msg.hwnd == hwnd)
869 {
870 switch (msg.message)
871 {
872 case WM_GATE_VFW_START:
873 {
874 result = gate_vfw_start_capture(impl);
875 continue;
876 }
877 case WM_GATE_VFW_STOP:
878 {
879 gate_vfw_stop_capture(impl);
880 continue;
881 }
882 }
883 }
884 lresult = userapi->UserDispatchMessage(&msg);
885 }
886 } while (0);
887
888 if (hwnd != NULL)
889 {
890 gate_capDriverDisconnect(hwnd);
891 userapi->UserDestroyWindow(hwnd);
892 impl->hwnd = NULL;
893 }
894
895 gate_win32_section_enter(&impl->current_frame_lock);
896 do
897 {
898 /* cleanup */
899 if (impl->current_frame_data != NULL)
900 {
901 gate_object_release(impl->current_frame_data);
902 impl->current_frame_data = NULL;
903 }
904 if (impl->draw_dib != NULL)
905 {
906 DrawDibEndFunc(impl->draw_dib);
907 }
908 if (impl->draw_dc != NULL)
909 {
910 gdi.GdiSelectObject(impl->draw_dc, impl->draw_oldbmp);
911 gdi.GdiDeleteObject(impl->draw_bitmap);
912 gdi.GdiDeleteDC(impl->draw_dc);
913 impl->draw_bitmap = NULL;
914 impl->draw_dc = NULL;
915 }
916 if (impl->draw_dib != NULL)
917 {
918 DrawDibCloseFunc(impl->draw_dib);
919 impl->draw_dib = NULL;
920 }
921 } while (0);
922 gate_win32_section_leave(&impl->current_frame_lock);
923
924 gate_syncevent_set(&impl->sync_event);
925 return GATE_RESULT_OK;
926 }
927
928 static gate_result_t gate_vfw_stop_queue(gate_video_vfw_impl_t* impl)
929 {
930 gate_result_t result;
931 gate_int32_t state = gate_atomic_int_xchg_if(&impl->queue_state, GATE_VFW_QUEUE_STATE_ONLINE, GATE_VFW_QUEUE_STATE_STOPPING);
932 if (state != GATE_VFW_QUEUE_STATE_ONLINE)
933 {
934 result = GATE_RESULT_INVALIDSTATE;
935 }
936 else
937 {
938 gate_win32_userapi_t const* const userapi = gate_win32_userapi();
939 userapi->UserPostThreadMessage(impl->thread_id, WM_QUIT, 0, 0);
940 result = gate_thread_join(&impl->thread, NULL);
941 impl->thread_id = 0;
942 gate_atomic_int_set(&impl->queue_state, GATE_VFW_QUEUE_STATE_OFFLINE);
943 }
944 return result;
945 }
946
947 static gate_result_t gate_vfw_start_queue(gate_video_vfw_impl_t* impl)
948 {
949 gate_result_t result;
950 gate_int32_t state;
951
952 state = gate_atomic_int_xchg_if(&impl->queue_state, GATE_VFW_QUEUE_STATE_OFFLINE, GATE_VFW_QUEUE_STATE_STARTING);
953 switch (state)
954 {
955 case GATE_VFW_QUEUE_STATE_OFFLINE:
956 {
957 gate_syncevent_reset(&impl->sync_event);
958 result = gate_thread_start_code(&gate_vfw_thread_code, impl, &impl->thread, NULL);
959 if (GATE_FAILED(result))
960 {
961 gate_atomic_int_xchg_if(&impl->queue_state, GATE_VFW_QUEUE_STATE_STARTING, GATE_VFW_QUEUE_STATE_OFFLINE);
962 result = GATE_RESULT_FAILED;
963 }
964 else
965 {
966 result = gate_syncevent_wait(&impl->sync_event);
967 state = gate_atomic_int_get(&impl->queue_state);
968 if (state == GATE_VFW_QUEUE_STATE_ONLINE)
969 {
970 result = GATE_RESULT_OK;
971 }
972 else
973 {
974 gate_vfw_stop_queue(impl);
975 if(GATE_SUCCEEDED(result))
976 {
977 result = GATE_RESULT_UNKNOWNEXCEPTION;
978 }
979 else
980 {
981 /* return the failed status from synevent_wait */
982 }
983 }
984 }
985 break;
986 }
987 case GATE_VFW_QUEUE_STATE_ONLINE:
988 {
989 result = GATE_RESULT_OK;
990 break;
991 }
992 default:
993 {
994 result = GATE_RESULT_INVALIDSTATE;
995 break;
996 }
997 }
998 return result;
999 }
1000
1001 static gate_size_t gate_video_vfw_get_supported_formats(void* self, gate_video_format_t* format_buffer, gate_size_t format_buffer_count)
1002 {
1003 gate_video_vfw_impl_t* impl = (gate_video_vfw_impl_t*)self;
1004 gate_size_t ret = 0;
1005 gate_result_t result = gate_vfw_start_queue(impl);
1006
1007 do
1008 {
1009 GATE_BREAK_IF_FAILED_TRACE(result, "Failed to start VFW queue");
1010
1011 if (format_buffer_count > 0)
1012 {
1013 *format_buffer = impl->default_format;
1014 ret = 1;
1015 }
1016 } while (0);
1017
1018 return ret;
1019 }
1020
1021 static gate_result_t gate_video_vfw_open(void* self, gate_video_format_t const* format)
1022 {
1023 gate_video_vfw_impl_t* impl = (gate_video_vfw_impl_t*)self;
1024 gate_win32_userapi_t const* const userapi = gate_win32_userapi();
1025 gate_result_t result = gate_vfw_start_queue(impl);
1026 do
1027 {
1028 GATE_BREAK_IF_FAILED_TRACE(result, "Failed to start VFW queue");
1029 impl->format = *format;
1030
1031 userapi->UserPostMessage(impl->hwnd, WM_GATE_VFW_START, 0, 0);
1032 } while (0);
1033
1034 return result;
1035 }
1036 static gate_result_t gate_video_vfw_close(void* self)
1037 {
1038 gate_video_vfw_impl_t* impl = (gate_video_vfw_impl_t*)self;
1039
1040 gate_result_t result = gate_vfw_stop_queue(impl);
1041
1042 return result;
1043 }
1044
1045 static gate_result_t gate_video_vfw_read(void* self, gate_video_frame_t* frame)
1046 {
1047 gate_video_vfw_impl_t* impl = (gate_video_vfw_impl_t*)self;
1048 gate_result_t result = GATE_RESULT_OK;
1049
1050 gate_win32_section_enter(&impl->current_frame_lock);
1051 do
1052 {
1053 int width, height;
1054 gate_color_t* dst_pixel;
1055 gate_uint8_t const* src_pixel;
1056
1057 if (impl->current_frame_data == NULL)
1058 {
1059 /* no frame available */
1060 result = GATE_RESULT_NOTAVAILABLE;
1061 break;
1062 }
1063
1064 width = impl->draw_bitmapinfo.bmiHeader.biWidth;
1065 height = impl->draw_bitmapinfo.bmiHeader.biHeight;
1066
1067 if (!DrawDibDrawFunc(impl->draw_dib, impl->draw_dc, 0, 0, width, height,
1068 &impl->draw_bitmapinfo.bmiHeader, gate_memoryblock_get_content(impl->current_frame_data),
1069 0, 0, width, height, 0))
1070 {
1071 if (impl->draw_bitmapinfo.bmiHeader.biCompression == 0x47504a4d)
1072 {
1073 /* MJPEG */
1074 result = mjpeg_export(frame,
1075 gate_memoryblock_get_content(impl->current_frame_data),
1076 gate_memoryblock_get_size(impl->current_frame_data));
1077 if (GATE_SUCCEEDED(result))
1078 {
1079 break;
1080 }
1081 }
1082 GATE_DEBUG_TRACE("VFW DrawDibDraw failed");
1083 result = GATE_RESULT_FAILED;
1084 break;
1085 }
1086
1087 if (NULL == gate_rasterimage_create(&frame->image, GATE_IMAGE_PIXELFORMAT_RGBA, impl->format.width, impl->format.height, NULL))
1088 {
1089 GATE_DEBUG_TRACE("Failed to create VFW raster image");
1090 result = GATE_RESULT_OUTOFMEMORY;
1091 break;
1092 }
1093 frame->format = impl->format;
1094 frame->record_time = 0; /*TODO*/
1095
1096 dst_pixel = (gate_color_t*)gate_rasterimage_get_line_ptr(&frame->image, 0);
1097 if (height < 0)
1098 {
1099 gate_size_t pixel_count = (gate_size_t)width * (gate_size_t)(-height);
1100 src_pixel = (gate_uint8_t const*)impl->draw_buffer;
1101
1102 while (pixel_count-- != 0)
1103 {
1104 dst_pixel->b = *src_pixel++;
1105 dst_pixel->g = *src_pixel++;
1106 dst_pixel->r = *src_pixel++;
1107 dst_pixel->a = 255;
1108 ++src_pixel;
1109 ++dst_pixel;
1110 }
1111 }
1112 else
1113 {
1114 int y;
1115 for (y = 0; y < height; ++y)
1116 {
1117 int x;
1118 src_pixel = (gate_uint8_t const*)impl->draw_buffer;
1119 src_pixel += (gate_size_t)(height - y - 1) * (gate_size_t)(width * 4);
1120 for (x = 0; x < width; ++x)
1121 {
1122 dst_pixel->b = *src_pixel++;
1123 dst_pixel->g = *src_pixel++;
1124 dst_pixel->r = *src_pixel++;
1125 dst_pixel->a = 255;
1126 ++src_pixel;
1127 ++dst_pixel;
1128 }
1129 }
1130 }
1131 } while (0);
1132 gate_win32_section_leave(&impl->current_frame_lock);
1133 return result;
1134 }
1135
1136
1137 static gate_video_source_t* create_vfw_video_source(WORD index, TCHAR const* name, TCHAR const* version)
1138 {
1139 gate_video_vfw_impl_t* impl = (gate_video_vfw_impl_t*)gate_mem_alloc(sizeof(gate_video_vfw_impl_t));
1140 gate_result_t result = GATE_RESULT_OK;
1141 if (NULL != impl)
1142 {
1143 do
1144 {
1145 gate_mem_clear(impl, sizeof(gate_video_vfw_impl_t));
1146
1147 result = gate_syncevent_create(&impl->sync_event, false);
1148 GATE_BREAK_IF_FAILED_TRACE(result, "Failed to create VFW sync event");
1149
1150 gate_init_video_vfw_vtbl_impl();
1151 impl->vtbl = &gate_video_vfw_vtbl_impl;
1152 gate_atomic_int_init(&impl->ref_counter, 1);
1153
1154 impl->id[0] = 'v';
1155 impl->id[1] = 'f';
1156 impl->id[2] = 'w';
1157 gate_str_print_uint16(&impl->id[3], sizeof(impl->id) - 4, (gate_uint16_t)index);
1158
1159 gate_win32_winstr_2_utf8(name, gate_win32_winstr_length(name), impl->name, sizeof(impl->name));
1160 impl->handle = index;
1161
1162 gate_atomic_int_init(&impl->queue_state, GATE_VFW_QUEUE_STATE_OFFLINE);
1163
1164 gate_win32_section_create(&impl->current_frame_lock);
1165
1166 } while (0);
1167 }
1168 return (gate_video_source_t*)impl;
1169 }
1170
1171 static gate_result_t get_vfw_source(UINT index, gate_video_source_t** ptr_source)
1172 {
1173 gate_result_t ret = GATE_RESULT_NOMATCH;
1174 TCHAR name_buffer[1024];
1175 TCHAR version_buffer[1024];
1176 gate_video_source_t* impl = NULL;
1177 if (capGetDriverDescriptionFunc(index, name_buffer, sizeof(name_buffer), version_buffer, sizeof(version_buffer)))
1178 {
1179 ret = GATE_RESULT_OK;
1180 if (ptr_source)
1181 {
1182 impl = create_vfw_video_source((WORD)index, name_buffer, version_buffer);
1183 if (impl == NULL)
1184 {
1185 ret = GATE_RESULT_FAILED;
1186 }
1187 else
1188 {
1189 *ptr_source = impl;
1190 }
1191 }
1192 }
1193 return ret;
1194 }
1195
1196 #endif /* GATE_IO_VIDEOSOURCES_USE_V4W */
1197
1198
1199
1200 #if defined(GATE_IO_VIDEOSOURCES_USE_DSHOW)
1201
1202 #include <guiddef.h>
1203
1204 #include "gate/io/platform/videosources_dshow.h"
1205
1206
1207 /*******************
1208 ** DIRECT SHOW **
1209 *******************/
1210
1211 static void dshow_free_media_type(AM_MEDIA_TYPE* mt)
1212 {
1213 if (mt->cbFormat)
1214 {
1215 gate_win32_com_dealloc((LPVOID)mt->pbFormat);
1216 mt->cbFormat = 0;
1217 mt->pbFormat = 0;
1218 }
1219 if (mt->pUnk)
1220 {
1221 mt->pUnk->lpVtbl->Release(mt->pUnk);
1222 mt->pUnk = 0;
1223 }
1224 }
1225 static void dshow_delete_media_type(AM_MEDIA_TYPE* ptr)
1226 {
1227 if (ptr)
1228 {
1229 dshow_free_media_type(ptr);
1230 gate_win32_com_dealloc(ptr);
1231 }
1232 }
1233
1234 typedef struct ISampleGrabberCBImpl_class
1235 {
1236 ISampleGrabberCBVtbl const* lpVtbl;
1237
1238 gate_atomic_int_t ref_counter;
1239 gate_mutex_t buffer_mutex;
1240 void* buffer_data;
1241 gate_size_t buffer_used;
1242 gate_size_t buffer_capacity;
1243 gate_syncevent_t buffer_event;
1244
1245 } ISampleGrabberCBImpl;
1246
1247
1248 static HRESULT STDMETHODCALLTYPE QueryInterface(ISampleGrabberCB* This, REFIID riid, void** ppvObject)
1249 {
1250 HRESULT hr;
1251 ISampleGrabberCBImpl* impl = (ISampleGrabberCBImpl*)This;
1252 if (IsEqualIID(riid, &gate_IID_IUnknown))
1253 {
1254 if (ppvObject)
1255 {
1256 *ppvObject = impl;
1257 }
1258 hr = S_OK;
1259 }
1260 else if (IsEqualIID(riid, &gate_IID_IUnknown))
1261 {
1262 if (ppvObject)
1263 {
1264 *ppvObject = impl;
1265 }
1266 hr = S_OK;
1267 }
1268 else
1269 {
1270 hr = E_NOTIMPL;
1271 }
1272 return hr;
1273 }
1274 static ULONG STDMETHODCALLTYPE AddRef(ISampleGrabberCB* This)
1275 {
1276 ISampleGrabberCBImpl* impl = (ISampleGrabberCBImpl*)This;
1277 return (ULONG)gate_atomic_int_inc(&impl->ref_counter);
1278 }
1279 static ULONG STDMETHODCALLTYPE Release(ISampleGrabberCB* This)
1280 {
1281 ISampleGrabberCBImpl* impl = (ISampleGrabberCBImpl*)This;
1282 gate_int32_t cnt = gate_atomic_int_inc(&impl->ref_counter);
1283 return (ULONG)cnt;
1284 }
1285
1286 static HRESULT STDMETHODCALLTYPE SampleCB(ISampleGrabberCB* This, double SampleTime, IMediaSample* pSample)
1287 {
1288 ISampleGrabberCBImpl* impl = (ISampleGrabberCBImpl*)This;
1289 (void)SampleTime;
1290 (void)pSample;
1291 return S_OK;
1292 }
1293 static HRESULT STDMETHODCALLTYPE BufferCB(ISampleGrabberCB* This, double SampleTime, BYTE* pBuffer, LONG BufferLen)
1294 {
1295 HRESULT hr = S_OK;
1296 gate_result_t result;
1297 ISampleGrabberCBImpl* impl = (ISampleGrabberCBImpl*)This;
1298 void* new_buffer;
1299
1300 do
1301 {
1302 if (!impl || !pBuffer || (BufferLen <= 0))
1303 {
1304 break;
1305 }
1306
1307 result = gate_mutex_acquire(&impl->buffer_mutex);
1308 GATE_BREAK_IF_FAILED(result);
1309
1310 do
1311 {
1312 if ((gate_size_t)BufferLen > impl->buffer_capacity)
1313 {
1314 new_buffer = gate_mem_alloc((gate_size_t)BufferLen);
1315 if (new_buffer == NULL)
1316 {
1317 break;
1318 }
1319 if (impl->buffer_data != NULL)
1320 {
1321 gate_mem_dealloc(impl->buffer_data);
1322 }
1323 impl->buffer_data = new_buffer;
1324 impl->buffer_capacity = (gate_size_t)BufferLen;
1325 }
1326 impl->buffer_used = (gate_size_t)BufferLen;
1327 gate_mem_copy(impl->buffer_data, pBuffer, impl->buffer_used);
1328 } while (0);
1329
1330 gate_mutex_release(&impl->buffer_mutex);
1331
1332 } while (0);
1333 return hr;
1334 }
1335
1336 static gate_result_t sample_grabber_export_rgb32(gate_video_frame_t* target_frame, void const* data)
1337 {
1338 gate_result_t ret;
1339 gate_size_t line_len = (gate_size_t)target_frame->format.width * 3;
1340 char const* buffer;
1341 gate_uint32_t x, y;
1342 gate_color_t* ptr_pixel;
1343
1344 if (NULL == gate_rasterimage_create(&target_frame->image, GATE_IMAGE_PIXELFORMAT_RGBA, target_frame->format.width, target_frame->format.height, NULL))
1345 {
1346 ret = GATE_RESULT_OUTOFMEMORY;
1347 }
1348 else
1349 {
1350 buffer = (char const*)data;
1351 for (y = 0; y != target_frame->format.height; ++y)
1352 {
1353 ptr_pixel = (gate_color_t*)gate_rasterimage_get_line_ptr(&target_frame->image, (target_frame->format.height - y - 1));
1354 for (x = 0; x != target_frame->format.width; ++x)
1355 {
1356 ptr_pixel->b = *buffer;
1357 ++buffer;
1358 ptr_pixel->g = *buffer;
1359 ++buffer;
1360 ptr_pixel->r = *buffer;
1361 ++buffer;
1362 ptr_pixel->a = (255 - *buffer);
1363 ++buffer;
1364
1365 ++ptr_pixel;
1366 }
1367 }
1368 ret = GATE_RESULT_OK;
1369 }
1370 return ret;
1371
1372 }
1373 static gate_result_t sample_grabber_export_rgb24(gate_video_frame_t* target_frame, void const* data)
1374 {
1375 gate_result_t ret;
1376 gate_size_t line_len = (gate_size_t)target_frame->format.width * 3;
1377 gate_size_t align_diff = line_len % 4;
1378 char const* buffer;
1379 gate_uint32_t x, y;
1380 gate_color_t* ptr_pixel;
1381
1382 if (NULL == gate_rasterimage_create(&target_frame->image, GATE_IMAGE_PIXELFORMAT_RGBA, target_frame->format.width, target_frame->format.height, NULL))
1383 {
1384 ret = GATE_RESULT_OUTOFMEMORY;
1385 }
1386 else
1387 {
1388 if (align_diff != 0)
1389 {
1390 align_diff = 4 - align_diff;
1391 }
1392 buffer = (char const*)data;
1393 for (y = 0; y != target_frame->format.height; ++y)
1394 {
1395 ptr_pixel = (gate_color_t*)gate_rasterimage_get_line_ptr(&target_frame->image, (target_frame->format.height - y - 1));
1396 for (x = 0; x != target_frame->format.width; ++x)
1397 {
1398 ptr_pixel->b = *buffer;
1399 ++buffer;
1400 ptr_pixel->g = *buffer;
1401 ++buffer;
1402 ptr_pixel->r = *buffer;
1403 ++buffer;
1404 ptr_pixel->a = 255;
1405
1406 ++ptr_pixel;
1407 }
1408 buffer += align_diff;
1409 }
1410 ret = GATE_RESULT_OK;
1411 }
1412 return ret;
1413 }
1414
1415 static gate_uint8_t convert_float(gate_real32_t f)
1416 {
1417 int num = (int)(f * 128.0f + 127.0f);
1418 if (num <= 0) return 0;
1419 if (num >= 255) return 255;
1420 return (gate_uint8_t)num;
1421 }
1422 static gate_real32_t convert_byte(gate_uint8_t b)
1423 {
1424 return (((gate_real32_t)b) - 127.0f) / 128.0f;
1425 }
1426
1427
1428 static gate_result_t sample_grabber_export_yuv2_16(gate_video_frame_t* target_frame, void const* data, gate_size_t data_length)
1429 {
1430 const gate_size_t line_len = (gate_size_t)target_frame->format.width * 2;
1431 const gate_size_t align_diff = (line_len % 4 == 0) ? 0 : (4 - (line_len % 4));
1432 const gate_uint32_t aligned_width = target_frame->format.width + (gate_uint32_t)align_diff;
1433 const gate_uint32_t expected_len = aligned_width * target_frame->format.height;
1434 gate_result_t result;
1435 char const* buffer;
1436 gate_intptr_t buffer_length;
1437 gate_uint32_t x, y;
1438 gate_color_t* ptr_pixel;
1439 gate_real32_t y1, u, y2, v;
1440 gate_real32_t fb, fr, fg;
1441
1442 if (data_length < expected_len)
1443 {
1444 /* some CAMs deliver MJPEG instead of specified format type */
1445 buffer = (char const*)data;
1446 if (((unsigned char)buffer[0] == 0xff) && ((unsigned char)buffer[1] == 0xd8))
1447 {
1448 /* content starts with jpeg signature -> try decoding */
1449 result = mjpeg_export(target_frame, data, data_length);
1450 if (GATE_SUCCEEDED(result))
1451 {
1452 return result;
1453 }
1454 }
1455 }
1456
1457 if (NULL == gate_rasterimage_create(&target_frame->image, GATE_IMAGE_PIXELFORMAT_RGBA, target_frame->format.width, target_frame->format.height, NULL))
1458 {
1459 return GATE_RESULT_OUTOFMEMORY;
1460 }
1461 buffer = (char const*)data;
1462 buffer_length = (gate_intptr_t)data_length;
1463 buffer_length -= (buffer_length % 4);
1464 for (y = 0; (y != target_frame->format.height) && (buffer_length != 0); ++y)
1465 {
1466 ptr_pixel = (gate_color_t*)gate_rasterimage_get_line_ptr(&target_frame->image, (target_frame->format.height - y - 1));
1467 for (x = 0; (x < target_frame->format.width) && (buffer_length != 0); x += 2)
1468 {
1469 y1 = convert_byte(buffer[0]);
1470 u = convert_byte(buffer[1]);
1471 y2 = convert_byte(buffer[2]);
1472 v = convert_byte(buffer[3]);
1473 buffer += 4;
1474 buffer_length -= 4;
1475
1476 fb = y1 + u / 0.493f;
1477 fr = y1 + v / 0.877f;
1478 fg = y1 - 0.39393f * u - 0.58081f * v;
1479 ptr_pixel->r = convert_float(fr);
1480 ptr_pixel->g = convert_float(fg);
1481 ptr_pixel->b = convert_float(fb);
1482 ptr_pixel->a = 255;
1483 ++ptr_pixel;
1484
1485 fb = y2 + u / 0.493f;
1486 fr = y2 + v / 0.877f;
1487 fg = y2 - 0.39393f * u - 0.58081f * v;
1488 ptr_pixel->r = convert_float(fr);
1489 ptr_pixel->g = convert_float(fg);
1490 ptr_pixel->b = convert_float(fb);
1491 ptr_pixel->a = 255;
1492 ++ptr_pixel;
1493 }
1494 buffer += align_diff;
1495 buffer_length -= (gate_intptr_t)align_diff;
1496 if (buffer_length <= 0)
1497 {
1498 break;
1499 }
1500 }
1501 return GATE_RESULT_OK;
1502 }
1503
1504
1505 static gate_result_t sample_grabber_export_image(ISampleGrabberCBImpl* this_ptr, gate_video_frame_t* frame)
1506 {
1507 gate_result_t ret = GATE_RESULT_OK;
1508
1509 do
1510 {
1511 switch (frame->format.compression)
1512 {
1513 case BI_RGB:
1514 {
1515 switch (frame->format.bits_per_pixel)
1516 {
1517 case 32:
1518 {
1519 ret = gate_mutex_acquire(&this_ptr->buffer_mutex);
1520 if (GATE_SUCCEEDED(ret))
1521 {
1522 if (this_ptr->buffer_data)
1523 {
1524 ret = sample_grabber_export_rgb32(frame, this_ptr->buffer_data);
1525 }
1526 gate_mutex_release(&this_ptr->buffer_mutex);
1527 }
1528 break;
1529 }
1530 case 24:
1531 {
1532 ret = gate_mutex_acquire(&this_ptr->buffer_mutex);
1533 if (GATE_SUCCEEDED(ret))
1534 {
1535 if (this_ptr->buffer_data)
1536 {
1537 ret = sample_grabber_export_rgb24(frame, this_ptr->buffer_data);
1538 }
1539 gate_mutex_release(&this_ptr->buffer_mutex);
1540 }
1541 break;
1542 }
1543 default:
1544 {
1545 ret = GATE_RESULT_NOTSUPPORTED;
1546 break;
1547 }
1548 }
1549 break;
1550 }
1551 case 0x32595559: // YUY2
1552 {
1553 switch (frame->format.bits_per_pixel)
1554 {
1555 case 16:
1556 {
1557 ret = gate_mutex_acquire(&this_ptr->buffer_mutex);
1558 if (GATE_SUCCEEDED(ret))
1559 {
1560 if (this_ptr->buffer_data && this_ptr->buffer_used)
1561 {
1562 ret = sample_grabber_export_yuv2_16(frame, this_ptr->buffer_data, this_ptr->buffer_used);
1563 }
1564 gate_mutex_release(&this_ptr->buffer_mutex);
1565 }
1566 break;
1567 }
1568 default:
1569 {
1570 ret = GATE_RESULT_NOTSUPPORTED;
1571 break;
1572 }
1573 }
1574 break;
1575 }
1576 case 0x47504a4d:
1577 {
1578 ret = gate_mutex_acquire(&this_ptr->buffer_mutex);
1579 if (GATE_SUCCEEDED(ret))
1580 {
1581 ret = mjpeg_export(frame, this_ptr->buffer_data, this_ptr->buffer_used);
1582 gate_mutex_release(&this_ptr->buffer_mutex);
1583 }
1584 break;
1585 }
1586 default:
1587 {
1588 ret = GATE_RESULT_NOTSUPPORTED;
1589 break;
1590 }
1591 }
1592
1593 } while (0);
1594
1595 return ret;
1596 /*
1597
1598
1599 virtual HRESULT STDMETHODCALLTYPE BufferCB(double SampleTime, BYTE* pBuffer, LONG BufferLen)
1600 {
1601 if(this->m_sink)
1602 {
1603 try
1604 {
1605 this->m_buffer.assign((char const*)pBuffer, (size_t)BufferLen);
1606 VideoFrame frame;
1607 frame.Format = this->m_format;
1608 frame.Format.Compression = 0;
1609 frame.Format.Depth = 32;
1610 frame.Format.EncodingId = 0;
1611 frame.Format.EncodingType = 0;
1612 frame.ClipX = 0;
1613 frame.ClipY = 0;
1614 frame.ClipWidth = this->m_format.Width;
1615 frame.ClipHeight = this->m_format.Height;
1616 frame.CaptureTime = int64_t(SampleTime);
1617 frame.Data.swap(this->m_buffer);
1618 bool nativeFrameUsed = this->m_sink->onNativeFrame(frame);
1619 if(!frame.Data.empty())
1620 {
1621 this->m_buffer.swap(frame.Data);
1622 }
1623 if(!nativeFrameUsed)
1624 {
1625 size_t len = this->m_format.Width * this->m_format.Height * 4;
1626 if((LONG)len <= BufferLen)
1627 {
1628 IRasterImageRgba img(this->m_format.Width, this->m_format.Height);
1629 uint8_t const* src = (uint8_t const*)pBuffer;
1630 for(uint32_t y = 0; y < frame.Format.Height; ++y)
1631 {
1632 ColorRgba* dst = &img.Image()(0, frame.Format.Height - y - 1);
1633 for(uint32_t x = 0; x < frame.Format.Width; ++x)
1634 {
1635 dst->B = *src++;
1636 dst->G = *src++;
1637 dst->R = *src++;
1638 dst->A = 255;
1639 ++src;
1640 ++dst;
1641 }
1642 }
1643 this->m_sink->onImage(img);
1644 }
1645 }
1646 }
1647 catch(...)
1648 {
1649 }
1650 }
1651 return S_OK;
1652 }
1653 };
1654
1655 */
1656
1657 }
1658
1659 static ISampleGrabberCBVtbl const SampleGrabberCBImplVtbl =
1660 {
1661 &QueryInterface,
1662 &AddRef,
1663 &Release,
1664
1665 &SampleCB,
1666 &BufferCB
1667 };
1668
1669 static gate_result_t sample_grabber_cb_impl_init(ISampleGrabberCBImpl* impl)
1670 {
1671 gate_result_t ret;
1672 do
1673 {
1674 gate_mem_clear(impl, sizeof(ISampleGrabberCBImpl));
1675 impl->lpVtbl = &SampleGrabberCBImplVtbl;
1676 gate_atomic_int_init(&impl->ref_counter, 1);
1677
1678 ret = gate_mutex_create(&impl->buffer_mutex);
1679 GATE_BREAK_IF_FAILED(ret);
1680
1681 ret = gate_syncevent_create(&impl->buffer_event, true);
1682 if (GATE_FAILED(ret))
1683 {
1684 gate_mutex_destroy(&impl->buffer_mutex);
1685 break;
1686 }
1687
1688 impl->buffer_data = NULL;
1689 impl->buffer_used = 0;
1690 impl->buffer_capacity = 0;
1691 } while (0);
1692 return ret;
1693 }
1694
1695 static gate_result_t sample_grabber_cb_impl_destroy(ISampleGrabberCBImpl* impl)
1696 {
1697 gate_result_t ret = GATE_RESULT_OK;
1698 gate_syncevent_destroy(&impl->buffer_event);
1699 gate_mutex_destroy(&impl->buffer_mutex);
1700
1701 if (impl->buffer_data)
1702 {
1703 gate_mem_dealloc(impl->buffer_data);
1704 }
1705 return ret;
1706 }
1707
1708
1709 typedef struct dshow_video_device_info
1710 {
1711 gate_uintptr_t handle;
1712 char name[256];
1713 char uid[256];
1714 } dshow_video_device_info_t;
1715
1716 typedef gate_bool_t(*dshow_enum_video_device_callback)(char const* id, char const* name, IMoniker* moniker, void* param);
1717
1718 static gate_result_t dshow_enum_video_devices(dshow_enum_video_device_callback callback, void* param)
1719 {
1720 gate_result_t ret = GATE_RESULT_OK;
1721 ICreateDevEnum* system_dev_enum = NULL;
1722 IEnumMoniker* enum_moniker = NULL;
1723 IMoniker* moniker = NULL;
1724 IPropertyBag* propbag = NULL;
1725 HRESULT hr;
1726 ULONG dwFetched;
1727 VARIANT var_path = GATE_INIT_EMPTY;
1728 VARIANT var_friendly = GATE_INIT_EMPTY;
1729 char str_path[4096];
1730 char str_name[4096];
1731 gate_bool_t com_initialized = false;
1732 gate_bool_t continue_enum = true;
1733
1734 do
1735 {
1736 ret = gate_win32_com_init();
1737 GATE_BREAK_IF_FAILED(ret);
1738 com_initialized = true;
1739
1740 gate_win32_com_pump_messages(0);
1741
1742 hr = gate_win32_com_create_instance(&gate_CLSID_SystemDeviceEnum, &gate_IID_ICreateDevEnum,
1743 (LPVOID*)&system_dev_enum);
1744 if (FAILED(hr))
1745 {
1746 ret = GATE_RESULT_FAILED;
1747 break;
1748 }
1749
1750 hr = system_dev_enum->lpVtbl->CreateClassEnumerator(system_dev_enum, &gate_CLSID_VideoInputDeviceCategory, &enum_moniker, 0);
1751 if (FAILED(hr))
1752 {
1753 ret = GATE_RESULT_FAILED;
1754 break;
1755 }
1756 if (hr == S_FALSE)
1757 {
1758 ret = GATE_RESULT_NOTAVAILABLE;
1759 break;
1760 }
1761
1762 hr = enum_moniker->lpVtbl->Reset(enum_moniker);
1763 if (FAILED(hr))
1764 {
1765 ret = GATE_RESULT_FAILED;
1766 break;
1767 }
1768
1769 while (continue_enum && (S_OK == enum_moniker->lpVtbl->Next(enum_moniker, 1, &moniker, &dwFetched)))
1770 {
1771 gate_win32_com_pump_messages(0);
1772 hr = moniker->lpVtbl->BindToStorage(moniker, NULL, NULL, &IID_IPropertyBag, (void**)&propbag);
1773 if (SUCCEEDED(hr))
1774 {
1775 gate_win32_variant_init(&var_path); /* variant MUST be empty before invocation */
1776 hr = propbag->lpVtbl->Read(propbag, L"DevicePath", &var_path, NULL);
1777 if (NOERROR == hr)
1778 {
1779 if (0 == gate_win32_variant_to_str(&var_path, str_path, sizeof(str_path)))
1780 {
1781 str_path[0] = '\0';
1782 }
1783 gate_win32_variant_clear(&var_path);
1784 }
1785 gate_win32_variant_init(&var_friendly); /* variant MUST be empty before invocation */
1786 hr = propbag->lpVtbl->Read(propbag, L"FriendlyName", &var_friendly, NULL);
1787 if (NOERROR == hr)
1788 {
1789 if (0 == gate_win32_variant_to_str(&var_friendly, str_name, sizeof(str_name)))
1790 {
1791 str_name[0] = '\0';
1792 }
1793 gate_win32_variant_clear(&var_friendly);
1794 }
1795 GATE_COM_RELEASE(propbag);
1796 if (!callback(str_path, str_name, moniker, param))
1797 {
1798 continue_enum = false;
1799 }
1800 }
1801 GATE_COM_RELEASE(moniker);
1802 }
1803 } while (0);
1804
1805 GATE_COM_RELEASE(enum_moniker);
1806 GATE_COM_RELEASE(system_dev_enum);
1807
1808 if (com_initialized)
1809 {
1810 gate_win32_com_pump_messages(0);
1811 gate_win32_com_uninit();
1812 }
1813
1814 return ret;
1815 }
1816
1817 struct dshow_list_video_device_param
1818 {
1819 dshow_video_device_info_t* infos;
1820 gate_size_t info_count;
1821 gate_size_t info_used;
1822 };
1823
1824 static gate_bool_t dshow_list_video_device_callback(char const* id, char const* name, IMoniker* moniker, void* param)
1825 {
1826 struct dshow_list_video_device_param* ptr = (struct dshow_list_video_device_param*)param;
1827 dshow_video_device_info_t* info = ptr->infos + ptr->info_used;
1828 if (ptr->info_used < ptr->info_count)
1829 {
1830 gate_str_print_text(info->uid, sizeof(info->uid), id, gate_str_length(id));
1831 gate_str_print_text(info->name, sizeof(info->name), name, gate_str_length(name));
1832 info->handle = 0;
1833 ++ptr->info_used;
1834 return true;
1835 }
1836 else
1837 {
1838 return false;
1839 }
1840 }
1841
1842 static gate_size_t dshow_list_video_devices(dshow_video_device_info_t* infos, gate_size_t info_count)
1843 {
1844 struct dshow_list_video_device_param param;
1845 param.infos = infos;
1846 param.info_count = info_count;
1847 param.info_used = 0;
1848
1849 dshow_enum_video_devices(&dshow_list_video_device_callback, &param);
1850 return param.info_used;
1851 }
1852
1853
1854 static HRESULT dshow_is_pin_connected(IPin* pin, gate_bool_t* ptr_result)
1855 {
1856 IPin* tmp = 0;
1857 HRESULT hr = pin->lpVtbl->ConnectedTo(pin, &tmp);
1858 if (SUCCEEDED(hr))
1859 {
1860 *ptr_result = true;
1861 }
1862 else if (hr == VFW_E_NOT_CONNECTED)
1863 {
1864 *ptr_result = false;
1865 hr = S_OK;
1866 }
1867 GATE_COM_RELEASE(tmp);
1868 return hr;
1869 }
1870
1871 static gate_bool_t const const_false = false;
1872 static gate_bool_t const const_true = true;
1873
1874
1875 static gate_size_t dshow_find_pins(IPin** pin_array, gate_size_t pin_array_len,
1876 IBaseFilter* filter, PIN_DIRECTION const* ptr_direction, gate_bool_t const* ptr_connected,
1877 gate_bool_t const* ptr_preview_only)
1878 {
1879 gate_size_t ret = 0; /* found pin objects */
1880 HRESULT hr;
1881 IEnumPins* pin_enum = NULL;
1882 IPin* current_pin = NULL;
1883 PIN_DIRECTION pin_dir;
1884 gate_bool_t is_connected;
1885 IKsPropertySet* ks_property_set = NULL;
1886 GUID guid_pin_category;
1887 DWORD cb_returned;
1888
1889 hr = filter->lpVtbl->EnumPins(filter, &pin_enum);
1890 if (SUCCEEDED(hr))
1891 {
1892 while (pin_array_len != 0)
1893 {
1894 if (S_OK != pin_enum->lpVtbl->Next(pin_enum, 1, &current_pin, NULL))
1895 {
1896 break;
1897 }
1898 do
1899 {
1900 if (ptr_direction != NULL)
1901 {
1902 /* filter for direction */
1903 if (S_OK != current_pin->lpVtbl->QueryDirection(current_pin, &pin_dir)) continue;
1904 if (pin_dir != *ptr_direction) continue;
1905 }
1906 if (ptr_connected != NULL)
1907 {
1908 /* filter for connection */
1909 is_connected = false;
1910 hr = dshow_is_pin_connected(current_pin, &is_connected);
1911 if (S_OK != hr) continue;
1912 if (*ptr_connected != is_connected) continue;
1913 }
1914 if (ptr_preview_only != NULL)
1915 {
1916 hr = GATE_COM_IFACE(current_pin, &gate_IID_IKsPropertySet, &ks_property_set);
1917 if (FAILED(hr)) continue;
1918 hr = ks_property_set->lpVtbl->Get(ks_property_set, &gate_AMPROPSETID_Pin, AMPROPERTY_PIN_CATEGORY, NULL, 0,
1919 &guid_pin_category, sizeof(guid_pin_category), &cb_returned);
1920 if (FAILED(hr)) continue;
1921 GATE_COM_RELEASE(ks_property_set);
1922 if (IsEqualGUID(&guid_pin_category, &gate_PIN_CATEGORY_CAPTURE))
1923 {
1924 if (*ptr_preview_only) continue;
1925 }
1926 else if (IsEqualGUID(&guid_pin_category, &gate_PIN_CATEGORY_PREVIEW))
1927 {
1928 if (!*ptr_preview_only) continue;
1929 }
1930 else
1931 {
1932 continue;
1933 }
1934 }
1935
1936 /* add pin to array */
1937 *pin_array = current_pin;
1938 ++pin_array;
1939 ++ret;
1940 --pin_array_len;
1941 current_pin = NULL;
1942 } while (0);
1943 GATE_COM_RELEASE(current_pin);
1944 }
1945 GATE_COM_RELEASE(pin_enum);
1946 }
1947 return ret;
1948 }
1949
1950 static gate_size_t dshow_find_pins_sorted(IPin** pin_array, gate_size_t pin_array_len,
1951 IBaseFilter* filter, PIN_DIRECTION const* ptr_direction,
1952 gate_bool_t const* ptr_connected)
1953 {
1954 gate_size_t count1 = dshow_find_pins(pin_array, pin_array_len, filter, ptr_direction, ptr_connected, &const_false);
1955 gate_size_t count2 = dshow_find_pins(&pin_array[count1], pin_array_len - count1, filter, ptr_direction, ptr_connected, &const_true);
1956 return count1 + count2;
1957 }
1958
1959 static HRESULT dShow_connect_filters(IGraphBuilder* graph, IPin* out_pin, IBaseFilter* dest_filter)
1960 {
1961 static PIN_DIRECTION const input_dir = PINDIR_INPUT;
1962 static gate_bool_t const not_connected = false;
1963 static gate_bool_t const no_preview = true;
1964 IPin* pin_array[256] = GATE_INIT_EMPTY;
1965 gate_size_t pin_array_len = sizeof(pin_array) / sizeof(pin_array[0]);
1966 gate_size_t ndx;
1967 HRESULT hr;
1968
1969 pin_array_len = dshow_find_pins(pin_array, pin_array_len, dest_filter, &input_dir, &not_connected, &no_preview);
1970 if (pin_array_len == 0)
1971 {
1972 hr = S_FALSE;
1973 }
1974 else
1975 {
1976 hr = graph->lpVtbl->Connect(graph, out_pin, pin_array[0]);
1977 for (ndx = 0; ndx != pin_array_len; ++ndx)
1978 {
1979 GATE_COM_RELEASE(pin_array[ndx]);
1980 }
1981 }
1982 return hr;
1983 }
1984
1985 struct dshow_open_video_device_param
1986 {
1987 gate_string_t const* desired_id;
1988 IBaseFilter* filter;
1989 };
1990
1991 static gate_bool_t dshow_open_video_device_callback(char const* id, char const* name, IMoniker* moniker, void* param)
1992 {
1993 struct dshow_open_video_device_param* ptr = (struct dshow_open_video_device_param*)param;
1994 HRESULT hr;
1995 if (0 == gate_str_compare(ptr->desired_id->str, ptr->desired_id->length, id, gate_str_length(id)))
1996 {
1997 /* device found */
1998 hr = moniker->lpVtbl->BindToObject(moniker, NULL, NULL, &gate_IID_IBaseFilter, (LPVOID*)&ptr->filter);
1999 if (SUCCEEDED(hr))
2000 {
2001 return false; /*cancel further search */
2002 }
2003 ptr->filter = NULL;
2004 }
2005 /* continue search */
2006 return true;
2007 }
2008
2009
2010
2011 static gate_result_t dshow_open_video_device(gate_string_t const* device_id, IBaseFilter** ptr_filter, IGraphBuilder** ptr_graph,
2012 ISampleGrabber** ptr_grabber, ISampleGrabberCB* ptr_grabber_cb, IBaseFilter** ptr_null_filter)
2013 {
2014 gate_result_t ret = GATE_RESULT_FAILED;
2015 ICreateDevEnum* dev_enum = NULL;
2016 IEnumMoniker* enum_moniker = NULL;
2017 IMoniker* ptr_moniker = NULL;
2018 IBaseFilter* filter = NULL;
2019 IGraphBuilder* graph = NULL;
2020 ISampleGrabber* grabber = NULL;
2021 IBaseFilter* grabber_filter = NULL;
2022 IBaseFilter* null_filter = NULL;
2023 HRESULT hr;
2024 struct dshow_open_video_device_param open_param;
2025
2026 *ptr_filter = NULL;
2027 *ptr_graph = NULL;
2028 *ptr_grabber = NULL;
2029 *ptr_null_filter = NULL;
2030
2031 do
2032 {
2033 open_param.desired_id = device_id;
2034 open_param.filter = NULL;
2035 ret = dshow_enum_video_devices(&dshow_open_video_device_callback, &open_param);
2036 GATE_BREAK_IF_FAILED(ret);
2037 if (open_param.filter == NULL)
2038 {
2039 ret = GATE_RESULT_NOMATCH;
2040 break;
2041 }
2042
2043 filter = open_param.filter;
2044 open_param.filter = NULL;
2045 hr = gate_win32_com_create_instance(&gate_CLSID_FilterGraph, &gate_IID_IGraphBuilder,
2046 (LPVOID*)&graph);
2047 if (FAILED(hr))
2048 {
2049 GATE_DEBUG_TRACE("Failed to create FilterGraph object");
2050 GATE_DEBUG_TRACE_VALUE(hr);
2051 break;
2052 }
2053
2054 hr = graph->lpVtbl->AddFilter(graph, filter, L"Video Source");
2055 if (FAILED(hr))
2056 {
2057 GATE_DEBUG_TRACE("Failed to add video source BaseFilter to FilterGraph");
2058 GATE_DEBUG_TRACE_VALUE(hr);
2059 break;
2060 }
2061
2062 hr = gate_win32_com_create_instance(&gate_CLSID_SampleGrabber, &gate_IID_ISampleGrabber,
2063 (LPVOID*)&grabber);
2064 if (FAILED(hr))
2065 {
2066 GATE_DEBUG_TRACE("Failed to create SampleGrabber");
2067 GATE_DEBUG_TRACE_VALUE(hr);
2068 break;
2069 }
2070
2071 hr = GATE_COM_IFACE(grabber, &gate_IID_IBaseFilter, &grabber_filter);
2072 if (FAILED(hr))
2073 {
2074 GATE_DEBUG_TRACE("Failed to access filter interface of SampleGrabber");
2075 GATE_DEBUG_TRACE_VALUE(hr);
2076 break;
2077 }
2078 hr = graph->lpVtbl->AddFilter(graph, grabber_filter, L"Sample Grabber Filter");
2079 GATE_COM_RELEASE(grabber_filter);
2080 if (FAILED(hr))
2081 {
2082 GATE_DEBUG_TRACE("Failed to access filter interface of SampleGrabber");
2083 GATE_DEBUG_TRACE_VALUE(hr);
2084 break;
2085 }
2086
2087 /*
2088 hr = gate_win32_com_create_instance(&gate_CLSID_NullRenderer, &gate_IID_IBaseFilter, &null_filter);
2089 if(FAILED(hr))
2090 {
2091 GATE_DEBUG_TRACE("Failed to create NullRenderer filter");
2092 GATE_DEBUG_TRACE_VALUE(hr);
2093 break;
2094 }
2095
2096 hr = graph->lpVtbl->AddFilter(graph, null_filter, L"Null Renderer Filter");
2097 GATE_COM_RELEASE(null_filter);
2098 if(FAILED(hr))
2099 {
2100 GATE_DEBUG_TRACE("Failed to add NullRenderer to graph");
2101 GATE_DEBUG_TRACE_VALUE(hr);
2102 break;
2103 }
2104 */
2105
2106 grabber->lpVtbl->SetCallback(grabber, ptr_grabber_cb, 1);
2107
2108 /* all steps succeeded: */
2109
2110 *ptr_filter = filter;
2111 *ptr_graph = graph;
2112 *ptr_grabber = grabber;
2113 *ptr_null_filter = null_filter;
2114 filter = NULL;
2115 graph = NULL;
2116 grabber = NULL;
2117 null_filter = NULL;
2118 ret = GATE_RESULT_OK;
2119
2120 } while (0);
2121
2122 GATE_COM_RELEASE(filter);
2123 GATE_COM_RELEASE(graph);
2124 GATE_COM_RELEASE(grabber);
2125 GATE_COM_RELEASE(null_filter);
2126 GATE_COM_RELEASE(enum_moniker);
2127 GATE_COM_RELEASE(dev_enum);
2128
2129 return ret;
2130 }
2131
2132
2133 static void dshow_close_video_resources(IGraphBuilder* graph, IBaseFilter* source_filter,
2134 ISampleGrabber* grabber, IBaseFilter* dest_filter)
2135 {
2136 gate_result_t ret = GATE_RESULT_FAILED;
2137 IBaseFilter* grabber_filter = NULL;
2138 HRESULT hr;
2139
2140 if (grabber != NULL)
2141 {
2142 hr = GATE_COM_IFACE(grabber, &gate_IID_IBaseFilter, &grabber_filter);
2143 if (FAILED(hr))
2144 {
2145 grabber_filter = NULL;
2146 }
2147 GATE_COM_RELEASE(grabber);
2148 }
2149 if (graph != NULL)
2150 {
2151 if (source_filter != NULL)
2152 {
2153 graph->lpVtbl->RemoveFilter(graph, source_filter);
2154 }
2155 if (grabber_filter != NULL)
2156 {
2157 graph->lpVtbl->RemoveFilter(graph, grabber_filter);
2158 }
2159 if (dest_filter != NULL)
2160 {
2161 graph->lpVtbl->RemoveFilter(graph, dest_filter);
2162 }
2163 GATE_COM_RELEASE(graph);
2164 }
2165
2166 GATE_COM_RELEASE(source_filter);
2167 GATE_COM_RELEASE(grabber_filter);
2168 GATE_COM_RELEASE(dest_filter);
2169 }
2170
2171 static gate_bool_t dshow_setup_pin(IPin* pin, gate_video_format_t const* format)
2172 {
2173 gate_bool_t ret = false;
2174 IAMStreamConfig* config = NULL;
2175 AM_MEDIA_TYPE* ptr_media_type = NULL;
2176 HRESULT hr;
2177 VIDEOINFOHEADER* video_info;
2178 do
2179 {
2180 hr = GATE_COM_IFACE(pin, &gate_IID_IAMStreamConfig, &config);
2181 if (FAILED(hr))
2182 {
2183 GATE_DEBUG_TRACE("Failed to get AMStreamConfig from Pin");
2184 GATE_DEBUG_TRACE_VALUE(hr);
2185 break;
2186 }
2187
2188 hr = config->lpVtbl->GetFormat(config, &ptr_media_type);
2189 if (FAILED(hr))
2190 {
2191 break;
2192 }
2193
2194 if (IsEqualGUID(&ptr_media_type->majortype, &gate_MEDIATYPE_Video)
2195 && IsEqualGUID(&ptr_media_type->formattype, &gate_FORMAT_VideoInfo)
2196 && (ptr_media_type->cbFormat >= sizeof(VIDEOINFOHEADER))
2197 && (ptr_media_type->pbFormat != NULL)
2198 )
2199 {
2200 video_info = (VIDEOINFOHEADER*)(ptr_media_type->pbFormat);
2201 video_info->bmiHeader.biCompression = format->compression;
2202 video_info->bmiHeader.biBitCount = format->bits_per_pixel;
2203 video_info->bmiHeader.biWidth = format->width;
2204 video_info->bmiHeader.biHeight = format->height;
2205
2206 hr = config->lpVtbl->SetFormat(config, ptr_media_type);
2207 if (SUCCEEDED(hr))
2208 {
2209 ret = true;
2210 }
2211 }
2212
2213 ret = true;
2214 } while (0);
2215
2216 if (ptr_media_type != NULL)
2217 {
2218 dshow_delete_media_type(ptr_media_type);
2219 }
2220 GATE_COM_RELEASE(config);
2221 return ret;
2222 }
2223
2224
2225 static void dshow_release_pin_array(IPin** pins, gate_size_t pin_count)
2226 {
2227 while (pin_count-- != 0)
2228 {
2229 GATE_COM_RELEASE(*pins);
2230 pins++;
2231 }
2232 }
2233
2234
2235 static gate_result_t dshow_connect_filter_pins(IGraphBuilder* graph, IBaseFilter* src_filter, IBaseFilter* dst_filter,
2236 gate_video_format_t const* format, IPin** ptr_out_pin, IPin** ptr_in_pin)
2237 {
2238 static PIN_DIRECTION const pin_output_direction = PINDIR_OUTPUT;
2239 static PIN_DIRECTION const pin_input_direction = PINDIR_INPUT;
2240 gate_bool_t no_preview = true;
2241 # define DSHOW_PIN_BUFFER_LEN 256
2242
2243 IPin* out_pins[DSHOW_PIN_BUFFER_LEN];
2244 gate_size_t out_pins_used = 0;
2245 IPin* in_pins[DSHOW_PIN_BUFFER_LEN];
2246 gate_size_t in_pins_used = 0;
2247 gate_size_t ndx_in, ndx_out;
2248 HRESULT hr;
2249 gate_result_t ret = GATE_RESULT_FAILED;
2250
2251 out_pins_used = dshow_find_pins(out_pins, DSHOW_PIN_BUFFER_LEN, src_filter, &pin_output_direction, NULL, &no_preview);
2252 in_pins_used = dshow_find_pins(in_pins, DSHOW_PIN_BUFFER_LEN, dst_filter, &pin_input_direction, NULL, &no_preview);
2253
2254 no_preview = false;
2255 /* try preview-PINs if possible */
2256 if (out_pins_used == 0)
2257 {
2258 out_pins_used = dshow_find_pins(out_pins, DSHOW_PIN_BUFFER_LEN, src_filter, &pin_output_direction, NULL, &no_preview);
2259 }
2260 if (in_pins_used == 0)
2261 {
2262 in_pins_used = dshow_find_pins(in_pins, DSHOW_PIN_BUFFER_LEN, dst_filter, &pin_input_direction, NULL, &no_preview);
2263 }
2264
2265 /* try PINs without advanced properties */
2266 if (out_pins_used == 0)
2267 {
2268 out_pins_used = dshow_find_pins(out_pins, DSHOW_PIN_BUFFER_LEN, src_filter, &pin_output_direction, NULL, NULL);
2269 }
2270 if (in_pins_used == 0)
2271 {
2272 in_pins_used = dshow_find_pins(in_pins, DSHOW_PIN_BUFFER_LEN, dst_filter, &pin_input_direction, NULL, NULL);
2273 }
2274
2275
2276 for (ndx_in = 0; ndx_in != in_pins_used; ++ndx_in)
2277 {
2278 for (ndx_out = 0; ndx_out != out_pins_used; ++ndx_out)
2279 {
2280 if (dshow_setup_pin(out_pins[ndx_out], format))
2281 {
2282 hr = graph->lpVtbl->Connect(graph, out_pins[ndx_out], in_pins[ndx_in]);
2283 if (SUCCEEDED(hr))
2284 {
2285 *ptr_out_pin = out_pins[ndx_out];
2286 *ptr_in_pin = in_pins[ndx_in];
2287 out_pins[ndx_out] = 0;
2288 in_pins[ndx_in] = 0;
2289 ret = GATE_RESULT_OK;
2290 ndx_in = in_pins_used - 1; /* leave outer loop */
2291 break;
2292 }
2293 else
2294 {
2295 GATE_DEBUG_TRACE("Failed to connect input and output pins");
2296 }
2297 }
2298 }
2299 }
2300
2301 dshow_release_pin_array(in_pins, in_pins_used);
2302 dshow_release_pin_array(out_pins, out_pins_used);
2303
2304 return ret;
2305 }
2306
2307 static gate_result_t dshow_set_pin_format(IPin* pin, gate_video_format_t const* format)
2308 {
2309 gate_result_t ret = GATE_RESULT_FAILED;
2310 IAMStreamConfig* config = NULL;
2311 HRESULT hr;
2312 int caps_count, caps_size, ndx;
2313 VIDEO_STREAM_CONFIG_CAPS config_caps;
2314 PIN_INFO pin_info;
2315 AM_MEDIA_TYPE* ptr_media_type;
2316 gate_real32_t min_fps, max_fps;
2317 VIDEOINFOHEADER* ptr_info_header;
2318
2319 do
2320 {
2321 hr = GATE_COM_IFACE(pin, &gate_IID_IAMStreamConfig, &config);
2322 if (FAILED(hr))
2323 {
2324 GATE_DEBUG_TRACE("Failed to access IAMStreamConfig interface");
2325 GATE_DEBUG_TRACE_VALUE(hr);
2326 break;
2327 }
2328 hr = config->lpVtbl->GetNumberOfCapabilities(config, &caps_count, &caps_size);
2329 if (FAILED(hr))
2330 {
2331 GATE_DEBUG_TRACE("Faild to get capabilities");
2332 GATE_DEBUG_TRACE_VALUE(hr);
2333 break;
2334 }
2335 if (caps_size != sizeof(VIDEO_STREAM_CONFIG_CAPS))
2336 {
2337 GATE_DEBUG_TRACE("Invalid capability size");
2338 GATE_DEBUG_TRACE_VALUE(caps_size);
2339 break;
2340 }
2341 hr = pin->lpVtbl->QueryPinInfo(pin, &pin_info);
2342 if (FAILED(hr))
2343 {
2344 GATE_DEBUG_TRACE("Failed to query pin infos");
2345 GATE_DEBUG_TRACE_VALUE(hr);
2346 break;
2347 }
2348 GATE_COM_RELEASE(pin_info.pFilter);
2349
2350 for (ndx = 0; ndx != caps_count; ++ndx)
2351 {
2352 hr = config->lpVtbl->GetStreamCaps(config, ndx, &ptr_media_type, (BYTE*)&config_caps);
2353 if (FAILED(hr))
2354 {
2355 continue;
2356 }
2357
2358 if (IsEqualGUID(&ptr_media_type->majortype, &gate_MEDIATYPE_Video)
2359 && IsEqualGUID(&ptr_media_type->formattype, &gate_FORMAT_VideoInfo)
2360 && (ptr_media_type->cbFormat >= sizeof(VIDEOINFOHEADER))
2361 && (ptr_media_type->pbFormat != NULL)
2362 )
2363 {
2364 min_fps = 10000000.0f / (gate_real32_t)config_caps.MinFrameInterval;
2365 max_fps = 10000000.0f / (gate_real32_t)config_caps.MaxFrameInterval;
2366
2367 ptr_info_header = (VIDEOINFOHEADER*)ptr_media_type->pbFormat;
2368 if ((ptr_info_header->bmiHeader.biCompression == format->compression)
2369 && (ptr_info_header->bmiHeader.biBitCount == format->bits_per_pixel)
2370 && (ptr_info_header->bmiHeader.biWidth == format->width)
2371 && (ptr_info_header->bmiHeader.biHeight == format->height)
2372 )
2373 {
2374 config->lpVtbl->SetFormat(config, ptr_media_type);
2375 ndx = caps_count - 1;
2376 ret = GATE_RESULT_OK;
2377 }
2378 }
2379 dshow_delete_media_type(ptr_media_type);
2380 }
2381
2382 } while (0);
2383
2384 GATE_COM_RELEASE(config);
2385 return ret;
2386 }
2387
2388 static gate_size_t dshow_read_supported_pin_formats(IPin* pin, gate_video_format_t* formats, gate_size_t format_count)
2389 {
2390 gate_size_t ret = 0;
2391 IAMStreamConfig* config = NULL;
2392 int caps_count, caps_size, ndx;
2393 HRESULT hr;
2394 VIDEO_STREAM_CONFIG_CAPS config_caps;
2395 AM_MEDIA_TYPE* ptr_media_type;
2396 gate_real32_t min_fps, max_fps;
2397 VIDEOINFOHEADER* ptr_info_header;
2398
2399 do
2400 {
2401 hr = GATE_COM_IFACE(pin, &gate_IID_IAMStreamConfig, &config);
2402 if (FAILED(hr))
2403 {
2404 GATE_DEBUG_TRACE("Failed to access IID_IAMStreamConfig");
2405 GATE_DEBUG_TRACE_VALUE(hr);
2406 break;
2407 }
2408 hr = config->lpVtbl->GetNumberOfCapabilities(config, &caps_count, &caps_size);
2409 if (FAILED(hr))
2410 {
2411 GATE_DEBUG_TRACE("Failed to get capabilities");
2412 GATE_DEBUG_TRACE_VALUE(hr);
2413 break;
2414 }
2415 if (sizeof(VIDEO_STREAM_CONFIG_CAPS) != caps_size)
2416 {
2417 GATE_DEBUG_TRACE("Invalid capability size");
2418 GATE_DEBUG_TRACE_VALUE(caps_size);
2419 break;
2420 }
2421 for (ndx = 0; ndx != caps_count; ++ndx)
2422 {
2423 hr = config->lpVtbl->GetStreamCaps(config, ndx, &ptr_media_type, (BYTE*)&config_caps);
2424 if (FAILED(hr))
2425 {
2426 continue;
2427 }
2428 if (IsEqualGUID(&ptr_media_type->majortype, &gate_MEDIATYPE_Video)
2429 && IsEqualGUID(&ptr_media_type->formattype, &gate_FORMAT_VideoInfo)
2430 && (ptr_media_type->cbFormat >= sizeof(VIDEOINFOHEADER))
2431 && (ptr_media_type->pbFormat != NULL)
2432 )
2433 {
2434 min_fps = 10000000.0f / (gate_real32_t)config_caps.MinFrameInterval;
2435 max_fps = 10000000.0f / (gate_real32_t)config_caps.MaxFrameInterval;
2436
2437 ptr_info_header = (VIDEOINFOHEADER*)ptr_media_type->pbFormat;
2438
2439 if (ret < format_count)
2440 {
2441 formats->bits_per_pixel = ptr_info_header->bmiHeader.biBitCount;
2442 formats->compression = ptr_info_header->bmiHeader.biCompression;
2443 formats->encoding_id = 0;
2444 formats->frames_per_second = max_fps;
2445 formats->width = ptr_info_header->bmiHeader.biWidth;
2446 formats->height = ptr_info_header->bmiHeader.biHeight;
2447 ++ret;
2448 ++formats;
2449 }
2450 if ((max_fps != min_fps) && (ret < format_count))
2451 {
2452 formats->bits_per_pixel = ptr_info_header->bmiHeader.biBitCount;
2453 formats->compression = ptr_info_header->bmiHeader.biCompression;
2454 formats->encoding_id = 0;
2455 formats->frames_per_second = min_fps;
2456 formats->width = ptr_info_header->bmiHeader.biWidth;
2457 formats->height = ptr_info_header->bmiHeader.biHeight;
2458 ++ret;
2459 ++formats;
2460 }
2461 }
2462 dshow_delete_media_type(ptr_media_type);
2463 }
2464
2465 } while (0);
2466
2467 GATE_COM_RELEASE(config);
2468 return ret;
2469 }
2470
2471 static void dshow_end_session(IGraphBuilder* graph, ISampleGrabber* grabber, IPin* src_out, IPin* grab_in, IPin* grab_out, IPin* dest_in)
2472 {
2473 HRESULT hr;
2474 IMediaControl* media_control = NULL;
2475 hr = GATE_COM_IFACE(graph, &gate_IID_IMediaControl, &media_control);
2476 if (SUCCEEDED(hr))
2477 {
2478 media_control->lpVtbl->Stop(media_control);
2479 GATE_COM_RELEASE(media_control);
2480 }
2481
2482 if (grabber != NULL)
2483 {
2484 grabber->lpVtbl->SetCallback(grabber, NULL, 0);
2485 }
2486 if (src_out != NULL) graph->lpVtbl->Disconnect(graph, src_out);
2487 if (grab_in != NULL) graph->lpVtbl->Disconnect(graph, grab_in);
2488 if (grab_out != NULL) graph->lpVtbl->Disconnect(graph, grab_out);
2489 if (dest_in != NULL) graph->lpVtbl->Disconnect(graph, dest_in);
2490 }
2491
2492 static gate_result_t dshow_start_session(gate_video_format_t* format, IGraphBuilder* graph,
2493 IBaseFilter* src_filter, ISampleGrabber* grabber,
2494 IBaseFilter* dest_filter, ISampleGrabberCB* callback,
2495 IPin** ptr_src_out, IPin** ptr_grabber_in,
2496 IPin** ptr_grabber_out, IPin** ptr_dest_in)
2497 {
2498 static PIN_DIRECTION const direction_in = PINDIR_INPUT;
2499 static PIN_DIRECTION const direction_out = PINDIR_OUTPUT;
2500 static gate_bool_t const const_false = false;
2501 static gate_bool_t const const_true = true;
2502
2503 gate_result_t result = GATE_RESULT_FAILED;
2504 IBaseFilter* grabber_filter = NULL;
2505 IMediaControl* media_control = NULL;
2506 HRESULT hr;
2507 IPin* src_pins[256];
2508 gate_size_t src_pins_used = 0;
2509 IPin* grabber_in_pins[256];
2510 gate_size_t grabber_in_pins_used = 0;
2511 IPin* grabber_out_pins[256];
2512 gate_size_t grabber_out_pins_used = 0;
2513 IPin* dst_pins[256];
2514 gate_size_t dst_pins_used = 0;
2515 AM_MEDIA_TYPE media_type;
2516
2517 do
2518 {
2519 *ptr_src_out = NULL;
2520 *ptr_grabber_in = NULL;
2521 *ptr_grabber_out = NULL;
2522 *ptr_dest_in = NULL;
2523
2524 hr = GATE_COM_IFACE(grabber, &gate_IID_IBaseFilter, &grabber_filter);
2525 if (FAILED(hr))
2526 {
2527 GATE_DEBUG_TRACE("Failed to access BaseFilter of SampleGrabber");
2528 GATE_DEBUG_TRACE_VALUE(hr);
2529 break;
2530 }
2531 hr = GATE_COM_IFACE(graph, &gate_IID_IMediaControl, &media_control);
2532 if (FAILED(hr))
2533 {
2534 GATE_DEBUG_TRACE("Failed to access MediaControl of GraphBuilder");
2535 GATE_DEBUG_TRACE_VALUE(hr);
2536 break;
2537 }
2538
2539 gate_mem_clear(&media_type, sizeof(media_type));
2540
2541 media_type.majortype = gate_MEDIATYPE_Video;
2542 media_type.subtype = gate_MEDIASUBTYPE_RGB32;
2543 grabber->lpVtbl->SetMediaType(grabber, &media_type);
2544 grabber->lpVtbl->SetOneShot(grabber, FALSE);
2545 grabber->lpVtbl->SetBufferSamples(grabber, FALSE);
2546 grabber->lpVtbl->SetCallback(grabber, callback, 1);
2547
2548 src_pins_used = dshow_find_pins_sorted(src_pins, sizeof(src_pins) / sizeof(src_pins[0]),
2549 src_filter, &direction_out, &const_false);
2550
2551 grabber_in_pins_used = dshow_find_pins(grabber_in_pins, sizeof(grabber_in_pins) / sizeof(grabber_in_pins[0]),
2552 grabber_filter, &direction_in, &const_false, NULL);
2553
2554 grabber_out_pins_used = dshow_find_pins(grabber_out_pins, sizeof(grabber_out_pins) / sizeof(grabber_out_pins[0]),
2555 grabber_filter, &direction_out, &const_false, NULL);
2556
2557 dst_pins_used = dshow_find_pins(dst_pins, sizeof(dst_pins) / sizeof(dst_pins[0]),
2558 dest_filter, &direction_in, &const_false, NULL);
2559
2560 if ((src_pins_used == 0) || (grabber_in_pins_used == 0))
2561 {
2562 GATE_DEBUG_TRACE("No source and grabber pins present");
2563 break;
2564 }
2565
2566 result = dshow_set_pin_format(src_pins[0], format);
2567 GATE_BREAK_IF_FAILED_TRACE(result, "Failed to set source pin format");
2568 result = GATE_RESULT_FAILED;
2569
2570 hr = graph->lpVtbl->Connect(graph, src_pins[0], grabber_in_pins[0]);
2571 if (FAILED(hr))
2572 {
2573 GATE_DEBUG_TRACE("Failed to connect source and grabber pins");
2574 break;
2575 }
2576 *ptr_src_out = src_pins[0];
2577 *ptr_grabber_in = grabber_in_pins[0];
2578 src_pins[0] = NULL;
2579 grabber_in_pins[0] = NULL;
2580
2581 if ((grabber_out_pins_used != 0) && (dst_pins_used != 0))
2582 {
2583 hr = graph->lpVtbl->Connect(graph, grabber_out_pins[0], dst_pins[0]);
2584 if (SUCCEEDED(hr))
2585 {
2586 *ptr_grabber_out = grabber_out_pins[0];
2587 *ptr_dest_in = dst_pins[0];
2588 grabber_out_pins[0] = NULL;
2589 dst_pins[0] = NULL;
2590 }
2591 }
2592
2593 hr = media_control->lpVtbl->Run(media_control);
2594 if (FAILED(hr))
2595 {
2596 dshow_end_session(graph, grabber, *ptr_src_out, *ptr_grabber_in, *ptr_grabber_out, *ptr_dest_in);
2597 GATE_COM_RELEASE(*ptr_src_out); *ptr_src_out = NULL;
2598 GATE_COM_RELEASE(*ptr_grabber_in); *ptr_grabber_in = NULL;
2599 GATE_COM_RELEASE(*ptr_grabber_out); *ptr_grabber_out = NULL;
2600 GATE_COM_RELEASE(*ptr_dest_in); *ptr_dest_in = NULL;
2601 GATE_DEBUG_TRACE("Failed to run MediaControl");
2602 break;
2603 }
2604 result = GATE_RESULT_OK;
2605 } while (0);
2606
2607 dshow_release_pin_array(src_pins, src_pins_used);
2608 dshow_release_pin_array(grabber_in_pins, grabber_in_pins_used);
2609 dshow_release_pin_array(grabber_out_pins, grabber_out_pins_used);
2610 dshow_release_pin_array(dst_pins, dst_pins_used);
2611 GATE_COM_RELEASE(grabber_filter);
2612 GATE_COM_RELEASE(media_control);
2613
2614 return result;
2615 }
2616
2617
2618 typedef struct gate_video_source_dshow_class
2619 {
2620 GATE_INTERFACE_VTBL(gate_video_source) const* vtbl;
2621
2622 gate_atomic_int_t ref_counter;
2623 dshow_video_device_info_t device_info;
2624 gate_win32_com_queue_t com_queue;
2625
2626 IBaseFilter* base_filter; /**< video source */
2627 IGraphBuilder* graph_builder; /**< rendering graph */
2628 ISampleGrabber* sample_grabber; /**< video target frame collector */
2629 IBaseFilter* null_filter; /**< currently unused */
2630
2631 gate_video_format_t video_format;
2632 ISampleGrabberCBImpl sample_grabber_cb; /**< sample grabber callback instance */
2633 } gate_video_source_dshow_t;
2634
2635 static char const* video_source_dshow_get_interface_name(void* obj);
2636 static void video_source_dshow_release(void* obj);
2637 static int video_source_dshow_retain(void* obj);
2638
2639 static char const* video_source_dshow_get_id(void* obj);
2640 static char const* video_source_dshow_get_name(void* obj);
2641 static gate_intptr_t video_source_dshow_get_handle(void* obj);
2642 static gate_size_t video_source_dshow_get_supported_formats(void* obj, gate_video_format_t* format_buffer, gate_size_t format_buffer_count);
2643
2644 static gate_result_t video_source_dshow_open(void* obj, gate_video_format_t const* format);
2645 static gate_result_t video_source_dshow_close(void* obj);
2646
2647 static gate_result_t video_source_dshow_read(void* obj, gate_video_frame_t* frame);
2648
2649 static GATE_INTERFACE_VTBL(gate_video_source) gate_video_source_dshow_vtbl;
2650 static void gate_init_video_source_dshow_vtbl()
2651 {
2652 if (!gate_video_source_dshow_vtbl.get_interface_name)
2653 {
2654 GATE_INTERFACE_VTBL(gate_video_source) const local_vtbl =
2655 {
2656 &video_source_dshow_get_interface_name,
2657 &video_source_dshow_release,
2658 &video_source_dshow_retain,
2659
2660 &video_source_dshow_read,
2661
2662 &video_source_dshow_get_id,
2663 &video_source_dshow_get_name,
2664 &video_source_dshow_get_handle,
2665 &video_source_dshow_get_supported_formats,
2666
2667 &video_source_dshow_open,
2668 &video_source_dshow_close
2669 };
2670 gate_video_source_dshow_vtbl = local_vtbl;
2671 }
2672 }
2673
2674
2675 static void video_source_dshow_destruct(gate_video_source_dshow_t* obj)
2676 {
2677 video_source_dshow_close(obj);
2678 gate_win32_com_queue_stop(&obj->com_queue);
2679 gate_win32_com_queue_destroy(&obj->com_queue);
2680 sample_grabber_cb_impl_destroy(&obj->sample_grabber_cb);
2681 }
2682
2683 static gate_video_source_dshow_t* video_source_dshow_construct(dshow_video_device_info_t const* info)
2684 {
2685 gate_video_source_dshow_t* ret = NULL;
2686 gate_video_source_dshow_t* obj = NULL;
2687 gate_result_t result;
2688
2689 do
2690 {
2691 obj = (gate_video_source_dshow_t*)gate_mem_alloc(sizeof(gate_video_source_dshow_t));
2692 if (obj == NULL)
2693 {
2694 break;
2695 }
2696
2697 gate_mem_clear(obj, sizeof(gate_video_source_dshow_t));
2698
2699 gate_init_video_source_dshow_vtbl();
2700 obj->vtbl = &gate_video_source_dshow_vtbl;
2701 gate_atomic_int_init(&obj->ref_counter, 1);
2702
2703 gate_mem_copy(&obj->device_info, info, sizeof(dshow_video_device_info_t));
2704
2705 result = sample_grabber_cb_impl_init(&obj->sample_grabber_cb);
2706 GATE_BREAK_IF_FAILED(result);
2707
2708 result = gate_win32_com_queue_create(&obj->com_queue);
2709 GATE_BREAK_IF_FAILED(result);
2710
2711 result = gate_win32_com_queue_start(&obj->com_queue);
2712 GATE_BREAK_IF_FAILED(result);
2713
2714 ret = obj;
2715 obj = NULL;
2716 } while (0);
2717
2718 if (obj != NULL)
2719 {
2720 video_source_dshow_destruct(obj);
2721 }
2722 return ret;
2723 }
2724
2725 static gate_result_t video_source_dshow_load(gate_video_source_dshow_t* this_ptr)
2726 {
2727 gate_result_t ret;
2728 gate_string_t device_id = GATE_STRING_INIT_EMPTY;
2729
2730 do
2731 {
2732 if ((this_ptr->base_filter != NULL) && (this_ptr->graph_builder != NULL))
2733 {
2734 /* already loaded */
2735 ret = GATE_RESULT_OK;
2736 }
2737 else
2738 {
2739 gate_string_create_static(&device_id, this_ptr->device_info.uid);
2740 ret = dshow_open_video_device(&device_id, &this_ptr->base_filter, &this_ptr->graph_builder,
2741 &this_ptr->sample_grabber, (ISampleGrabberCB*)&this_ptr->sample_grabber_cb,
2742 &this_ptr->null_filter);
2743 gate_string_release(&device_id);
2744 }
2745 } while (0);
2746 return ret;
2747 }
2748 static void video_source_dshow_unload(gate_video_source_dshow_t* this_ptr)
2749 {
2750 GATE_COM_RELEASE(this_ptr->null_filter);
2751 GATE_COM_RELEASE(this_ptr->sample_grabber);
2752 GATE_COM_RELEASE(this_ptr->graph_builder);
2753 GATE_COM_RELEASE(this_ptr->base_filter);
2754
2755 this_ptr->null_filter = NULL;
2756 this_ptr->sample_grabber = NULL;
2757 this_ptr->graph_builder = NULL;
2758 this_ptr->base_filter = NULL;
2759 }
2760
2761
2762 static char const* video_source_dshow_get_interface_name(void* obj)
2763 {
2764 gate_video_source_dshow_t* this_ptr = (gate_video_source_dshow_t*)obj;
2765 return GATE_INTERFACE_NAME_VIDEO_SOURCE;
2766 }
2767 static void video_source_dshow_release(void* obj)
2768 {
2769 gate_video_source_dshow_t* this_ptr = (gate_video_source_dshow_t*)obj;
2770 if (0 == gate_atomic_int_dec(&this_ptr->ref_counter))
2771 {
2772 video_source_dshow_destruct(this_ptr);
2773 gate_mem_dealloc(obj);
2774 }
2775 }
2776 static int video_source_dshow_retain(void* obj)
2777 {
2778 gate_video_source_dshow_t* this_ptr = (gate_video_source_dshow_t*)obj;
2779 return gate_atomic_int_inc(&this_ptr->ref_counter);
2780 }
2781
2782 static char const* video_source_dshow_get_id(void* obj)
2783 {
2784 gate_video_source_dshow_t* this_ptr = (gate_video_source_dshow_t*)obj;
2785 return this_ptr->device_info.uid;
2786 }
2787 static char const* video_source_dshow_get_name(void* obj)
2788 {
2789 gate_video_source_dshow_t* this_ptr = (gate_video_source_dshow_t*)obj;
2790 return this_ptr->device_info.name;
2791 }
2792 static gate_intptr_t video_source_dshow_get_handle(void* obj)
2793 {
2794 gate_video_source_dshow_t* this_ptr = (gate_video_source_dshow_t*)obj;
2795 return this_ptr->device_info.handle;
2796 }
2797
2798 struct dshow_get_supported_formats_params
2799 {
2800 gate_video_source_dshow_t* video_source;
2801 gate_video_format_t* format_buffer;
2802 gate_size_t format_buffer_count;
2803 gate_size_t return_value;
2804 };
2805
2806 static gate_result_t dshow_get_supported_formats(void* ptr_params)
2807 {
2808 struct dshow_get_supported_formats_params* params = (struct dshow_get_supported_formats_params*)ptr_params;
2809 gate_result_t ret;
2810 IPin* pins[16] = GATE_INIT_EMPTY;
2811 gate_size_t pin_count = sizeof(pins) / sizeof(pins[0]);
2812 gate_size_t pins_used = 0;
2813 gate_size_t index;
2814 PIN_DIRECTION pin_dir = PINDIR_OUTPUT;
2815 gate_bool_t connected_pins = false;
2816 gate_bool_t preview_pins = false;
2817 gate_size_t formats_used = 0;
2818 gate_size_t formats_found;
2819
2820
2821 do
2822 {
2823 ret = video_source_dshow_load(params->video_source);
2824 GATE_BREAK_IF_FAILED(ret);
2825
2826 pins_used = dshow_find_pins(pins, pin_count, params->video_source->base_filter, &pin_dir, &connected_pins, &preview_pins);
2827 for (index = 0; index != pins_used; ++index)
2828 {
2829 formats_found = dshow_read_supported_pin_formats(
2830 pins[index], &params->format_buffer[formats_used], params->format_buffer_count - formats_used);
2831 formats_used += formats_found;
2832 }
2833 params->return_value = formats_used;
2834 } while (0);
2835
2836 for (index = 0; index != pins_used; ++index)
2837 {
2838 GATE_COM_RELEASE(pins[index]);
2839 }
2840 return ret;
2841 }
2842
2843 static gate_size_t video_source_dshow_get_supported_formats(void* obj, gate_video_format_t* format_buffer, gate_size_t format_buffer_count)
2844 {
2845 gate_video_source_dshow_t* this_ptr = (gate_video_source_dshow_t*)obj;
2846 gate_result_t ret = GATE_RESULT_NOTIMPLEMENTED;
2847 gate_result_t func_result = GATE_RESULT_FAILED;
2848 struct dshow_get_supported_formats_params params;
2849
2850 params.video_source = this_ptr;
2851 params.format_buffer = format_buffer;
2852 params.format_buffer_count = format_buffer_count;
2853 params.return_value = 0;
2854
2855 ret = gate_win32_com_queue_execute(&this_ptr->com_queue, &dshow_get_supported_formats, &params, &func_result);
2856 if (GATE_FAILED(ret) || GATE_FAILED(func_result))
2857 {
2858 return 0;
2859 }
2860 else
2861 {
2862 return params.return_value;
2863 }
2864 }
2865
2866 struct dshow_open_params
2867 {
2868 gate_video_source_dshow_t* video_source;
2869 gate_video_format_t const* format;
2870 };
2871
2872 static gate_result_t dshow_open(void* ptr_params)
2873 {
2874 struct dshow_open_params* params = (struct dshow_open_params*)ptr_params;
2875 gate_result_t ret = GATE_RESULT_FAILED;
2876 IMediaControl* ptr_media_control = NULL;
2877 HRESULT hr;
2878 IBaseFilter* grabber_filter = NULL;
2879 IPin* source_output_pin = NULL;
2880 IPin* grabber_input_pin = NULL;
2881
2882 do
2883 {
2884 ret = video_source_dshow_load(params->video_source);
2885 GATE_BREAK_IF_FAILED(ret);
2886
2887 hr = params->video_source->sample_grabber->lpVtbl->QueryInterface(
2888 params->video_source->sample_grabber, &gate_IID_IBaseFilter, (LPVOID*)&grabber_filter);
2889 if (FAILED(hr))
2890 {
2891 ret = GATE_RESULT_NOTAVAILABLE;
2892 break;
2893 }
2894
2895 gate_mem_copy(&params->video_source->video_format, params->format, sizeof(params->video_source->video_format));
2896
2897 ret = dshow_connect_filter_pins(params->video_source->graph_builder,
2898 params->video_source->base_filter,
2899 grabber_filter,
2900 &params->video_source->video_format,
2901 &source_output_pin,
2902 &grabber_input_pin);
2903 GATE_BREAK_IF_FAILED(ret);
2904
2905
2906 hr = params->video_source->graph_builder->lpVtbl->QueryInterface(
2907 params->video_source->graph_builder, &gate_IID_IMediaControl, (void**)&ptr_media_control);
2908 if (FAILED(hr))
2909 {
2910 ret = GATE_RESULT_NOTAVAILABLE;
2911 break;
2912 }
2913
2914 hr = ptr_media_control->lpVtbl->Run(ptr_media_control);
2915 if (FAILED(hr))
2916 {
2917 ret = GATE_RESULT_FAILED;
2918 break;
2919 }
2920
2921 ret = GATE_RESULT_OK;
2922 } while (0);
2923
2924 GATE_COM_RELEASE(ptr_media_control);
2925 GATE_COM_RELEASE(source_output_pin);
2926 GATE_COM_RELEASE(grabber_input_pin);
2927 GATE_COM_RELEASE(grabber_filter);
2928
2929 return ret;
2930 }
2931
2932 static gate_result_t video_source_dshow_open(void* obj, gate_video_format_t const* format)
2933 {
2934 gate_video_source_dshow_t* this_ptr = (gate_video_source_dshow_t*)obj;
2935 gate_result_t ret = GATE_RESULT_NOTIMPLEMENTED;
2936 gate_result_t func_result = GATE_RESULT_FAILED;
2937 struct dshow_open_params params;
2938 params.video_source = this_ptr;
2939 params.format = format;
2940
2941 ret = gate_win32_com_queue_execute(&this_ptr->com_queue, &dshow_open, &params, &func_result);
2942 if (GATE_SUCCEEDED(ret))
2943 {
2944 ret = func_result;
2945 }
2946 return ret;
2947 }
2948
2949 static gate_result_t dshow_close(void* ptr_param)
2950 {
2951 gate_video_source_dshow_t* this_ptr = (gate_video_source_dshow_t*)ptr_param;
2952 gate_result_t ret = GATE_RESULT_FAILED;
2953 IMediaControl* ptr_media_control = NULL;
2954 HRESULT hr;
2955
2956 do
2957 {
2958 if (this_ptr->graph_builder != NULL)
2959 {
2960 hr = this_ptr->graph_builder->lpVtbl->QueryInterface(
2961 this_ptr->graph_builder, &gate_IID_IMediaControl, (void**)&ptr_media_control);
2962 if (SUCCEEDED(hr))
2963 {
2964 hr = ptr_media_control->lpVtbl->Stop(ptr_media_control);
2965 GATE_COM_RELEASE(ptr_media_control);
2966 }
2967 }
2968
2969 if (this_ptr->base_filter)
2970 {
2971 if (this_ptr->graph_builder != NULL)
2972 {
2973 hr = this_ptr->graph_builder->lpVtbl->RemoveFilter(this_ptr->graph_builder, this_ptr->base_filter);
2974 }
2975 GATE_COM_RELEASE(this_ptr->base_filter);
2976 this_ptr->base_filter = NULL;
2977 }
2978
2979 if (this_ptr->sample_grabber)
2980 {
2981 GATE_COM_RELEASE(this_ptr->sample_grabber);
2982 this_ptr->sample_grabber = NULL;
2983 }
2984
2985 if (this_ptr->null_filter)
2986 {
2987 if (this_ptr->graph_builder != NULL)
2988 {
2989 hr = this_ptr->graph_builder->lpVtbl->RemoveFilter(this_ptr->graph_builder, this_ptr->null_filter);
2990 }
2991 GATE_COM_RELEASE(this_ptr->null_filter);
2992 this_ptr->null_filter = NULL;
2993 }
2994
2995 if (this_ptr->graph_builder != NULL)
2996 {
2997 GATE_COM_RELEASE(this_ptr->graph_builder);
2998 this_ptr->graph_builder = NULL;
2999 }
3000
3001 ret = GATE_RESULT_OK;
3002 } while (0);
3003
3004 return ret;
3005 }
3006
3007 static gate_result_t video_source_dshow_close(void* obj)
3008 {
3009 gate_video_source_dshow_t* this_ptr = (gate_video_source_dshow_t*)obj;
3010 gate_result_t ret = GATE_RESULT_NOTIMPLEMENTED;
3011 gate_result_t func_result = GATE_RESULT_FAILED;
3012
3013 ret = gate_win32_com_queue_execute(&this_ptr->com_queue, &dshow_close, this_ptr, &func_result);
3014 if (GATE_SUCCEEDED(ret))
3015 {
3016 ret = func_result;
3017 }
3018 return ret;
3019 }
3020
3021
3022 static gate_result_t video_source_dshow_read(void* obj, gate_video_frame_t* frame)
3023 {
3024 gate_video_source_dshow_t* this_ptr = (gate_video_source_dshow_t*)obj;
3025 gate_result_t ret = GATE_RESULT_NOTIMPLEMENTED;
3026
3027 do
3028 {
3029 if (!this_ptr || !frame)
3030 {
3031 ret = GATE_RESULT_NULLPOINTER;
3032 break;
3033 }
3034 gate_mem_clear(frame, sizeof(gate_video_frame_t));
3035
3036 gate_mem_copy(&frame->format, &this_ptr->video_format, sizeof(gate_video_format_t));
3037
3038 ret = sample_grabber_export_image(&this_ptr->sample_grabber_cb, frame);
3039
3040 } while (0);
3041
3042 return ret;
3043 }
3044
3045 struct dshow_read_async_params
3046 {
3047 gate_video_source_dshow_t* video_source;
3048 gate_delegate_t const* completion;
3049 };
3050
3051 static gate_result_t dshow_read_async(void* ptr_params)
3052 {
3053 struct dshow_read_async_params* params = (struct dshow_read_async_params*)ptr_params;
3054 gate_result_t ret = GATE_RESULT_FAILED;
3055 /* TODO */
3056 return ret;
3057 }
3058
3059 static gate_result_t video_source_dshow_read_async(void* obj, gate_delegate_t const* completion)
3060 {
3061 gate_video_source_dshow_t* this_ptr = (gate_video_source_dshow_t*)obj;
3062 gate_result_t ret = GATE_RESULT_NOTIMPLEMENTED;
3063 gate_result_t func_result = GATE_RESULT_FAILED;
3064
3065 struct dshow_read_async_params params;
3066 params.video_source = this_ptr;
3067 params.completion = completion;
3068
3069 ret = gate_win32_com_queue_execute(&this_ptr->com_queue, &dshow_read_async, &params, &func_result);
3070 if (GATE_SUCCEEDED(ret))
3071 {
3072 ret = func_result;
3073 }
3074 return ret;
3075 }
3076
3077
3078 #endif
3079
3080
3081
3082 gate_result_t gate_video_sources_enum(gate_delegate_t const* callback, void* param)
3083 {
3084 gate_result_t result = GATE_RESULT_OK;
3085 UINT index;
3086 gate_video_source_t* impl = NULL;
3087 do
3088 {
3089
3090 #if defined(GATE_IO_VIDEOSOURCES_USE_DSHOW)
3091 result = GATE_RESULT_OK;
3092
3093 do
3094 {
3095 gate_size_t count;
3096 dshow_video_device_info_t infos[24];
3097 gate_video_source_dshow_t* dshow_impl;
3098
3099 count = dshow_list_video_devices(infos, sizeof(infos) / sizeof(infos[0]));
3100 for (index = 0; index != count; ++index)
3101 {
3102 dshow_impl = video_source_dshow_construct(&infos[index]);
3103 if (dshow_impl != NULL)
3104 {
3105 if (callback != NULL)
3106 {
3107 impl = (gate_video_source_t*)dshow_impl;
3108 result = gate_delegate_invoke(callback, impl, param);
3109 gate_object_release(impl);
3110 }
3111 GATE_BREAK_IF_FAILED(result);
3112 }
3113 }
3114 } while (0);
3115 GATE_BREAK_IF_FAILED(result);
3116 #endif
3117
3118 #if defined(GATE_IO_VIDEOSOURCES_USE_V4W)
3119 do
3120 {
3121 result = load_vfw_functions();
3122 GATE_BREAK_IF_FAILED_TRACE(result, "Failed to load VFW functions");
3123 if (!capGetDriverDescriptionFunc)
3124 {
3125 result = GATE_RESULT_NOTSUPPORTED;
3126 break;
3127 }
3128 for (index = 0; index < 10; ++index)
3129 {
3130 result = get_vfw_source(index, &impl);
3131 if (result == GATE_RESULT_NOMATCH)
3132 {
3133 /* this index is not available */
3134 result = GATE_RESULT_OK;
3135 continue;
3136 }
3137 GATE_BREAK_IF_FAILED(result);
3138
3139 if (callback != NULL)
3140 {
3141 result = gate_delegate_invoke(callback, impl, param);
3142 }
3143 gate_object_release(impl);
3144 GATE_BREAK_IF_FAILED(result);
3145 }
3146 } while (0);
3147 GATE_BREAK_IF_FAILED(result);
3148 #endif
3149
3150
3151 } while (0);
3152 return result;
3153 }
3154
3155 gate_result_t gate_video_source_by_id(char const* id, gate_video_source_t** ptr_source)
3156 {
3157 gate_result_t result = GATE_RESULT_NOMATCH;
3158 gate_size_t id_len = gate_str_length(id);
3159 gate_uint8_t vfw_id = 0;
3160 gate_video_source_t* source = NULL;
3161
3162 do
3163 {
3164 #if defined(GATE_IO_VIDEOSOURCES_USE_V4W)
3165 if (id_len == 4)
3166 {
3167 if ((id[0] == 'v') && (id[1] == 'f') && (id[2] == 'w'))
3168 {
3169 if (gate_str_parse_digit(&id[3], &vfw_id))
3170 {
3171 result = load_vfw_functions();
3172 GATE_BREAK_IF_FAILED_TRACE(result, "Failed to load VFW functions");
3173 result = get_vfw_source(vfw_id, &source);
3174 GATE_BREAK_IF_FAILED(result);
3175 }
3176 }
3177 }
3178 #endif
3179 #if defined(GATE_IO_VIDEOSOURCES_USE_DSHOW)
3180 do
3181 {
3182 gate_size_t index;
3183 gate_size_t count;
3184 dshow_video_device_info_t infos[24];
3185 gate_video_source_dshow_t* dshow_impl;
3186
3187 result = GATE_RESULT_NOMATCH;
3188 count = dshow_list_video_devices(infos, sizeof(infos) / sizeof(infos[0]));
3189 for (index = 0; index != count; ++index)
3190 {
3191 if (0 == gate_str_comp(infos[index].uid, id))
3192 {
3193 result = GATE_RESULT_OK;
3194 if (ptr_source)
3195 {
3196 dshow_impl = video_source_dshow_construct(&infos[index]);
3197 if (dshow_impl != NULL)
3198 {
3199 source = (gate_video_source_t*)dshow_impl;
3200 }
3201 else
3202 {
3203 result = GATE_RESULT_FAILED;
3204 }
3205 }
3206 break;
3207 }
3208 }
3209 } while (0);
3210 #endif
3211
3212 } while (0);
3213
3214 if (source != NULL)
3215 {
3216 if (ptr_source != NULL)
3217 {
3218 *ptr_source = source;
3219 }
3220 else
3221 {
3222 gate_object_release(source);
3223 }
3224 result = GATE_RESULT_OK;
3225 }
3226 return result;
3227 }
3228 #endif /* GATE_IO_VIDEO_WINAPI */
3229
3230
3231
3232
3233
3234
3235
3236 #if defined(GATE_IO_VIDEO_V4L)
3237
3238 # include "gate/files.h"
3239 # include "gate/platforms.h"
3240 # include "gate/threading.h"
3241 # include "gate/libraries.h"
3242
3243 # include <sys/mman.h>
3244 # include <sys/types.h>
3245 # include <sys/stat.h>
3246 # include <string.h>
3247 # include <stdlib.h>
3248 # include <errno.h>
3249 # include <fcntl.h>
3250 # include <linux/videodev2.h>
3251
3252 #if defined(HAVE_LIBV4LCONVERT_HEADER)
3253 # include <libv4lconvert.h> // package: libv4l-dev
3254 #endif
3255 # include <libv4l2.h>
3256
3257
3258 struct gate_v4l_convert_code
3259 {
3260 void const* (*get_default_dev_ops) ();
3261 void* (*create) (int fd);
3262 void* (*create_with_dev_ops) (int fd, void* dev_ops_priv, void const* dev_ops);
3263 void (*destroy) (void* data);
3264 int (*supported_dst_fmt_only) (void* data);
3265 int (*try_format) (void* data, struct v4l2_format* dest_fmt, struct v4l2_format* src_fmt);
3266 int (*enum_fmt) (void* data, struct v4l2_fmtdesc* fmt);
3267 int (*needs_conversion) (void* data, const struct v4l2_format* src_fmt, const struct v4l2_format* dest_fmt);
3268 int (*convert) (void* data, const struct v4l2_format* src_fmt, const struct v4l2_format* dest_fmt, unsigned char* src, int src_size, unsigned char* dest, int dest_size);
3269 const char* (*get_error_message) (void* data);
3270 int (*enum_framesizes) (void* data, struct v4l2_frmsizeenum* frmsize);
3271 int (*enum_frameintervals) (void* data, struct v4l2_frmivalenum* frmival);
3272 int (*vidioc_queryctrl) (void* data, void* arg);
3273 int (*vidioc_g_ctrl) (void* data, void* arg);
3274 int (*vidioc_s_ctrl) (void* data, void* arg);
3275 int (*supported_dst_format) (unsigned int pixelformat);
3276 int (*get_fps) (void* data);
3277 void (*set_fps) (void* data, int fps);
3278 };
3279
3280 static struct gate_v4l_convert_code v4l_convert = GATE_INIT_EMPTY;
3281
3282 static gate_result_t gate_vfl_convert_init()
3283 {
3284 static gate_library_t convert_lib = NULL;
3285 static gate_bool_t volatile convert_lib_loaded = false;
3286 static gate_string_t const convert_lib_name = GATE_STRING_INIT_STATIC("libv4lconvert.so");
3287
3288 gate_result_t ret = GATE_RESULT_FAILED;
3289
3290 do
3291 {
3292 if (convert_lib_loaded)
3293 {
3294 ret = GATE_RESULT_OK;
3295 break;
3296 }
3297
3298 if (convert_lib == NULL)
3299 {
3300 ret = gate_library_open(&convert_lib_name, &convert_lib, GATE_LIBRARY_FLAG_DEFAULT);
3301 GATE_BREAK_IF_FAILED(ret);
3302 }
3303 ret = gate_library_get_function_name(convert_lib, "v4lconvert_create", &v4l_convert.create);
3304 GATE_BREAK_IF_FAILED(ret);
3305 ret = gate_library_get_function_name(convert_lib, "v4lconvert_destroy", &v4l_convert.destroy);
3306 GATE_BREAK_IF_FAILED(ret);
3307 ret = gate_library_get_function_name(convert_lib, "v4lconvert_supported_dst_fmt_only",
3308 &v4l_convert.supported_dst_fmt_only);
3309 GATE_BREAK_IF_FAILED(ret);
3310 ret = gate_library_get_function_name(convert_lib, "v4lconvert_try_format", &v4l_convert.try_format);
3311 GATE_BREAK_IF_FAILED(ret);
3312 ret = gate_library_get_function_name(convert_lib, "v4lconvert_enum_fmt", &v4l_convert.enum_fmt);
3313 GATE_BREAK_IF_FAILED(ret);
3314 ret = gate_library_get_function_name(convert_lib, "v4lconvert_needs_conversion", &v4l_convert.needs_conversion);
3315 GATE_BREAK_IF_FAILED(ret);
3316 ret = gate_library_get_function_name(convert_lib, "v4lconvert_convert", &v4l_convert.convert);
3317 GATE_BREAK_IF_FAILED(ret);
3318 ret = gate_library_get_function_name(convert_lib, "v4lconvert_get_error_message", &v4l_convert.get_error_message);
3319 GATE_BREAK_IF_FAILED(ret);
3320 ret = gate_library_get_function_name(convert_lib, "v4lconvert_enum_framesizes", &v4l_convert.enum_framesizes);
3321 GATE_BREAK_IF_FAILED(ret);
3322 ret = gate_library_get_function_name(convert_lib, "v4lconvert_enum_frameintervals", &v4l_convert.enum_frameintervals);
3323 GATE_BREAK_IF_FAILED(ret);
3324 ret = gate_library_get_function_name(convert_lib, "v4lconvert_vidioc_queryctrl", &v4l_convert.vidioc_queryctrl);
3325 GATE_BREAK_IF_FAILED(ret);
3326 ret = gate_library_get_function_name(convert_lib, "v4lconvert_vidioc_g_ctrl", &v4l_convert.vidioc_g_ctrl);
3327 GATE_BREAK_IF_FAILED(ret);
3328 ret = gate_library_get_function_name(convert_lib, "v4lconvert_vidioc_s_ctrl", &v4l_convert.vidioc_s_ctrl);
3329 GATE_BREAK_IF_FAILED(ret);
3330 ret = gate_library_get_function_name(convert_lib, "v4lconvert_supported_dst_format", &v4l_convert.supported_dst_format);
3331 GATE_BREAK_IF_FAILED(ret);
3332 ret = gate_library_get_function_name(convert_lib, "v4lconvert_get_fps", &v4l_convert.get_fps);
3333 GATE_BREAK_IF_FAILED(ret);
3334 ret = gate_library_get_function_name(convert_lib, "v4lconvert_set_fps", &v4l_convert.set_fps);
3335 GATE_BREAK_IF_FAILED(ret);
3336
3337 convert_lib_loaded = true;
3338 } while (0);
3339
3340 return ret;
3341 }
3342
3343
3344
3345 static void gate_video_vfl_release(void* self);
3346 static int gate_video_vfl_retain(void* self);
3347 static char const* gate_video_vfl_get_interface_name(void* self);
3348
3349 static char const* gate_video_vfl_get_id(void* self);
3350 static char const* gate_video_vfl_get_name(void* self);
3351 static gate_intptr_t gate_video_vfl_get_handle(void* self);
3352 static gate_size_t gate_video_vfl_get_supported_formats(void* self, gate_video_format_t* format_buffer, gate_size_t format_buffer_count);
3353
3354 static gate_result_t gate_video_vfl_open(void* self, gate_video_format_t const* format);
3355 static gate_result_t gate_video_vfl_close(void* self);
3356
3357 static gate_result_t gate_video_vfl_read(void* self, gate_video_frame_t* frame);
3358
3359 static GATE_INTERFACE_VTBL(gate_video_source) gate_video_vfl_vtbl_impl;
3360 static void gate_init_video_vfl_vtbl_impl()
3361 {
3362 if (!gate_video_vfl_vtbl_impl.get_interface_name)
3363 {
3364 GATE_INTERFACE_VTBL(gate_video_source) const local_vtbl =
3365 {
3366 &gate_video_vfl_get_interface_name,
3367 &gate_video_vfl_release,
3368 &gate_video_vfl_retain,
3369
3370 &gate_video_vfl_read,
3371
3372 &gate_video_vfl_get_id,
3373 &gate_video_vfl_get_name,
3374 &gate_video_vfl_get_handle,
3375 &gate_video_vfl_get_supported_formats,
3376
3377 &gate_video_vfl_open,
3378 &gate_video_vfl_close
3379 };
3380 gate_video_vfl_vtbl_impl = local_vtbl;
3381 }
3382 }
3383
3384 #define GATE_VIDEO_VFL_STATE_OFFLINE 0
3385 #define GATE_VIDEO_VFL_STATE_STARTING 1
3386 #define GATE_VIDEO_VFL_STATE_RUNNING 2
3387 #define GATE_VIDEO_VFL_STATE_STOPPING 3
3388 #define GATE_VIDEO_VFL_STATE_ERROR 4
3389
3390
3391 #define GATE_VIDEO_VFL_TYPE_NONE 0
3392 #define GATE_VIDEO_VFL_TYPE_USERPTR 1
3393 #define GATE_VIDEO_VFL_TYPE_MMAP 2
3394 #define GATE_VIDEO_VFL_TYPE_READ 3
3395
3396 typedef struct
3397 {
3398 void* data;
3399 gate_size_t length;
3400 } gate_video_vfl_buffer_t;
3401
3402 typedef struct
3403 {
3404 GATE_INTERFACE_VTBL(gate_video_source) const* vtbl;
3405
3406 gate_atomic_int_t ref_counter;
3407 char id[256];
3408 char name[256];
3409 gate_intptr_t handle;
3410 gate_video_format_t selected_format;
3411
3412 struct v4l2_format source_format;
3413 struct v4l2_format target_format;
3414 void* converter;
3415
3416 gate_atomic_int_t capture_state;
3417 gate_mutex_t capture_mutex;
3418 gate_thread_t capture_thread;
3419 gate_thread_id_t capture_thread_id;
3420 int capture_type;
3421 gate_size_t capture_frame_size;
3422 gate_video_vfl_buffer_t capture_buffers[4];
3423 gate_size_t capture_buffers_used;
3424 gate_video_vfl_buffer_t capture_current_image;
3425
3426 } gate_video_vfl_impl_t;
3427
3428 static void gate_video_vfl_destruct(gate_video_vfl_impl_t* impl)
3429 {
3430 gate_video_vfl_close(impl);
3431
3432 gate_mutex_destroy(&impl->capture_mutex);
3433 }
3434
3435 static void gate_video_vfl_release(void* self)
3436 {
3437 gate_video_vfl_impl_t* impl = (gate_video_vfl_impl_t*)self;
3438 if (gate_atomic_int_dec(&impl->ref_counter) == 0)
3439 {
3440 gate_video_vfl_destruct(impl);
3441 gate_mem_dealloc(impl);
3442 }
3443 }
3444 static int gate_video_vfl_retain(void* self)
3445 {
3446 gate_video_vfl_impl_t* impl = (gate_video_vfl_impl_t*)self;
3447 return gate_atomic_int_inc(&impl->ref_counter);
3448 }
3449 static char const* gate_video_vfl_get_interface_name(void* self)
3450 {
3451 (void)self;
3452 return GATE_INTERFACE_NAME_VIDEO_SOURCE;
3453 }
3454
3455 static char const* gate_video_vfl_get_id(void* self)
3456 {
3457 gate_video_vfl_impl_t* impl = (gate_video_vfl_impl_t*)self;
3458 return impl->id;
3459 }
3460 static char const* gate_video_vfl_get_name(void* self)
3461 {
3462 gate_video_vfl_impl_t* impl = (gate_video_vfl_impl_t*)self;
3463 return impl->name;
3464 }
3465 static gate_intptr_t gate_video_vfl_get_handle(void* self)
3466 {
3467 gate_video_vfl_impl_t* impl = (gate_video_vfl_impl_t*)self;
3468 return impl->handle;
3469 }
3470 static gate_size_t gate_video_vfl_get_supported_formats(void* self, gate_video_format_t* format_buffer, gate_size_t format_buffer_count)
3471 {
3472 gate_size_t ret = 0;
3473 gate_video_vfl_impl_t* impl = (gate_video_vfl_impl_t*)self;
3474 int fd = -1;
3475 struct v4l2_format fmt;
3476 struct v4l2_fmtdesc descr;
3477 gate_uint32_t index;
3478 int result;
3479
3480 do
3481 {
3482 fd = gate_posix_open(impl->id, O_RDONLY, 0);
3483 if (fd == -1)
3484 {
3485 break;
3486 }
3487 gate_mem_clear(&fmt, sizeof(fmt));
3488 fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
3489 if (-1 == gate_posix_ioctl(fd, VIDIOC_G_FMT, &fmt))
3490 {
3491 break;
3492 }
3493
3494 for (index = 0; format_buffer_count > 0; ++index)
3495 {
3496 descr.index = index;
3497 descr.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
3498 result = gate_posix_ioctl(fd, VIDIOC_ENUM_FMT, &descr);
3499 if (result != 0)
3500 {
3501 break;
3502 }
3503
3504 format_buffer->bits_per_pixel = fmt.fmt.pix.bytesperline / fmt.fmt.pix.width;
3505 format_buffer->frames_per_second = 0;
3506 format_buffer->encoding_id = descr.pixelformat;
3507 format_buffer->compression = descr.flags;
3508 format_buffer->width = fmt.fmt.pix.width;
3509 format_buffer->height = fmt.fmt.pix.height;
3510
3511 --format_buffer_count;
3512 ++format_buffer;
3513 ++ret;
3514 }
3515 } while (0);
3516
3517 if (fd != -1)
3518 {
3519 gate_posix_close(fd);
3520 }
3521
3522 return ret;
3523 }
3524
3525 static gate_result_t gate_video_vfl_buffer_create(gate_video_vfl_buffer_t* buffer, gate_size_t length)
3526 {
3527 GATE_DEBUG_ASSERT(buffer != NULL);
3528 GATE_DEBUG_ASSERT(length > 0);
3529 buffer->data = gate_mem_alloc(length);
3530 if (buffer->data == NULL)
3531 {
3532 return GATE_RESULT_OUTOFMEMORY;
3533 }
3534 else
3535 {
3536 gate_mem_clear(buffer->data, length);
3537 buffer->length = length;
3538 return GATE_RESULT_OK;
3539 }
3540 }
3541 static void gate_video_vfl_buffer_destroy(gate_video_vfl_buffer_t* buffer)
3542 {
3543 GATE_DEBUG_ASSERT(buffer != NULL);
3544 if (buffer->data != NULL)
3545 {
3546 gate_mem_dealloc(buffer->data);
3547 buffer->data = NULL;
3548 }
3549 buffer->length = 0;
3550 }
3551
3552 static void gate_video_vfl_unmap_buffers(gate_video_vfl_buffer_t* buffer, gate_size_t count)
3553 {
3554 GATE_DEBUG_ASSERT(buffer != NULL);
3555 while (count-- != 0)
3556 {
3557 munmap(buffer->data, buffer->length);
3558 ++buffer;
3559 }
3560 }
3561
3562 static gate_result_t gate_video_vfl_shutdown_session(gate_video_vfl_impl_t* impl)
3563 {
3564 gate_result_t ret = GATE_RESULT_OK;
3565 enum v4l2_buf_type buf_type;
3566 int fd = impl->handle;
3567
3568 impl->handle = -1;
3569
3570 switch (impl->capture_type)
3571 {
3572 case GATE_VIDEO_VFL_TYPE_USERPTR:
3573 {
3574 /* disable streaming */
3575 buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
3576 if (-1 == gate_posix_ioctl(fd, VIDIOC_STREAMOFF, &buf_type))
3577 {
3578 /* failed, but we can't do anything here ... */
3579 GATE_DEBUG_TRACE("gate_posix_ioctl(VIDIOC_STREAMOFF) failed");
3580 ret = GATE_RESULT_FAILED;
3581 }
3582 /* memory can be reused, it is managed by the lifetime of this VflCaptureDriver instance */
3583 break;
3584 }
3585 case GATE_VIDEO_VFL_TYPE_MMAP:
3586 {
3587 /* disable streaming */
3588 buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
3589 if (-1 == gate_posix_ioctl(fd, VIDIOC_STREAMOFF, &buf_type))
3590 {
3591 /* failed, but we can't do anything here ... */
3592 ret = GATE_RESULT_FAILED;
3593 }
3594 /* deallocate memory */
3595 gate_video_vfl_unmap_buffers(impl->capture_buffers, impl->capture_buffers_used);
3596 gate_mem_clear(impl->capture_buffers, sizeof(impl->capture_buffers));
3597 impl->capture_buffers_used = 0;
3598 break;
3599 }
3600 case GATE_VIDEO_VFL_TYPE_READ:
3601 {
3602 /* nothing to do */
3603 break;
3604 }
3605 }
3606
3607 gate_posix_close(fd);
3608 return ret;
3609 }
3610
3611 static gate_result_t gate_video_vfl_thread(void* ptr)
3612 {
3613 gate_result_t ret = GATE_RESULT_OK;
3614 gate_video_vfl_impl_t* impl = (gate_video_vfl_impl_t*)ptr;
3615
3616 int fd = (int)impl->handle;
3617 int capture_type = impl->capture_type;
3618 gate_size_t image_size = impl->source_format.fmt.pix.sizeimage;
3619 struct v4l2_buffer buf;
3620
3621 struct timeval tv;
3622 fd_set fds;
3623 int result;
3624 gate_result_t res;
3625 gate_video_vfl_buffer_t const* current_buffer;
3626 ssize_t bytes_read;
3627
3628 do
3629 {
3630 // initialize capture data buffer
3631 res = gate_mutex_acquire(&impl->capture_mutex);
3632 ret = gate_video_vfl_buffer_create(&impl->capture_current_image, image_size);
3633 if (GATE_SUCCEEDED(res))
3634 {
3635 gate_mutex_release(&impl->capture_mutex);
3636 }
3637
3638 GATE_BREAK_IF_FAILED(ret);
3639
3640 if (GATE_VIDEO_VFL_STATE_STARTING != gate_atomic_int_xchg_if(&impl->capture_state,
3641 GATE_VIDEO_VFL_STATE_STARTING, GATE_VIDEO_VFL_STATE_RUNNING))
3642 {
3643 break;
3644 }
3645
3646 /* capture stage */
3647 while (gate_atomic_int_get(&impl->capture_state) == GATE_VIDEO_VFL_STATE_RUNNING)
3648 {
3649 FD_ZERO(&fds);
3650 FD_SET(fd, &fds);
3651
3652 tv.tv_sec = 0;
3653 tv.tv_usec = 250000;
3654
3655 result = select(fd + 1, &fds, NULL, NULL, &tv);
3656 if (-1 == result)
3657 {
3658 gate_atomic_int_set(&impl->capture_state, GATE_VIDEO_VFL_STATE_ERROR);
3659 /* critical error */
3660 break;
3661 }
3662
3663 if (0 == result)
3664 {
3665 /* no frame or data retrieved */
3666 continue;
3667 }
3668
3669 switch (capture_type)
3670 {
3671 case GATE_VIDEO_VFL_TYPE_USERPTR:
3672 {
3673 gate_mem_clear(&buf, sizeof(buf));
3674 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
3675 buf.memory = V4L2_MEMORY_USERPTR;
3676 if (0 == gate_posix_ioctl(fd, VIDIOC_DQBUF, &buf))
3677 {
3678 /*this->processImageData((char*)buf.m.userptr, buf.bytesused, &buf);*/
3679 res = gate_mutex_acquire(&impl->capture_mutex);
3680 if (GATE_SUCCEEDED(res))
3681 {
3682 gate_mem_copy(impl->capture_current_image.data, (char const*)buf.m.userptr,
3683 impl->capture_frame_size);
3684 gate_mutex_release(&impl->capture_mutex);
3685 }
3686 gate_posix_ioctl(fd, VIDIOC_QBUF, &buf);
3687 }
3688 break;
3689 }
3690 case GATE_VIDEO_VFL_TYPE_MMAP:
3691 {
3692 gate_mem_clear(&buf, sizeof(buf));
3693 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
3694 buf.memory = V4L2_MEMORY_MMAP;
3695 if (0 == gate_posix_ioctl(fd, VIDIOC_DQBUF, &buf))
3696 {
3697 if (buf.index < sizeof(impl->capture_buffers) / sizeof(impl->capture_buffers[0]))
3698 {
3699 current_buffer = &impl->capture_buffers[buf.index];
3700 /*this->processImageData((char*)info.Data, info.Length, NULL);*/
3701 res = gate_mutex_acquire(&impl->capture_mutex);
3702 if (GATE_SUCCEEDED(res))
3703 {
3704 gate_mem_copy(impl->capture_current_image.data, current_buffer->data, impl->capture_frame_size);
3705 gate_mutex_release(&impl->capture_mutex);
3706 }
3707 }
3708 gate_posix_ioctl(fd, VIDIOC_QBUF, &buf);
3709 }
3710 break;
3711 }
3712 case GATE_VIDEO_VFL_TYPE_READ:
3713 {
3714 res = gate_mutex_acquire(&impl->capture_mutex);
3715 if (GATE_SUCCEEDED(res))
3716 {
3717 bytes_read = gate_posix_read(fd, impl->capture_current_image.data, impl->capture_frame_size);
3718 if (bytes_read < 0)
3719 {
3720 /* read errors are ignored */
3721 }
3722 else
3723 {
3724 /* data directly copied to image buffer */
3725 }
3726 gate_mutex_release(&impl->capture_mutex);
3727 }
3728 break;
3729 }
3730 }
3731
3732 } /* while(running) */
3733
3734 /* shutdown stage */
3735 gate_video_vfl_shutdown_session(impl);
3736
3737 if (impl->converter && (v4l_convert.destroy != NULL))
3738 {
3739 v4l_convert.destroy(impl->converter);
3740 impl->converter = NULL;
3741 }
3742
3743 } while (0);
3744
3745 res = gate_mutex_acquire(&impl->capture_mutex);
3746 gate_video_vfl_buffer_destroy(&impl->capture_current_image);
3747 if (GATE_SUCCEEDED(res))
3748 {
3749 gate_mutex_release(&impl->capture_mutex);
3750 }
3751
3752 return ret;
3753 }
3754
3755 static gate_result_t gate_video_vfl_open(void* self, gate_video_format_t const* format)
3756 {
3757 gate_result_t ret = GATE_RESULT_FAILED;
3758 gate_video_vfl_impl_t* impl = (gate_video_vfl_impl_t*)self;
3759 int fd = -1;
3760 struct v4l2_capability cap;
3761 struct v4l2_requestbuffers req;
3762 struct v4l2_format fmt;
3763 struct v4l2_buffer buf;
3764 gate_uint32_t index;
3765 enum v4l2_buf_type buf_type;
3766
3767 do
3768 {
3769 if (GATE_VIDEO_VFL_STATE_OFFLINE != gate_atomic_int_xchg_if(&impl->capture_state, GATE_VIDEO_VFL_STATE_OFFLINE, GATE_VIDEO_VFL_STATE_STARTING))
3770 {
3771 return GATE_RESULT_INVALIDSTATE;
3772 }
3773
3774
3775 fd = gate_posix_open(impl->id, O_RDWR | O_NONBLOCK, 0);
3776 if (fd == -1)
3777 {
3778 ret = GATE_RESULT_NOTAVAILABLE;
3779 break;
3780 }
3781
3782 if (-1 == gate_posix_ioctl(fd, VIDIOC_QUERYCAP, &cap))
3783 {
3784 ret = GATE_RESULT_NOTREADY;
3785 break;
3786 }
3787
3788 if ((cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) == 0)
3789 {
3790 ret = GATE_RESULT_NOTSUPPORTED;
3791 break;
3792 }
3793
3794 gate_mem_clear(&fmt, sizeof(fmt));
3795 fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
3796 if (-1 == gate_posix_ioctl(fd, VIDIOC_G_FMT, &fmt))
3797 {
3798 ret = GATE_RESULT_INVALIDHEADER;
3799 break;
3800 }
3801
3802 ret = gate_vfl_convert_init();
3803 if (GATE_SUCCEEDED(ret))
3804 {
3805 do
3806 {
3807 impl->converter = v4l_convert.create(fd);
3808 if (impl->converter == NULL)
3809 {
3810 break;
3811 }
3812 gate_mem_copy(&impl->target_format, &fmt, sizeof(fmt));
3813 impl->target_format.fmt.pix.pixelformat = V4L2_PIX_FMT_RGB24;
3814
3815 if (v4l_convert.try_format != NULL)
3816 {
3817 v4l_convert.try_format(impl->converter, &impl->target_format, &fmt);
3818 }
3819
3820 } while (0);
3821 }
3822
3823 gate_mem_copy(&impl->source_format, &fmt, sizeof(fmt));
3824 if (-1 == gate_posix_ioctl(fd, VIDIOC_G_FMT, &impl->source_format))
3825 {
3826 ret = GATE_RESULT_FAILED;
3827 break;
3828 }
3829
3830 impl->capture_frame_size = impl->source_format.fmt.pix.sizeimage;
3831
3832 /* find best access method */
3833 do
3834 {
3835 impl->capture_type = GATE_VIDEO_VFL_TYPE_NONE;
3836
3837 if ((cap.capabilities & V4L2_CAP_STREAMING) == V4L2_CAP_STREAMING)
3838 {
3839 /* video source supports streaming into memory */
3840 gate_mem_clear(&req, sizeof(req));
3841 req.count = sizeof(impl->capture_buffers) / sizeof(impl->capture_buffers[0]);
3842 req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
3843 req.memory = V4L2_MEMORY_MMAP;
3844
3845 if (0 == gate_posix_ioctl(fd, VIDIOC_REQBUFS, &req))
3846 {
3847 /* the driver supports streaming to mmap-blocks */
3848 if (req.count >= 2)
3849 {
3850 for (index = 0; index < req.count; ++index)
3851 {
3852 gate_mem_clear(&buf, sizeof(buf));
3853 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
3854 buf.memory = V4L2_MEMORY_MMAP;
3855 buf.index = index;
3856 if (-1 == gate_posix_ioctl(fd, VIDIOC_QUERYBUF, &buf))
3857 {
3858 break;
3859 }
3860
3861 impl->capture_buffers[index].length = buf.length;
3862 impl->capture_buffers[index].data = mmap(NULL, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, buf.m.offset);
3863
3864 if (MAP_FAILED == impl->capture_buffers[index].data)
3865 {
3866 break;
3867 }
3868
3869 if (-1 == gate_posix_ioctl(fd, VIDIOC_QBUF, &buf))
3870 {
3871 break;
3872 }
3873 }
3874
3875 if (index < req.count)
3876 {
3877 /* error */
3878 if (index > 0)
3879 {
3880 gate_video_vfl_unmap_buffers(impl->capture_buffers, index - 1);
3881 }
3882 break;
3883 }
3884 impl->capture_buffers_used = req.count;
3885
3886 buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
3887 if (-1 == gate_posix_ioctl(fd, VIDIOC_STREAMON, &buf_type))
3888 {
3889 gate_video_vfl_unmap_buffers(impl->capture_buffers, index - 1);
3890 break;
3891 }
3892 }
3893
3894 impl->capture_type = GATE_VIDEO_VFL_TYPE_MMAP;
3895 break; /* type detected */
3896 }
3897
3898 /* try userptr: */
3899 do
3900 {
3901 gate_mem_clear(impl->capture_buffers, sizeof(impl->capture_buffers));
3902 gate_mem_clear(&req, sizeof(req));
3903 req.count = 4;
3904 req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
3905 req.memory = V4L2_MEMORY_USERPTR;
3906
3907 if (0 != gate_posix_ioctl(fd, VIDIOC_REQBUFS, &req))
3908 {
3909 /* the driver does NOT supports streaming to process-allocated memory */
3910 break;
3911 }
3912
3913 for (index = 0; index < req.count; ++index)
3914 {
3915 impl->capture_buffers[index].data = gate_mem_alloc(impl->capture_frame_size);
3916 if (impl->capture_buffers[index].data == NULL)
3917 {
3918 break;
3919 }
3920 impl->capture_buffers[index].length = impl->capture_frame_size;
3921
3922 gate_mem_clear(&buf, sizeof(buf));
3923 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
3924 buf.memory = V4L2_MEMORY_USERPTR;
3925 buf.index = index;
3926 buf.m.userptr = (unsigned long)(gate_uintptr_t)impl->capture_buffers[index].data;
3927 buf.length = impl->capture_frame_size;
3928
3929 if (-1 == gate_posix_ioctl(fd, VIDIOC_QBUF, &buf))
3930 {
3931 break;
3932 }
3933 }
3934
3935 if (index < req.count)
3936 {
3937 /* error detected */
3938 for (index = 0; index < req.count; ++index)
3939 {
3940 if (impl->capture_buffers[index].data != NULL)
3941 {
3942 gate_mem_dealloc(impl->capture_buffers[index].data);
3943 }
3944 }
3945 break;
3946 }
3947 impl->capture_buffers_used = req.count;
3948 buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
3949 if (-1 == gate_posix_ioctl(fd, VIDIOC_STREAMON, &buf_type))
3950 {
3951 break;
3952 }
3953
3954 impl->capture_type = GATE_VIDEO_VFL_TYPE_USERPTR;
3955 } while (0);
3956
3957 if (impl->capture_type == GATE_VIDEO_VFL_TYPE_USERPTR)
3958 {
3959 break;
3960 }
3961 }
3962
3963 if ((cap.capabilities & V4L2_CAP_READWRITE) == V4L2_CAP_READWRITE)
3964 {
3965 // the capture device does support read() access
3966 impl->capture_type = GATE_VIDEO_VFL_TYPE_READ;
3967 break;
3968 }
3969
3970 /* no access type detected */
3971
3972 } while (0);
3973
3974 if (impl->capture_type == GATE_VIDEO_VFL_TYPE_NONE)
3975 {
3976 ret = GATE_RESULT_NOTSUPPORTED;
3977 break;
3978 }
3979
3980 impl->handle = fd;
3981 fd = -1;
3982 ret = gate_thread_start_code(&gate_video_vfl_thread, impl, &impl->capture_thread, &impl->capture_thread_id);
3983 if (GATE_FAILED(ret))
3984 {
3985 gate_video_vfl_shutdown_session(impl);
3986 }
3987 else
3988 {
3989 /* wait until thread is removed starting state */
3990 unsigned counter = 0;
3991 static unsigned const max_counter = 1000;
3992 for (counter = 0; (gate_atomic_int_get(&impl->capture_state) == GATE_VIDEO_VFL_STATE_STARTING) && (counter < max_counter); ++counter)
3993 {
3994 gate_thread_sleep(1);
3995 }
3996 }
3997
3998
3999 } while (0);
4000
4001 if (fd != -1)
4002 {
4003 gate_posix_close(fd);
4004 }
4005
4006 if (GATE_FAILED(ret))
4007 {
4008 gate_atomic_int_set(&impl->capture_state, GATE_VIDEO_VFL_STATE_OFFLINE);
4009 }
4010
4011 return ret;
4012 }
4013 static gate_result_t gate_video_vfl_close(void* self)
4014 {
4015 gate_result_t ret = GATE_RESULT_FAILED;
4016 gate_video_vfl_impl_t* impl = (gate_video_vfl_impl_t*)self;
4017 gate_result_t thread_result;
4018
4019 if (GATE_VIDEO_VFL_STATE_RUNNING == gate_atomic_int_xchg_if(&impl->capture_state,
4020 GATE_VIDEO_VFL_STATE_RUNNING, GATE_VIDEO_VFL_STATE_STOPPING))
4021 {
4022 ret = gate_thread_join(&impl->capture_thread, &thread_result);
4023 }
4024 else
4025 {
4026 ret = GATE_RESULT_INVALIDSTATE;
4027 }
4028
4029 return ret;
4030 }
4031
4032 static gate_result_t gate_video_vfl_read(void* self, gate_video_frame_t* frame)
4033 {
4034 gate_result_t ret = GATE_RESULT_NOTIMPLEMENTED;
4035 void* frame_data = NULL;
4036 struct v4l2_format fmt;
4037 gate_video_vfl_impl_t* impl = (gate_video_vfl_impl_t*)self;
4038 gate_result_t res = gate_mutex_acquire(&impl->capture_mutex);
4039
4040 if (GATE_SUCCEEDED(res))
4041 {
4042 do
4043 {
4044 gate_size_t frame_data_size;
4045
4046 if (frame == NULL)
4047 {
4048 ret = GATE_RESULT_NULLPOINTER;
4049 break;
4050 }
4051 frame_data_size = impl->target_format.fmt.pix.sizeimage;
4052 if (frame_data_size < impl->source_format.fmt.pix.sizeimage)
4053 {
4054 frame_data_size = impl->source_format.fmt.pix.sizeimage;
4055 }
4056
4057 frame_data = gate_mem_alloc(frame_data_size);
4058 if (frame_data == NULL)
4059 {
4060 ret = GATE_RESULT_OUTOFMEMORY;
4061 break;
4062 }
4063
4064 if (impl->converter && (v4l_convert.convert != NULL) && (impl->capture_current_image.length > 0))
4065 {
4066 int result = v4l_convert.convert(impl->converter, &impl->source_format, &impl->target_format,
4067 impl->capture_current_image.data, impl->capture_current_image.length,
4068 frame_data, impl->target_format.fmt.pix.sizeimage);
4069 if (result >= 0)
4070 {
4071 gate_mem_copy(&fmt, &impl->target_format, sizeof(fmt));
4072 break;
4073 }
4074 }
4075 gate_mem_copy(&fmt, &impl->source_format, sizeof(fmt));
4076 gate_mem_copy(frame_data, impl->capture_current_image.data, impl->capture_current_image.length);
4077 } while (0);
4078 gate_mutex_release(&impl->capture_mutex);
4079 }
4080
4081 if (frame_data != NULL)
4082 {
4083 do
4084 {
4085 gate_color_t* ptr_frame_pixels;
4086 frame->format.width = fmt.fmt.pix.width;
4087 frame->format.height = fmt.fmt.pix.height;
4088 frame->format.bits_per_pixel = fmt.fmt.pix.bytesperline * 8 / fmt.fmt.pix.width;
4089 frame->format.compression = 0;
4090 frame->format.encoding_id = 0;
4091 frame->format.frames_per_second = 0;
4092
4093 if (NULL == gate_rasterimage_create(&frame->image, GATE_IMAGE_PIXELFORMAT_RGBA,
4094 frame->format.width, frame->format.height, NULL))
4095 {
4096 ret = GATE_RESULT_OUTOFMEMORY;
4097 break;
4098 }
4099
4100 ptr_frame_pixels = (gate_color_t*)gate_rasterimage_get_line_ptr(&frame->image, 0);
4101
4102 switch (fmt.fmt.pix.pixelformat)
4103 {
4104 case V4L2_PIX_FMT_RGB24:
4105 {
4106 gate_color_load_rgb_24(ptr_frame_pixels, frame->format.width * frame->format.height, frame_data);
4107 ret = GATE_RESULT_OK;
4108 break;
4109 }
4110 case V4L2_PIX_FMT_RGB32:
4111 {
4112 gate_color_load_rgb_32(ptr_frame_pixels, frame->format.width * frame->format.height, frame_data);
4113 ret = GATE_RESULT_OK;
4114 break;
4115 }
4116 case V4L2_PIX_FMT_SPCA501:
4117 {
4118 gate_color_load_yuv2_16(ptr_frame_pixels, frame->format.width * frame->format.height, frame_data);
4119 ret = GATE_RESULT_OK;
4120 break;
4121 }
4122 default:
4123 {
4124 ret = GATE_RESULT_NOTSUPPORTED;
4125 break;
4126 }
4127 }
4128
4129 } while (0);
4130 }
4131
4132 if (frame_data != NULL)
4133 {
4134 gate_mem_dealloc(frame_data);
4135 }
4136
4137 return ret;
4138 }
4139
4140
4141 struct gate_v4l_enum_param
4142 {
4143 gate_delegate_t const* callback;
4144 void* param;
4145 };
4146
4147 static gate_video_vfl_impl_t* gate_v4l_create_device(char const* path)
4148 {
4149 gate_video_vfl_impl_t* impl = NULL;
4150 int fd;
4151
4152 do
4153 {
4154 struct v4l2_capability cap;
4155 int result;
4156 gate_result_t res;
4157
4158 fd = gate_posix_open(path, O_RDONLY /*O_RDWR*/, 0);
4159 if (fd == -1)
4160 {
4161 break;
4162 }
4163
4164 gate_mem_clear(&cap, sizeof(cap));
4165
4166 result = gate_posix_ioctl(fd, VIDIOC_QUERYCAP, &cap);
4167 if (result != 0)
4168 {
4169 break;
4170 }
4171
4172 impl = (gate_video_vfl_impl_t*)gate_mem_alloc(sizeof(gate_video_vfl_impl_t));
4173 if (impl == NULL)
4174 {
4175 break;
4176 }
4177
4178 gate_mem_clear(impl, sizeof(gate_video_vfl_impl_t));
4179 gate_init_video_vfl_vtbl_impl();
4180 impl->vtbl = &gate_video_vfl_vtbl_impl;
4181 gate_atomic_int_init(&impl->ref_counter, 1);
4182 gate_str_print_text(impl->id, sizeof(impl->id), path, gate_str_length(path));
4183 gate_str_print_text(impl->name, sizeof(impl->name),
4184 (char const*)&cap.card[0],
4185 gate_str_length_max((char const*)&cap.card[0], sizeof(cap.card)));
4186 impl->handle = -1;
4187
4188 res = gate_mutex_create(&impl->capture_mutex);
4189 if (GATE_FAILED(res))
4190 {
4191 gate_mem_dealloc(impl);
4192 impl = NULL;
4193 }
4194 } while (0);
4195
4196 if (fd != -1)
4197 {
4198 gate_posix_close(fd);
4199 }
4200
4201 return impl;
4202 }
4203
4204 static gate_bool_t gate_v4l_enum_callback(gate_file_entry_t const* entry, void* userparam)
4205 {
4206 struct gate_v4l_enum_param* param = (struct gate_v4l_enum_param*)userparam;
4207
4208 do
4209 {
4210 gate_video_vfl_impl_t* impl;
4211 if (!gate_str_starts_with(entry->name, gate_str_length(entry->name), "video", 5))
4212 {
4213 break;
4214 }
4215
4216 impl = gate_v4l_create_device(entry->path);
4217 if (impl != NULL)
4218 {
4219 gate_delegate_invoke(param->callback, impl, param->param);
4220 gate_object_release(impl);
4221 }
4222 } while (0);
4223
4224 return true;
4225 }
4226
4227 gate_result_t gate_video_sources_enum(gate_delegate_t const* callback, void* param)
4228 {
4229 static gate_string_t dev_directory = { "/dev/", 5, NULL };
4230 gate_result_t result;
4231 struct gate_v4l_enum_param cb_param = { callback, param };
4232 result = gate_file_list(&dev_directory, &gate_v4l_enum_callback, &cb_param);
4233 return result;
4234 }
4235
4236 gate_result_t gate_video_source_by_id(char const* id, gate_video_source_t** ptr_source)
4237 {
4238 gate_result_t result = GATE_RESULT_NOTIMPLEMENTED;
4239
4240 gate_video_vfl_impl_t* impl = gate_v4l_create_device(id);
4241 if (impl == NULL)
4242 {
4243 result = GATE_RESULT_NOTAVAILABLE;
4244 }
4245 else
4246 {
4247 if (ptr_source != NULL)
4248 {
4249 *ptr_source = (gate_video_source_t*)impl;
4250 }
4251 else
4252 {
4253 gate_object_release(impl);
4254 }
4255 result = GATE_RESULT_OK;
4256 }
4257 return result;
4258 }
4259
4260
4261 #endif /* GATE_IO_VIDEO_V4L */
4262
4263
4264
4265 #if defined(GATE_IO_VIDEO_ANDROID)
4266
4267 /*
4268 #include <android/storage_manager.h>
4269 #include <jni.h>
4270 #include <android/configuration.h>
4271 */
4272
4273 gate_result_t gate_video_sources_enum(gate_delegate_t const* callback, void* param)
4274 {
4275 (void)callback;
4276 (void)param;
4277 return GATE_RESULT_NOTIMPLEMENTED;
4278 }
4279
4280 gate_result_t gate_video_source_by_id(char const* id, gate_video_source_t** ptr_source)
4281 {
4282 (void)id;
4283 (void)ptr_source;
4284 return GATE_RESULT_NOTIMPLEMENTED;
4285 }
4286
4287 #endif /* GATE_IO_VIDEO_ANDROID */
4288
4289
4290
4291 #if defined(GATE_IO_VIDEO_NO_IMPL)
4292
4293 gate_result_t gate_video_sources_enum(gate_delegate_t const* callback, void* param)
4294 {
4295 (void)callback;
4296 (void)param;
4297 return GATE_RESULT_NOTIMPLEMENTED;
4298 }
4299
4300 gate_result_t gate_video_source_by_id(char const* id, gate_video_source_t** ptr_source)
4301 {
4302 (void)id;
4303 (void)ptr_source;
4304 return GATE_RESULT_NOTIMPLEMENTED;
4305 }
4306
4307 #endif /* GATE_IO_VIDEO_NO_IMPL */
4308
4309