GCC Code Coverage Report


Directory: src/gate/
File: src/gate/io/videosources.c
Date: 2025-09-22 22:57:09
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-2025, 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 result = GATE_RESULT_UNKNOWNEXCEPTION;
976 }
977 }
978 break;
979 }
980 case GATE_VFW_QUEUE_STATE_ONLINE:
981 {
982 result = GATE_RESULT_OK;
983 break;
984 }
985 default:
986 {
987 result = GATE_RESULT_INVALIDSTATE;
988 break;
989 }
990 }
991 return result;
992 }
993
994 static gate_size_t gate_video_vfw_get_supported_formats(void* self, gate_video_format_t* format_buffer, gate_size_t format_buffer_count)
995 {
996 gate_video_vfw_impl_t* impl = (gate_video_vfw_impl_t*)self;
997 gate_size_t ret = 0;
998 gate_result_t result = gate_vfw_start_queue(impl);
999
1000 do
1001 {
1002 GATE_BREAK_IF_FAILED_TRACE(result, "Failed to start VFW queue");
1003
1004 if (format_buffer_count > 0)
1005 {
1006 *format_buffer = impl->default_format;
1007 ret = 1;
1008 }
1009 } while (0);
1010
1011 return ret;
1012 }
1013
1014 static gate_result_t gate_video_vfw_open(void* self, gate_video_format_t const* format)
1015 {
1016 gate_video_vfw_impl_t* impl = (gate_video_vfw_impl_t*)self;
1017 gate_win32_userapi_t const* const userapi = gate_win32_userapi();
1018 gate_result_t result = gate_vfw_start_queue(impl);
1019 do
1020 {
1021 GATE_BREAK_IF_FAILED_TRACE(result, "Failed to start VFW queue");
1022 impl->format = *format;
1023
1024 userapi->UserPostMessage(impl->hwnd, WM_GATE_VFW_START, 0, 0);
1025 } while (0);
1026
1027 return result;
1028 }
1029 static gate_result_t gate_video_vfw_close(void* self)
1030 {
1031 gate_video_vfw_impl_t* impl = (gate_video_vfw_impl_t*)self;
1032
1033 gate_result_t result = gate_vfw_stop_queue(impl);
1034
1035 return result;
1036 }
1037
1038 static gate_result_t gate_video_vfw_read(void* self, gate_video_frame_t* frame)
1039 {
1040 gate_video_vfw_impl_t* impl = (gate_video_vfw_impl_t*)self;
1041 gate_result_t result = GATE_RESULT_OK;
1042 int width, height, x, y;
1043 gate_color_t* dst_pixel;
1044 gate_uint8_t const* src_pixel;
1045 gate_size_t pixel_count;
1046
1047 gate_win32_section_enter(&impl->current_frame_lock);
1048 do
1049 {
1050 if (impl->current_frame_data == NULL)
1051 {
1052 /* no frame available */
1053 result = GATE_RESULT_NOTAVAILABLE;
1054 break;
1055 }
1056
1057 width = impl->draw_bitmapinfo.bmiHeader.biWidth;
1058 height = impl->draw_bitmapinfo.bmiHeader.biHeight;
1059
1060 if (!DrawDibDrawFunc(impl->draw_dib, impl->draw_dc, 0, 0, width, height,
1061 &impl->draw_bitmapinfo.bmiHeader, gate_memoryblock_get_content(impl->current_frame_data),
1062 0, 0, width, height, 0))
1063 {
1064 if (impl->draw_bitmapinfo.bmiHeader.biCompression == 0x47504a4d)
1065 {
1066 /* MJPEG */
1067 result = mjpeg_export(frame,
1068 gate_memoryblock_get_content(impl->current_frame_data),
1069 gate_memoryblock_get_size(impl->current_frame_data));
1070 if (GATE_SUCCEEDED(result))
1071 {
1072 break;
1073 }
1074 }
1075 GATE_DEBUG_TRACE("VFW DrawDibDraw failed");
1076 result = GATE_RESULT_FAILED;
1077 break;
1078 }
1079
1080 if (NULL == gate_rasterimage_create(&frame->image, GATE_IMAGE_PIXELFORMAT_RGBA, impl->format.width, impl->format.height, NULL))
1081 {
1082 GATE_DEBUG_TRACE("Failed to create VFW raster image");
1083 result = GATE_RESULT_OUTOFMEMORY;
1084 break;
1085 }
1086 frame->format = impl->format;
1087 frame->record_time = 0; /*TODO*/
1088
1089 dst_pixel = (gate_color_t*)gate_rasterimage_get_line_ptr(&frame->image, 0);
1090 if (height < 0)
1091 {
1092 src_pixel = (gate_uint8_t const*)impl->draw_buffer;
1093
1094 pixel_count = (gate_size_t)width * (gate_size_t)(-height);
1095 while (pixel_count-- != 0)
1096 {
1097 dst_pixel->b = *src_pixel++;
1098 dst_pixel->g = *src_pixel++;
1099 dst_pixel->r = *src_pixel++;
1100 dst_pixel->a = 255;
1101 ++src_pixel;
1102 ++dst_pixel;
1103 }
1104 }
1105 else
1106 {
1107 for (y = 0; y < height; ++y)
1108 {
1109 src_pixel = (gate_uint8_t const*)impl->draw_buffer;
1110 src_pixel += (gate_size_t)(height - y - 1) * (gate_size_t)(width * 4);
1111 for (x = 0; x < width; ++x)
1112 {
1113 dst_pixel->b = *src_pixel++;
1114 dst_pixel->g = *src_pixel++;
1115 dst_pixel->r = *src_pixel++;
1116 dst_pixel->a = 255;
1117 ++src_pixel;
1118 ++dst_pixel;
1119 }
1120 }
1121 }
1122 } while (0);
1123 gate_win32_section_leave(&impl->current_frame_lock);
1124 return result;
1125 }
1126
1127
1128 static gate_video_source_t* create_vfw_video_source(WORD index, TCHAR const* name, TCHAR const* version)
1129 {
1130 gate_video_vfw_impl_t* impl = (gate_video_vfw_impl_t*)gate_mem_alloc(sizeof(gate_video_vfw_impl_t));
1131 gate_result_t result = GATE_RESULT_OK;
1132 if (NULL != impl)
1133 {
1134 do
1135 {
1136 gate_mem_clear(impl, sizeof(gate_video_vfw_impl_t));
1137
1138 result = gate_syncevent_create(&impl->sync_event, false);
1139 GATE_BREAK_IF_FAILED_TRACE(result, "Failed to create VFW sync event");
1140
1141 gate_init_video_vfw_vtbl_impl();
1142 impl->vtbl = &gate_video_vfw_vtbl_impl;
1143 gate_atomic_int_init(&impl->ref_counter, 1);
1144
1145 impl->id[0] = 'v';
1146 impl->id[1] = 'f';
1147 impl->id[2] = 'w';
1148 gate_str_print_uint16(&impl->id[3], sizeof(impl->id) - 4, (gate_uint16_t)index);
1149
1150 gate_win32_winstr_2_utf8(name, gate_win32_winstr_length(name), impl->name, sizeof(impl->name));
1151 impl->handle = index;
1152
1153 gate_atomic_int_init(&impl->queue_state, GATE_VFW_QUEUE_STATE_OFFLINE);
1154
1155 gate_win32_section_create(&impl->current_frame_lock);
1156
1157 } while (0);
1158 }
1159 return (gate_video_source_t*)impl;
1160 }
1161
1162 static gate_result_t get_vfw_source(UINT index, gate_video_source_t** ptr_source)
1163 {
1164 gate_result_t ret = GATE_RESULT_NOMATCH;
1165 TCHAR name_buffer[1024];
1166 TCHAR version_buffer[1024];
1167 gate_video_source_t* impl = NULL;
1168 if (capGetDriverDescriptionFunc(index, name_buffer, sizeof(name_buffer), version_buffer, sizeof(version_buffer)))
1169 {
1170 ret = GATE_RESULT_OK;
1171 if (ptr_source)
1172 {
1173 impl = create_vfw_video_source((WORD)index, name_buffer, version_buffer);
1174 if (impl == NULL)
1175 {
1176 ret = GATE_RESULT_FAILED;
1177 }
1178 else
1179 {
1180 *ptr_source = impl;
1181 }
1182 }
1183 }
1184 return ret;
1185 }
1186
1187 #endif /* GATE_IO_VIDEOSOURCES_USE_V4W */
1188
1189
1190
1191 #if defined(GATE_IO_VIDEOSOURCES_USE_DSHOW)
1192
1193 #include <guiddef.h>
1194
1195 #include "gate/io/platform/videosources_dshow.h"
1196
1197
1198 /*******************
1199 ** DIRECT SHOW **
1200 *******************/
1201
1202 static void dshow_free_media_type(AM_MEDIA_TYPE* mt)
1203 {
1204 if (mt->cbFormat)
1205 {
1206 gate_win32_com_dealloc((LPVOID)mt->pbFormat);
1207 mt->cbFormat = 0;
1208 mt->pbFormat = 0;
1209 }
1210 if (mt->pUnk)
1211 {
1212 mt->pUnk->lpVtbl->Release(mt->pUnk);
1213 mt->pUnk = 0;
1214 }
1215 }
1216 static void dshow_delete_media_type(AM_MEDIA_TYPE* ptr)
1217 {
1218 if (ptr)
1219 {
1220 dshow_free_media_type(ptr);
1221 gate_win32_com_dealloc(ptr);
1222 }
1223 }
1224
1225 typedef struct ISampleGrabberCBImpl_class
1226 {
1227 ISampleGrabberCBVtbl const* lpVtbl;
1228
1229 gate_atomic_int_t ref_counter;
1230 gate_mutex_t buffer_mutex;
1231 void* buffer_data;
1232 gate_size_t buffer_used;
1233 gate_size_t buffer_capacity;
1234 gate_syncevent_t buffer_event;
1235
1236 } ISampleGrabberCBImpl;
1237
1238
1239 static HRESULT STDMETHODCALLTYPE QueryInterface(ISampleGrabberCB* This, REFIID riid, void** ppvObject)
1240 {
1241 HRESULT hr;
1242 ISampleGrabberCBImpl* impl = (ISampleGrabberCBImpl*)This;
1243 if (IsEqualIID(riid, &gate_IID_IUnknown))
1244 {
1245 if (ppvObject)
1246 {
1247 *ppvObject = impl;
1248 }
1249 hr = S_OK;
1250 }
1251 else if (IsEqualIID(riid, &gate_IID_IUnknown))
1252 {
1253 if (ppvObject)
1254 {
1255 *ppvObject = impl;
1256 }
1257 hr = S_OK;
1258 }
1259 else
1260 {
1261 hr = E_NOTIMPL;
1262 }
1263 return hr;
1264 }
1265 static ULONG STDMETHODCALLTYPE AddRef(ISampleGrabberCB* This)
1266 {
1267 ISampleGrabberCBImpl* impl = (ISampleGrabberCBImpl*)This;
1268 return (ULONG)gate_atomic_int_inc(&impl->ref_counter);
1269 }
1270 static ULONG STDMETHODCALLTYPE Release(ISampleGrabberCB* This)
1271 {
1272 ISampleGrabberCBImpl* impl = (ISampleGrabberCBImpl*)This;
1273 gate_int32_t cnt = gate_atomic_int_inc(&impl->ref_counter);
1274 return (ULONG)cnt;
1275 }
1276
1277 static HRESULT STDMETHODCALLTYPE SampleCB(ISampleGrabberCB* This, double SampleTime, IMediaSample* pSample)
1278 {
1279 ISampleGrabberCBImpl* impl = (ISampleGrabberCBImpl*)This;
1280 (void)SampleTime;
1281 (void)pSample;
1282 return S_OK;
1283 }
1284 static HRESULT STDMETHODCALLTYPE BufferCB(ISampleGrabberCB* This, double SampleTime, BYTE* pBuffer, LONG BufferLen)
1285 {
1286 HRESULT hr = S_OK;
1287 gate_result_t result;
1288 ISampleGrabberCBImpl* impl = (ISampleGrabberCBImpl*)This;
1289 void* new_buffer;
1290
1291 do
1292 {
1293 if (!impl || !pBuffer || (BufferLen <= 0))
1294 {
1295 break;
1296 }
1297
1298 result = gate_mutex_acquire(&impl->buffer_mutex);
1299 GATE_BREAK_IF_FAILED(result);
1300
1301 do
1302 {
1303 if ((gate_size_t)BufferLen > impl->buffer_capacity)
1304 {
1305 new_buffer = gate_mem_alloc((gate_size_t)BufferLen);
1306 if (new_buffer == NULL)
1307 {
1308 break;
1309 }
1310 if (impl->buffer_data != NULL)
1311 {
1312 gate_mem_dealloc(impl->buffer_data);
1313 }
1314 impl->buffer_data = new_buffer;
1315 impl->buffer_capacity = (gate_size_t)BufferLen;
1316 }
1317 impl->buffer_used = (gate_size_t)BufferLen;
1318 gate_mem_copy(impl->buffer_data, pBuffer, impl->buffer_used);
1319 } while (0);
1320
1321 gate_mutex_release(&impl->buffer_mutex);
1322
1323 } while (0);
1324 return hr;
1325 }
1326
1327 static gate_result_t sample_grabber_export_rgb32(gate_video_frame_t* target_frame, void const* data)
1328 {
1329 gate_result_t ret;
1330 gate_size_t line_len = (gate_size_t)target_frame->format.width * 3;
1331 char const* buffer;
1332 gate_uint32_t x, y;
1333 gate_color_t* ptr_pixel;
1334
1335 if (NULL == gate_rasterimage_create(&target_frame->image, GATE_IMAGE_PIXELFORMAT_RGBA, target_frame->format.width, target_frame->format.height, NULL))
1336 {
1337 ret = GATE_RESULT_OUTOFMEMORY;
1338 }
1339 else
1340 {
1341 buffer = (char const*)data;
1342 for (y = 0; y != target_frame->format.height; ++y)
1343 {
1344 ptr_pixel = (gate_color_t*)gate_rasterimage_get_line_ptr(&target_frame->image, (target_frame->format.height - y - 1));
1345 for (x = 0; x != target_frame->format.width; ++x)
1346 {
1347 ptr_pixel->b = *buffer;
1348 ++buffer;
1349 ptr_pixel->g = *buffer;
1350 ++buffer;
1351 ptr_pixel->r = *buffer;
1352 ++buffer;
1353 ptr_pixel->a = (255 - *buffer);
1354 ++buffer;
1355
1356 ++ptr_pixel;
1357 }
1358 }
1359 ret = GATE_RESULT_OK;
1360 }
1361 return ret;
1362
1363 }
1364 static gate_result_t sample_grabber_export_rgb24(gate_video_frame_t* target_frame, void const* data)
1365 {
1366 gate_result_t ret;
1367 gate_size_t line_len = (gate_size_t)target_frame->format.width * 3;
1368 gate_size_t align_diff = line_len % 4;
1369 char const* buffer;
1370 gate_uint32_t x, y;
1371 gate_color_t* ptr_pixel;
1372
1373 if (NULL == gate_rasterimage_create(&target_frame->image, GATE_IMAGE_PIXELFORMAT_RGBA, target_frame->format.width, target_frame->format.height, NULL))
1374 {
1375 ret = GATE_RESULT_OUTOFMEMORY;
1376 }
1377 else
1378 {
1379 if (align_diff != 0)
1380 {
1381 align_diff = 4 - align_diff;
1382 }
1383 buffer = (char const*)data;
1384 for (y = 0; y != target_frame->format.height; ++y)
1385 {
1386 ptr_pixel = (gate_color_t*)gate_rasterimage_get_line_ptr(&target_frame->image, (target_frame->format.height - y - 1));
1387 for (x = 0; x != target_frame->format.width; ++x)
1388 {
1389 ptr_pixel->b = *buffer;
1390 ++buffer;
1391 ptr_pixel->g = *buffer;
1392 ++buffer;
1393 ptr_pixel->r = *buffer;
1394 ++buffer;
1395 ptr_pixel->a = 255;
1396
1397 ++ptr_pixel;
1398 }
1399 buffer += align_diff;
1400 }
1401 ret = GATE_RESULT_OK;
1402 }
1403 return ret;
1404 }
1405
1406 static gate_uint8_t convert_float(gate_real32_t f)
1407 {
1408 int num = (int)(f * 128.0f + 127.0f);
1409 if (num <= 0) return 0;
1410 if (num >= 255) return 255;
1411 return (gate_uint8_t)num;
1412 }
1413 static gate_real32_t convert_byte(gate_uint8_t b)
1414 {
1415 return (((gate_real32_t)b) - 127.0f) / 128.0f;
1416 }
1417
1418
1419 static gate_result_t sample_grabber_export_yuv2_16(gate_video_frame_t* target_frame, void const* data, gate_size_t data_length)
1420 {
1421 const gate_size_t line_len = (gate_size_t)target_frame->format.width * 2;
1422 const gate_size_t align_diff = (line_len % 4 == 0) ? 0 : (4 - (line_len % 4));
1423 const gate_uint32_t aligned_width = target_frame->format.width + (gate_uint32_t)align_diff;
1424 const gate_uint32_t expected_len = aligned_width * target_frame->format.height;
1425 gate_result_t result;
1426 char const* buffer;
1427 gate_intptr_t buffer_length;
1428 gate_uint32_t x, y;
1429 gate_color_t* ptr_pixel;
1430 gate_real32_t y1, u, y2, v;
1431 gate_real32_t fb, fr, fg;
1432
1433 if (data_length < expected_len)
1434 {
1435 /* some CAMs deliver MJPEG instead of specified format type */
1436 buffer = (char const*)data;
1437 if (((unsigned char)buffer[0] == 0xff) && ((unsigned char)buffer[1] == 0xd8))
1438 {
1439 /* content starts with jpeg signature -> try decoding */
1440 result = mjpeg_export(target_frame, data, data_length);
1441 if (GATE_SUCCEEDED(result))
1442 {
1443 return result;
1444 }
1445 }
1446 }
1447
1448 if (NULL == gate_rasterimage_create(&target_frame->image, GATE_IMAGE_PIXELFORMAT_RGBA, target_frame->format.width, target_frame->format.height, NULL))
1449 {
1450 return GATE_RESULT_OUTOFMEMORY;
1451 }
1452 buffer = (char const*)data;
1453 buffer_length = (gate_intptr_t)data_length;
1454 buffer_length -= (buffer_length % 4);
1455 for (y = 0; (y != target_frame->format.height) && (buffer_length != 0); ++y)
1456 {
1457 ptr_pixel = (gate_color_t*)gate_rasterimage_get_line_ptr(&target_frame->image, (target_frame->format.height - y - 1));
1458 for (x = 0; (x < target_frame->format.width) && (buffer_length != 0); x += 2)
1459 {
1460 y1 = convert_byte(buffer[0]);
1461 u = convert_byte(buffer[1]);
1462 y2 = convert_byte(buffer[2]);
1463 v = convert_byte(buffer[3]);
1464 buffer += 4;
1465 buffer_length -= 4;
1466
1467 fb = y1 + u / 0.493f;
1468 fr = y1 + v / 0.877f;
1469 fg = y1 - 0.39393f * u - 0.58081f * v;
1470 ptr_pixel->r = convert_float(fr);
1471 ptr_pixel->g = convert_float(fg);
1472 ptr_pixel->b = convert_float(fb);
1473 ptr_pixel->a = 255;
1474 ++ptr_pixel;
1475
1476 fb = y2 + u / 0.493f;
1477 fr = y2 + v / 0.877f;
1478 fg = y2 - 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 buffer += align_diff;
1486 buffer_length -= (gate_intptr_t)align_diff;
1487 if (buffer_length <= 0)
1488 {
1489 break;
1490 }
1491 }
1492 return GATE_RESULT_OK;
1493 }
1494
1495
1496 static gate_result_t sample_grabber_export_image(ISampleGrabberCBImpl* this_ptr, gate_video_frame_t* frame)
1497 {
1498 gate_result_t ret = GATE_RESULT_OK;
1499
1500 do
1501 {
1502 switch (frame->format.compression)
1503 {
1504 case BI_RGB:
1505 {
1506 switch (frame->format.bits_per_pixel)
1507 {
1508 case 32:
1509 {
1510 ret = gate_mutex_acquire(&this_ptr->buffer_mutex);
1511 if (GATE_SUCCEEDED(ret))
1512 {
1513 if (this_ptr->buffer_data)
1514 {
1515 ret = sample_grabber_export_rgb32(frame, this_ptr->buffer_data);
1516 }
1517 gate_mutex_release(&this_ptr->buffer_mutex);
1518 }
1519 break;
1520 }
1521 case 24:
1522 {
1523 ret = gate_mutex_acquire(&this_ptr->buffer_mutex);
1524 if (GATE_SUCCEEDED(ret))
1525 {
1526 if (this_ptr->buffer_data)
1527 {
1528 ret = sample_grabber_export_rgb24(frame, this_ptr->buffer_data);
1529 }
1530 gate_mutex_release(&this_ptr->buffer_mutex);
1531 }
1532 break;
1533 }
1534 default:
1535 {
1536 ret = GATE_RESULT_NOTSUPPORTED;
1537 break;
1538 }
1539 }
1540 break;
1541 }
1542 case 0x32595559: // YUY2
1543 {
1544 switch (frame->format.bits_per_pixel)
1545 {
1546 case 16:
1547 {
1548 ret = gate_mutex_acquire(&this_ptr->buffer_mutex);
1549 if (GATE_SUCCEEDED(ret))
1550 {
1551 if (this_ptr->buffer_data && this_ptr->buffer_used)
1552 {
1553 ret = sample_grabber_export_yuv2_16(frame, this_ptr->buffer_data, this_ptr->buffer_used);
1554 }
1555 gate_mutex_release(&this_ptr->buffer_mutex);
1556 }
1557 break;
1558 }
1559 default:
1560 {
1561 ret = GATE_RESULT_NOTSUPPORTED;
1562 break;
1563 }
1564 }
1565 break;
1566 }
1567 case 0x47504a4d:
1568 {
1569 ret = gate_mutex_acquire(&this_ptr->buffer_mutex);
1570 if (GATE_SUCCEEDED(ret))
1571 {
1572 ret = mjpeg_export(frame, this_ptr->buffer_data, this_ptr->buffer_used);
1573 gate_mutex_release(&this_ptr->buffer_mutex);
1574 }
1575 break;
1576 }
1577 default:
1578 {
1579 ret = GATE_RESULT_NOTSUPPORTED;
1580 break;
1581 }
1582 }
1583
1584 } while (0);
1585
1586 return ret;
1587 /*
1588
1589
1590 virtual HRESULT STDMETHODCALLTYPE BufferCB(double SampleTime, BYTE* pBuffer, LONG BufferLen)
1591 {
1592 if(this->m_sink)
1593 {
1594 try
1595 {
1596 this->m_buffer.assign((char const*)pBuffer, (size_t)BufferLen);
1597 VideoFrame frame;
1598 frame.Format = this->m_format;
1599 frame.Format.Compression = 0;
1600 frame.Format.Depth = 32;
1601 frame.Format.EncodingId = 0;
1602 frame.Format.EncodingType = 0;
1603 frame.ClipX = 0;
1604 frame.ClipY = 0;
1605 frame.ClipWidth = this->m_format.Width;
1606 frame.ClipHeight = this->m_format.Height;
1607 frame.CaptureTime = int64_t(SampleTime);
1608 frame.Data.swap(this->m_buffer);
1609 bool nativeFrameUsed = this->m_sink->onNativeFrame(frame);
1610 if(!frame.Data.empty())
1611 {
1612 this->m_buffer.swap(frame.Data);
1613 }
1614 if(!nativeFrameUsed)
1615 {
1616 size_t len = this->m_format.Width * this->m_format.Height * 4;
1617 if((LONG)len <= BufferLen)
1618 {
1619 IRasterImageRgba img(this->m_format.Width, this->m_format.Height);
1620 uint8_t const* src = (uint8_t const*)pBuffer;
1621 for(uint32_t y = 0; y < frame.Format.Height; ++y)
1622 {
1623 ColorRgba* dst = &img.Image()(0, frame.Format.Height - y - 1);
1624 for(uint32_t x = 0; x < frame.Format.Width; ++x)
1625 {
1626 dst->B = *src++;
1627 dst->G = *src++;
1628 dst->R = *src++;
1629 dst->A = 255;
1630 ++src;
1631 ++dst;
1632 }
1633 }
1634 this->m_sink->onImage(img);
1635 }
1636 }
1637 }
1638 catch(...)
1639 {
1640 }
1641 }
1642 return S_OK;
1643 }
1644 };
1645
1646 */
1647
1648 }
1649
1650 static ISampleGrabberCBVtbl const SampleGrabberCBImplVtbl =
1651 {
1652 &QueryInterface,
1653 &AddRef,
1654 &Release,
1655
1656 &SampleCB,
1657 &BufferCB
1658 };
1659
1660 static gate_result_t sample_grabber_cb_impl_init(ISampleGrabberCBImpl* impl)
1661 {
1662 gate_result_t ret;
1663 do
1664 {
1665 gate_mem_clear(impl, sizeof(ISampleGrabberCBImpl));
1666 impl->lpVtbl = &SampleGrabberCBImplVtbl;
1667 gate_atomic_int_init(&impl->ref_counter, 1);
1668
1669 ret = gate_mutex_create(&impl->buffer_mutex);
1670 GATE_BREAK_IF_FAILED(ret);
1671
1672 ret = gate_syncevent_create(&impl->buffer_event, true);
1673 if (GATE_FAILED(ret))
1674 {
1675 gate_mutex_destroy(&impl->buffer_mutex);
1676 break;
1677 }
1678
1679 impl->buffer_data = NULL;
1680 impl->buffer_used = 0;
1681 impl->buffer_capacity = 0;
1682 } while (0);
1683 return ret;
1684 }
1685
1686 static gate_result_t sample_grabber_cb_impl_destroy(ISampleGrabberCBImpl* impl)
1687 {
1688 gate_result_t ret = GATE_RESULT_OK;
1689 gate_syncevent_destroy(&impl->buffer_event);
1690 gate_mutex_destroy(&impl->buffer_mutex);
1691
1692 if (impl->buffer_data)
1693 {
1694 gate_mem_dealloc(impl->buffer_data);
1695 }
1696 return ret;
1697 }
1698
1699
1700 typedef struct dshow_video_device_info
1701 {
1702 gate_uintptr_t handle;
1703 char name[256];
1704 char uid[256];
1705 } dshow_video_device_info_t;
1706
1707 typedef gate_bool_t(*dshow_enum_video_device_callback)(char const* id, char const* name, IMoniker* moniker, void* param);
1708
1709 static gate_result_t dshow_enum_video_devices(dshow_enum_video_device_callback callback, void* param)
1710 {
1711 gate_result_t ret = GATE_RESULT_OK;
1712 ICreateDevEnum* system_dev_enum = NULL;
1713 IEnumMoniker* enum_moniker = NULL;
1714 IMoniker* moniker = NULL;
1715 IPropertyBag* propbag = NULL;
1716 HRESULT hr;
1717 ULONG dwFetched;
1718 VARIANT var_path = GATE_INIT_EMPTY;
1719 VARIANT var_friendly = GATE_INIT_EMPTY;
1720 char str_path[4096];
1721 char str_name[4096];
1722 gate_bool_t com_initialized = false;
1723 gate_bool_t continue_enum = true;
1724
1725 do
1726 {
1727 ret = gate_win32_com_init();
1728 GATE_BREAK_IF_FAILED(ret);
1729 com_initialized = true;
1730
1731 hr = gate_win32_com_create_instance(&gate_CLSID_SystemDeviceEnum, &gate_IID_ICreateDevEnum,
1732 (LPVOID*)&system_dev_enum);
1733 if (FAILED(hr))
1734 {
1735 ret = GATE_RESULT_FAILED;
1736 break;
1737 }
1738
1739 hr = system_dev_enum->lpVtbl->CreateClassEnumerator(system_dev_enum, &gate_CLSID_VideoInputDeviceCategory, &enum_moniker, 0);
1740 if (FAILED(hr))
1741 {
1742 ret = GATE_RESULT_FAILED;
1743 break;
1744 }
1745 if (hr == S_FALSE)
1746 {
1747 ret = GATE_RESULT_NOTAVAILABLE;
1748 break;
1749 }
1750
1751 hr = enum_moniker->lpVtbl->Reset(enum_moniker);
1752 if (FAILED(hr))
1753 {
1754 ret = GATE_RESULT_FAILED;
1755 break;
1756 }
1757
1758 while (continue_enum && (S_OK == enum_moniker->lpVtbl->Next(enum_moniker, 1, &moniker, &dwFetched)))
1759 {
1760 hr = moniker->lpVtbl->BindToStorage(moniker, NULL, NULL, &IID_IPropertyBag, (void**)&propbag);
1761 if (SUCCEEDED(hr))
1762 {
1763 gate_win32_variant_init(&var_path); /* variant MUST be empty before invocation */
1764 hr = propbag->lpVtbl->Read(propbag, L"DevicePath", &var_path, NULL);
1765 if (NOERROR == hr)
1766 {
1767 if (0 == gate_win32_variant_to_str(&var_path, str_path, sizeof(str_path)))
1768 {
1769 str_path[0] = '\0';
1770 }
1771 gate_win32_variant_clear(&var_path);
1772 }
1773 gate_win32_variant_init(&var_friendly); /* variant MUST be empty before invocation */
1774 hr = propbag->lpVtbl->Read(propbag, L"FriendlyName", &var_friendly, NULL);
1775 if (NOERROR == hr)
1776 {
1777 if (0 == gate_win32_variant_to_str(&var_friendly, str_name, sizeof(str_name)))
1778 {
1779 str_name[0] = '\0';
1780 }
1781 gate_win32_variant_clear(&var_friendly);
1782 }
1783 GATE_COM_RELEASE(propbag);
1784 if (!callback(str_path, str_name, moniker, param))
1785 {
1786 continue_enum = false;
1787 }
1788 }
1789 GATE_COM_RELEASE(moniker);
1790 }
1791 } while (0);
1792
1793 GATE_COM_RELEASE(enum_moniker);
1794 GATE_COM_RELEASE(system_dev_enum);
1795
1796 if (com_initialized)
1797 {
1798 gate_win32_com_uninit();
1799 }
1800
1801 return ret;
1802 }
1803
1804 struct dshow_list_video_device_param
1805 {
1806 dshow_video_device_info_t* infos;
1807 gate_size_t info_count;
1808 gate_size_t info_used;
1809 };
1810
1811 static gate_bool_t dshow_list_video_device_callback(char const* id, char const* name, IMoniker* moniker, void* param)
1812 {
1813 struct dshow_list_video_device_param* ptr = (struct dshow_list_video_device_param*)param;
1814 dshow_video_device_info_t* info = ptr->infos + ptr->info_used;
1815 if (ptr->info_used < ptr->info_count)
1816 {
1817 gate_str_print_text(info->uid, sizeof(info->uid), id, gate_str_length(id));
1818 gate_str_print_text(info->name, sizeof(info->name), name, gate_str_length(name));
1819 info->handle = 0;
1820 ++ptr->info_used;
1821 return true;
1822 }
1823 else
1824 {
1825 return false;
1826 }
1827 }
1828
1829 static gate_size_t dshow_list_video_devices(dshow_video_device_info_t* infos, gate_size_t info_count)
1830 {
1831 struct dshow_list_video_device_param param;
1832 param.infos = infos;
1833 param.info_count = info_count;
1834 param.info_used = 0;
1835
1836 dshow_enum_video_devices(&dshow_list_video_device_callback, &param);
1837 return param.info_used;
1838 }
1839
1840
1841 static HRESULT dshow_is_pin_connected(IPin* pin, gate_bool_t* ptr_result)
1842 {
1843 IPin* tmp = 0;
1844 HRESULT hr = pin->lpVtbl->ConnectedTo(pin, &tmp);
1845 if (SUCCEEDED(hr))
1846 {
1847 *ptr_result = true;
1848 }
1849 else if (hr == VFW_E_NOT_CONNECTED)
1850 {
1851 *ptr_result = false;
1852 hr = S_OK;
1853 }
1854 GATE_COM_RELEASE(tmp);
1855 return hr;
1856 }
1857
1858 static gate_bool_t const const_false = false;
1859 static gate_bool_t const const_true = true;
1860
1861
1862 static gate_size_t dshow_find_pins(IPin** pin_array, gate_size_t pin_array_len,
1863 IBaseFilter* filter, PIN_DIRECTION const* ptr_direction, gate_bool_t const* ptr_connected,
1864 gate_bool_t const* ptr_preview_only)
1865 {
1866 gate_size_t ret = 0; /* found pin objects */
1867 HRESULT hr;
1868 IEnumPins* pin_enum = NULL;
1869 IPin* current_pin = NULL;
1870 PIN_DIRECTION pin_dir;
1871 gate_bool_t is_connected;
1872 IKsPropertySet* ks_property_set = NULL;
1873 GUID guid_pin_category;
1874 DWORD cb_returned;
1875
1876 hr = filter->lpVtbl->EnumPins(filter, &pin_enum);
1877 if (SUCCEEDED(hr))
1878 {
1879 while (pin_array_len != 0)
1880 {
1881 if (S_OK != pin_enum->lpVtbl->Next(pin_enum, 1, &current_pin, NULL))
1882 {
1883 break;
1884 }
1885 do
1886 {
1887 if (ptr_direction != NULL)
1888 {
1889 /* filter for direction */
1890 if (S_OK != current_pin->lpVtbl->QueryDirection(current_pin, &pin_dir)) continue;
1891 if (pin_dir != *ptr_direction) continue;
1892 }
1893 if (ptr_connected != NULL)
1894 {
1895 /* filter for connection */
1896 is_connected = false;
1897 hr = dshow_is_pin_connected(current_pin, &is_connected);
1898 if (S_OK != hr) continue;
1899 if (*ptr_connected != is_connected) continue;
1900 }
1901 if (ptr_preview_only != NULL)
1902 {
1903 hr = GATE_COM_IFACE(current_pin, &gate_IID_IKsPropertySet, &ks_property_set);
1904 if (FAILED(hr)) continue;
1905 hr = ks_property_set->lpVtbl->Get(ks_property_set, &gate_AMPROPSETID_Pin, AMPROPERTY_PIN_CATEGORY, NULL, 0,
1906 &guid_pin_category, sizeof(guid_pin_category), &cb_returned);
1907 if (FAILED(hr)) continue;
1908 GATE_COM_RELEASE(ks_property_set);
1909 if (IsEqualGUID(&guid_pin_category, &gate_PIN_CATEGORY_CAPTURE))
1910 {
1911 if (*ptr_preview_only) continue;
1912 }
1913 else if (IsEqualGUID(&guid_pin_category, &gate_PIN_CATEGORY_PREVIEW))
1914 {
1915 if (!*ptr_preview_only) continue;
1916 }
1917 else
1918 {
1919 continue;
1920 }
1921 }
1922
1923 /* add pin to array */
1924 *pin_array = current_pin;
1925 ++pin_array;
1926 ++ret;
1927 --pin_array_len;
1928 current_pin = NULL;
1929 } while (0);
1930 GATE_COM_RELEASE(current_pin);
1931 }
1932 GATE_COM_RELEASE(pin_enum);
1933 }
1934 return ret;
1935 }
1936
1937 static gate_size_t dshow_find_pins_sorted(IPin** pin_array, gate_size_t pin_array_len,
1938 IBaseFilter* filter, PIN_DIRECTION const* ptr_direction,
1939 gate_bool_t const* ptr_connected)
1940 {
1941 gate_size_t count1 = dshow_find_pins(pin_array, pin_array_len, filter, ptr_direction, ptr_connected, &const_false);
1942 gate_size_t count2 = dshow_find_pins(&pin_array[count1], pin_array_len - count1, filter, ptr_direction, ptr_connected, &const_true);
1943 return count1 + count2;
1944 }
1945
1946 static HRESULT dShow_connect_filters(IGraphBuilder* graph, IPin* out_pin, IBaseFilter* dest_filter)
1947 {
1948 static PIN_DIRECTION const input_dir = PINDIR_INPUT;
1949 static gate_bool_t const not_connected = false;
1950 static gate_bool_t const no_preview = true;
1951 IPin* pin_array[256] = GATE_INIT_EMPTY;
1952 gate_size_t pin_array_len = sizeof(pin_array) / sizeof(pin_array[0]);
1953 gate_size_t ndx;
1954 HRESULT hr;
1955
1956 pin_array_len = dshow_find_pins(pin_array, pin_array_len, dest_filter, &input_dir, &not_connected, &no_preview);
1957 if (pin_array_len == 0)
1958 {
1959 hr = S_FALSE;
1960 }
1961 else
1962 {
1963 hr = graph->lpVtbl->Connect(graph, out_pin, pin_array[0]);
1964 for (ndx = 0; ndx != pin_array_len; ++ndx)
1965 {
1966 GATE_COM_RELEASE(pin_array[ndx]);
1967 }
1968 }
1969 return hr;
1970 }
1971
1972 struct dshow_open_video_device_param
1973 {
1974 gate_string_t const* desired_id;
1975 IBaseFilter* filter;
1976 };
1977
1978 static gate_bool_t dshow_open_video_device_callback(char const* id, char const* name, IMoniker* moniker, void* param)
1979 {
1980 struct dshow_open_video_device_param* ptr = (struct dshow_open_video_device_param*)param;
1981 HRESULT hr;
1982 if (0 == gate_str_compare(ptr->desired_id->str, ptr->desired_id->length, id, gate_str_length(id)))
1983 {
1984 /* device found */
1985 hr = moniker->lpVtbl->BindToObject(moniker, NULL, NULL, &gate_IID_IBaseFilter, (LPVOID*)&ptr->filter);
1986 if (SUCCEEDED(hr))
1987 {
1988 return false; /*cancel further search */
1989 }
1990 ptr->filter = NULL;
1991 }
1992 /* continue search */
1993 return true;
1994 }
1995
1996
1997
1998 static gate_result_t dshow_open_video_device(gate_string_t const* device_id, IBaseFilter** ptr_filter, IGraphBuilder** ptr_graph,
1999 ISampleGrabber** ptr_grabber, ISampleGrabberCB* ptr_grabber_cb, IBaseFilter** ptr_null_filter)
2000 {
2001 gate_result_t ret = GATE_RESULT_FAILED;
2002 ICreateDevEnum* dev_enum = NULL;
2003 IEnumMoniker* enum_moniker = NULL;
2004 IMoniker* ptr_moniker = NULL;
2005 IBaseFilter* filter = NULL;
2006 IGraphBuilder* graph = NULL;
2007 ISampleGrabber* grabber = NULL;
2008 IBaseFilter* grabber_filter = NULL;
2009 IBaseFilter* null_filter = NULL;
2010 HRESULT hr;
2011 struct dshow_open_video_device_param open_param;
2012
2013 *ptr_filter = NULL;
2014 *ptr_graph = NULL;
2015 *ptr_grabber = NULL;
2016 *ptr_null_filter = NULL;
2017
2018 do
2019 {
2020 open_param.desired_id = device_id;
2021 open_param.filter = NULL;
2022 ret = dshow_enum_video_devices(&dshow_open_video_device_callback, &open_param);
2023 GATE_BREAK_IF_FAILED(ret);
2024 if (open_param.filter == NULL)
2025 {
2026 ret = GATE_RESULT_NOMATCH;
2027 break;
2028 }
2029
2030 filter = open_param.filter;
2031 open_param.filter = NULL;
2032 hr = gate_win32_com_create_instance(&gate_CLSID_FilterGraph, &gate_IID_IGraphBuilder,
2033 (LPVOID*)&graph);
2034 if (FAILED(hr))
2035 {
2036 GATE_DEBUG_TRACE("Failed to create FilterGraph object");
2037 GATE_DEBUG_TRACE_VALUE(hr);
2038 break;
2039 }
2040
2041 hr = graph->lpVtbl->AddFilter(graph, filter, L"Video Source");
2042 if (FAILED(hr))
2043 {
2044 GATE_DEBUG_TRACE("Failed to add video source BaseFilter to FilterGraph");
2045 GATE_DEBUG_TRACE_VALUE(hr);
2046 break;
2047 }
2048
2049 hr = gate_win32_com_create_instance(&gate_CLSID_SampleGrabber, &gate_IID_ISampleGrabber,
2050 (LPVOID*)&grabber);
2051 if (FAILED(hr))
2052 {
2053 GATE_DEBUG_TRACE("Failed to create SampleGrabber");
2054 GATE_DEBUG_TRACE_VALUE(hr);
2055 break;
2056 }
2057
2058 hr = GATE_COM_IFACE(grabber, &gate_IID_IBaseFilter, &grabber_filter);
2059 if (FAILED(hr))
2060 {
2061 GATE_DEBUG_TRACE("Failed to access filter interface of SampleGrabber");
2062 GATE_DEBUG_TRACE_VALUE(hr);
2063 break;
2064 }
2065 hr = graph->lpVtbl->AddFilter(graph, grabber_filter, L"Sample Grabber Filter");
2066 GATE_COM_RELEASE(grabber_filter);
2067 if (FAILED(hr))
2068 {
2069 GATE_DEBUG_TRACE("Failed to access filter interface of SampleGrabber");
2070 GATE_DEBUG_TRACE_VALUE(hr);
2071 break;
2072 }
2073
2074 /*
2075 hr = gate_win32_com_create_instance(&gate_CLSID_NullRenderer, &gate_IID_IBaseFilter, &null_filter);
2076 if(FAILED(hr))
2077 {
2078 GATE_DEBUG_TRACE("Failed to create NullRenderer filter");
2079 GATE_DEBUG_TRACE_VALUE(hr);
2080 break;
2081 }
2082
2083 hr = graph->lpVtbl->AddFilter(graph, null_filter, L"Null Renderer Filter");
2084 GATE_COM_RELEASE(null_filter);
2085 if(FAILED(hr))
2086 {
2087 GATE_DEBUG_TRACE("Failed to add NullRenderer to graph");
2088 GATE_DEBUG_TRACE_VALUE(hr);
2089 break;
2090 }
2091 */
2092
2093 grabber->lpVtbl->SetCallback(grabber, ptr_grabber_cb, 1);
2094
2095 /* all steps succeeded: */
2096
2097 *ptr_filter = filter;
2098 *ptr_graph = graph;
2099 *ptr_grabber = grabber;
2100 *ptr_null_filter = null_filter;
2101 filter = NULL;
2102 graph = NULL;
2103 grabber = NULL;
2104 null_filter = NULL;
2105 ret = GATE_RESULT_OK;
2106
2107 } while (0);
2108
2109 GATE_COM_RELEASE(filter);
2110 GATE_COM_RELEASE(graph);
2111 GATE_COM_RELEASE(grabber);
2112 GATE_COM_RELEASE(null_filter);
2113 GATE_COM_RELEASE(enum_moniker);
2114 GATE_COM_RELEASE(dev_enum);
2115
2116 return ret;
2117 }
2118
2119
2120 static void dshow_close_video_resources(IGraphBuilder* graph, IBaseFilter* source_filter,
2121 ISampleGrabber* grabber, IBaseFilter* dest_filter)
2122 {
2123 gate_result_t ret = GATE_RESULT_FAILED;
2124 IBaseFilter* grabber_filter = NULL;
2125 HRESULT hr;
2126
2127 if (grabber != NULL)
2128 {
2129 hr = GATE_COM_IFACE(grabber, &gate_IID_IBaseFilter, &grabber_filter);
2130 if (FAILED(hr))
2131 {
2132 grabber_filter = NULL;
2133 }
2134 GATE_COM_RELEASE(grabber);
2135 }
2136 if (graph != NULL)
2137 {
2138 if (source_filter != NULL)
2139 {
2140 graph->lpVtbl->RemoveFilter(graph, source_filter);
2141 }
2142 if (grabber_filter != NULL)
2143 {
2144 graph->lpVtbl->RemoveFilter(graph, grabber_filter);
2145 }
2146 if (dest_filter != NULL)
2147 {
2148 graph->lpVtbl->RemoveFilter(graph, dest_filter);
2149 }
2150 GATE_COM_RELEASE(graph);
2151 }
2152
2153 GATE_COM_RELEASE(source_filter);
2154 GATE_COM_RELEASE(grabber_filter);
2155 GATE_COM_RELEASE(dest_filter);
2156 }
2157
2158 static gate_bool_t dshow_setup_pin(IPin* pin, gate_video_format_t const* format)
2159 {
2160 gate_bool_t ret = false;
2161 IAMStreamConfig* config = NULL;
2162 AM_MEDIA_TYPE* ptr_media_type = NULL;
2163 HRESULT hr;
2164 VIDEOINFOHEADER* video_info;
2165 do
2166 {
2167 hr = GATE_COM_IFACE(pin, &gate_IID_IAMStreamConfig, &config);
2168 if (FAILED(hr))
2169 {
2170 GATE_DEBUG_TRACE("Failed to get AMStreamConfig from Pin");
2171 GATE_DEBUG_TRACE_VALUE(hr);
2172 break;
2173 }
2174
2175 hr = config->lpVtbl->GetFormat(config, &ptr_media_type);
2176 if (FAILED(hr))
2177 {
2178 break;
2179 }
2180
2181 if (IsEqualGUID(&ptr_media_type->majortype, &gate_MEDIATYPE_Video)
2182 && IsEqualGUID(&ptr_media_type->formattype, &gate_FORMAT_VideoInfo)
2183 && (ptr_media_type->cbFormat >= sizeof(VIDEOINFOHEADER))
2184 && (ptr_media_type->pbFormat != NULL)
2185 )
2186 {
2187 video_info = (VIDEOINFOHEADER*)(ptr_media_type->pbFormat);
2188 video_info->bmiHeader.biCompression = format->compression;
2189 video_info->bmiHeader.biBitCount = format->bits_per_pixel;
2190 video_info->bmiHeader.biWidth = format->width;
2191 video_info->bmiHeader.biHeight = format->height;
2192
2193 hr = config->lpVtbl->SetFormat(config, ptr_media_type);
2194 if (SUCCEEDED(hr))
2195 {
2196 ret = true;
2197 }
2198 }
2199
2200 ret = true;
2201 } while (0);
2202
2203 if (ptr_media_type != NULL)
2204 {
2205 dshow_delete_media_type(ptr_media_type);
2206 }
2207 GATE_COM_RELEASE(config);
2208 return ret;
2209 }
2210
2211
2212 static void dshow_release_pin_array(IPin** pins, gate_size_t pin_count)
2213 {
2214 while (pin_count-- != 0)
2215 {
2216 GATE_COM_RELEASE(*pins);
2217 pins++;
2218 }
2219 }
2220
2221
2222 static gate_result_t dshow_connect_filter_pins(IGraphBuilder* graph, IBaseFilter* src_filter, IBaseFilter* dst_filter,
2223 gate_video_format_t const* format, IPin** ptr_out_pin, IPin** ptr_in_pin)
2224 {
2225 static PIN_DIRECTION const pin_output_direction = PINDIR_OUTPUT;
2226 static PIN_DIRECTION const pin_input_direction = PINDIR_INPUT;
2227 gate_bool_t no_preview = true;
2228 # define DSHOW_PIN_BUFFER_LEN 256
2229
2230 IPin* out_pins[DSHOW_PIN_BUFFER_LEN];
2231 gate_size_t out_pins_used = 0;
2232 IPin* in_pins[DSHOW_PIN_BUFFER_LEN];
2233 gate_size_t in_pins_used = 0;
2234 gate_size_t ndx_in, ndx_out;
2235 HRESULT hr;
2236 gate_result_t ret = GATE_RESULT_FAILED;
2237
2238 out_pins_used = dshow_find_pins(out_pins, DSHOW_PIN_BUFFER_LEN, src_filter, &pin_output_direction, NULL, &no_preview);
2239 in_pins_used = dshow_find_pins(in_pins, DSHOW_PIN_BUFFER_LEN, dst_filter, &pin_input_direction, NULL, &no_preview);
2240
2241 no_preview = false;
2242 /* try preview-PINs if possible */
2243 if (out_pins_used == 0)
2244 {
2245 out_pins_used = dshow_find_pins(out_pins, DSHOW_PIN_BUFFER_LEN, src_filter, &pin_output_direction, NULL, &no_preview);
2246 }
2247 if (in_pins_used == 0)
2248 {
2249 in_pins_used = dshow_find_pins(in_pins, DSHOW_PIN_BUFFER_LEN, dst_filter, &pin_input_direction, NULL, &no_preview);
2250 }
2251
2252 /* try PINs without advanced properties */
2253 if (out_pins_used == 0)
2254 {
2255 out_pins_used = dshow_find_pins(out_pins, DSHOW_PIN_BUFFER_LEN, src_filter, &pin_output_direction, NULL, NULL);
2256 }
2257 if (in_pins_used == 0)
2258 {
2259 in_pins_used = dshow_find_pins(in_pins, DSHOW_PIN_BUFFER_LEN, dst_filter, &pin_input_direction, NULL, NULL);
2260 }
2261
2262
2263 for (ndx_in = 0; ndx_in != in_pins_used; ++ndx_in)
2264 {
2265 for (ndx_out = 0; ndx_out != out_pins_used; ++ndx_out)
2266 {
2267 if (dshow_setup_pin(out_pins[ndx_out], format))
2268 {
2269 hr = graph->lpVtbl->Connect(graph, out_pins[ndx_out], in_pins[ndx_in]);
2270 if (SUCCEEDED(hr))
2271 {
2272 *ptr_out_pin = out_pins[ndx_out];
2273 *ptr_in_pin = in_pins[ndx_in];
2274 out_pins[ndx_out] = 0;
2275 in_pins[ndx_in] = 0;
2276 ret = GATE_RESULT_OK;
2277 ndx_in = in_pins_used - 1; /* leave outer loop */
2278 break;
2279 }
2280 else
2281 {
2282 GATE_DEBUG_TRACE("Failed to connect input and output pins");
2283 }
2284 }
2285 }
2286 }
2287
2288 dshow_release_pin_array(in_pins, in_pins_used);
2289 dshow_release_pin_array(out_pins, out_pins_used);
2290
2291 return ret;
2292 }
2293
2294 static gate_result_t dshow_set_pin_format(IPin* pin, gate_video_format_t const* format)
2295 {
2296 gate_result_t ret = GATE_RESULT_FAILED;
2297 IAMStreamConfig* config = NULL;
2298 HRESULT hr;
2299 int caps_count, caps_size, ndx;
2300 VIDEO_STREAM_CONFIG_CAPS config_caps;
2301 PIN_INFO pin_info;
2302 AM_MEDIA_TYPE* ptr_media_type;
2303 gate_real32_t min_fps, max_fps;
2304 VIDEOINFOHEADER* ptr_info_header;
2305
2306 do
2307 {
2308 hr = GATE_COM_IFACE(pin, &gate_IID_IAMStreamConfig, &config);
2309 if (FAILED(hr))
2310 {
2311 GATE_DEBUG_TRACE("Failed to access IAMStreamConfig interface");
2312 GATE_DEBUG_TRACE_VALUE(hr);
2313 break;
2314 }
2315 hr = config->lpVtbl->GetNumberOfCapabilities(config, &caps_count, &caps_size);
2316 if (FAILED(hr))
2317 {
2318 GATE_DEBUG_TRACE("Faild to get capabilities");
2319 GATE_DEBUG_TRACE_VALUE(hr);
2320 break;
2321 }
2322 if (caps_size != sizeof(VIDEO_STREAM_CONFIG_CAPS))
2323 {
2324 GATE_DEBUG_TRACE("Invalid capability size");
2325 GATE_DEBUG_TRACE_VALUE(caps_size);
2326 break;
2327 }
2328 hr = pin->lpVtbl->QueryPinInfo(pin, &pin_info);
2329 if (FAILED(hr))
2330 {
2331 GATE_DEBUG_TRACE("Failed to query pin infos");
2332 GATE_DEBUG_TRACE_VALUE(hr);
2333 break;
2334 }
2335 GATE_COM_RELEASE(pin_info.pFilter);
2336
2337 for (ndx = 0; ndx != caps_count; ++ndx)
2338 {
2339 hr = config->lpVtbl->GetStreamCaps(config, ndx, &ptr_media_type, (BYTE*)&config_caps);
2340 if (FAILED(hr))
2341 {
2342 continue;
2343 }
2344
2345 if (IsEqualGUID(&ptr_media_type->majortype, &gate_MEDIATYPE_Video)
2346 && IsEqualGUID(&ptr_media_type->formattype, &gate_FORMAT_VideoInfo)
2347 && (ptr_media_type->cbFormat >= sizeof(VIDEOINFOHEADER))
2348 && (ptr_media_type->pbFormat != NULL)
2349 )
2350 {
2351 min_fps = 10000000.0f / (gate_real32_t)config_caps.MinFrameInterval;
2352 max_fps = 10000000.0f / (gate_real32_t)config_caps.MaxFrameInterval;
2353
2354 ptr_info_header = (VIDEOINFOHEADER*)ptr_media_type->pbFormat;
2355 if ((ptr_info_header->bmiHeader.biCompression == format->compression)
2356 && (ptr_info_header->bmiHeader.biBitCount == format->bits_per_pixel)
2357 && (ptr_info_header->bmiHeader.biWidth == format->width)
2358 && (ptr_info_header->bmiHeader.biHeight == format->height)
2359 )
2360 {
2361 config->lpVtbl->SetFormat(config, ptr_media_type);
2362 ndx = caps_count - 1;
2363 ret = GATE_RESULT_OK;
2364 }
2365 }
2366 dshow_delete_media_type(ptr_media_type);
2367 }
2368
2369 } while (0);
2370
2371 GATE_COM_RELEASE(config);
2372 return ret;
2373 }
2374
2375 static gate_size_t dshow_read_supported_pin_formats(IPin* pin, gate_video_format_t* formats, gate_size_t format_count)
2376 {
2377 gate_size_t ret = 0;
2378 IAMStreamConfig* config = NULL;
2379 int caps_count, caps_size, ndx;
2380 HRESULT hr;
2381 VIDEO_STREAM_CONFIG_CAPS config_caps;
2382 AM_MEDIA_TYPE* ptr_media_type;
2383 gate_real32_t min_fps, max_fps;
2384 VIDEOINFOHEADER* ptr_info_header;
2385
2386 do
2387 {
2388 hr = GATE_COM_IFACE(pin, &gate_IID_IAMStreamConfig, &config);
2389 if (FAILED(hr))
2390 {
2391 GATE_DEBUG_TRACE("Failed to access IID_IAMStreamConfig");
2392 GATE_DEBUG_TRACE_VALUE(hr);
2393 break;
2394 }
2395 hr = config->lpVtbl->GetNumberOfCapabilities(config, &caps_count, &caps_size);
2396 if (FAILED(hr))
2397 {
2398 GATE_DEBUG_TRACE("Failed to get capabilities");
2399 GATE_DEBUG_TRACE_VALUE(hr);
2400 break;
2401 }
2402 if (sizeof(VIDEO_STREAM_CONFIG_CAPS) != caps_size)
2403 {
2404 GATE_DEBUG_TRACE("Invalid capability size");
2405 GATE_DEBUG_TRACE_VALUE(caps_size);
2406 break;
2407 }
2408 for (ndx = 0; ndx != caps_count; ++ndx)
2409 {
2410 hr = config->lpVtbl->GetStreamCaps(config, ndx, &ptr_media_type, (BYTE*)&config_caps);
2411 if (FAILED(hr))
2412 {
2413 continue;
2414 }
2415 if (IsEqualGUID(&ptr_media_type->majortype, &gate_MEDIATYPE_Video)
2416 && IsEqualGUID(&ptr_media_type->formattype, &gate_FORMAT_VideoInfo)
2417 && (ptr_media_type->cbFormat >= sizeof(VIDEOINFOHEADER))
2418 && (ptr_media_type->pbFormat != NULL)
2419 )
2420 {
2421 min_fps = 10000000.0f / (gate_real32_t)config_caps.MinFrameInterval;
2422 max_fps = 10000000.0f / (gate_real32_t)config_caps.MaxFrameInterval;
2423
2424 ptr_info_header = (VIDEOINFOHEADER*)ptr_media_type->pbFormat;
2425
2426 if (ret < format_count)
2427 {
2428 formats->bits_per_pixel = ptr_info_header->bmiHeader.biBitCount;
2429 formats->compression = ptr_info_header->bmiHeader.biCompression;
2430 formats->encoding_id = 0;
2431 formats->frames_per_second = max_fps;
2432 formats->width = ptr_info_header->bmiHeader.biWidth;
2433 formats->height = ptr_info_header->bmiHeader.biHeight;
2434 ++ret;
2435 ++formats;
2436 }
2437 if ((max_fps != min_fps) && (ret < format_count))
2438 {
2439 formats->bits_per_pixel = ptr_info_header->bmiHeader.biBitCount;
2440 formats->compression = ptr_info_header->bmiHeader.biCompression;
2441 formats->encoding_id = 0;
2442 formats->frames_per_second = min_fps;
2443 formats->width = ptr_info_header->bmiHeader.biWidth;
2444 formats->height = ptr_info_header->bmiHeader.biHeight;
2445 ++ret;
2446 ++formats;
2447 }
2448 }
2449 dshow_delete_media_type(ptr_media_type);
2450 }
2451
2452 } while (0);
2453
2454 GATE_COM_RELEASE(config);
2455 return ret;
2456 }
2457
2458 static void dshow_end_session(IGraphBuilder* graph, ISampleGrabber* grabber, IPin* src_out, IPin* grab_in, IPin* grab_out, IPin* dest_in)
2459 {
2460 HRESULT hr;
2461 IMediaControl* media_control = NULL;
2462 hr = GATE_COM_IFACE(graph, &gate_IID_IMediaControl, &media_control);
2463 if (SUCCEEDED(hr))
2464 {
2465 media_control->lpVtbl->Stop(media_control);
2466 GATE_COM_RELEASE(media_control);
2467 }
2468
2469 if (grabber != NULL)
2470 {
2471 grabber->lpVtbl->SetCallback(grabber, NULL, 0);
2472 }
2473 if (src_out != NULL) graph->lpVtbl->Disconnect(graph, src_out);
2474 if (grab_in != NULL) graph->lpVtbl->Disconnect(graph, grab_in);
2475 if (grab_out != NULL) graph->lpVtbl->Disconnect(graph, grab_out);
2476 if (dest_in != NULL) graph->lpVtbl->Disconnect(graph, dest_in);
2477 }
2478
2479 static gate_result_t dshow_start_session(gate_video_format_t* format, IGraphBuilder* graph,
2480 IBaseFilter* src_filter, ISampleGrabber* grabber,
2481 IBaseFilter* dest_filter, ISampleGrabberCB* callback,
2482 IPin** ptr_src_out, IPin** ptr_grabber_in,
2483 IPin** ptr_grabber_out, IPin** ptr_dest_in)
2484 {
2485 static PIN_DIRECTION const direction_in = PINDIR_INPUT;
2486 static PIN_DIRECTION const direction_out = PINDIR_OUTPUT;
2487 static gate_bool_t const const_false = false;
2488 static gate_bool_t const const_true = true;
2489
2490 gate_result_t result = GATE_RESULT_FAILED;
2491 IBaseFilter* grabber_filter = NULL;
2492 IMediaControl* media_control = NULL;
2493 HRESULT hr;
2494 IPin* src_pins[256];
2495 gate_size_t src_pins_used = 0;
2496 IPin* grabber_in_pins[256];
2497 gate_size_t grabber_in_pins_used = 0;
2498 IPin* grabber_out_pins[256];
2499 gate_size_t grabber_out_pins_used = 0;
2500 IPin* dst_pins[256];
2501 gate_size_t dst_pins_used = 0;
2502 AM_MEDIA_TYPE media_type;
2503
2504 do
2505 {
2506 *ptr_src_out = NULL;
2507 *ptr_grabber_in = NULL;
2508 *ptr_grabber_out = NULL;
2509 *ptr_dest_in = NULL;
2510
2511 hr = GATE_COM_IFACE(grabber, &gate_IID_IBaseFilter, &grabber_filter);
2512 if (FAILED(hr))
2513 {
2514 GATE_DEBUG_TRACE("Failed to access BaseFilter of SampleGrabber");
2515 GATE_DEBUG_TRACE_VALUE(hr);
2516 break;
2517 }
2518 hr = GATE_COM_IFACE(graph, &gate_IID_IMediaControl, &media_control);
2519 if (FAILED(hr))
2520 {
2521 GATE_DEBUG_TRACE("Failed to access MediaControl of GraphBuilder");
2522 GATE_DEBUG_TRACE_VALUE(hr);
2523 break;
2524 }
2525
2526 gate_mem_clear(&media_type, sizeof(media_type));
2527
2528 media_type.majortype = gate_MEDIATYPE_Video;
2529 media_type.subtype = gate_MEDIASUBTYPE_RGB32;
2530 grabber->lpVtbl->SetMediaType(grabber, &media_type);
2531 grabber->lpVtbl->SetOneShot(grabber, FALSE);
2532 grabber->lpVtbl->SetBufferSamples(grabber, FALSE);
2533 grabber->lpVtbl->SetCallback(grabber, callback, 1);
2534
2535 src_pins_used = dshow_find_pins_sorted(src_pins, sizeof(src_pins) / sizeof(src_pins[0]),
2536 src_filter, &direction_out, &const_false);
2537
2538 grabber_in_pins_used = dshow_find_pins(grabber_in_pins, sizeof(grabber_in_pins) / sizeof(grabber_in_pins[0]),
2539 grabber_filter, &direction_in, &const_false, NULL);
2540
2541 grabber_out_pins_used = dshow_find_pins(grabber_out_pins, sizeof(grabber_out_pins) / sizeof(grabber_out_pins[0]),
2542 grabber_filter, &direction_out, &const_false, NULL);
2543
2544 dst_pins_used = dshow_find_pins(dst_pins, sizeof(dst_pins) / sizeof(dst_pins[0]),
2545 dest_filter, &direction_in, &const_false, NULL);
2546
2547 if ((src_pins_used == 0) || (grabber_in_pins_used == 0))
2548 {
2549 GATE_DEBUG_TRACE("No source and grabber pins present");
2550 break;
2551 }
2552
2553 result = dshow_set_pin_format(src_pins[0], format);
2554 GATE_BREAK_IF_FAILED_TRACE(result, "Failed to set source pin format");
2555 result = GATE_RESULT_FAILED;
2556
2557 hr = graph->lpVtbl->Connect(graph, src_pins[0], grabber_in_pins[0]);
2558 if (FAILED(hr))
2559 {
2560 GATE_DEBUG_TRACE("Failed to connect source and grabber pins");
2561 break;
2562 }
2563 *ptr_src_out = src_pins[0];
2564 *ptr_grabber_in = grabber_in_pins[0];
2565 src_pins[0] = NULL;
2566 grabber_in_pins[0] = NULL;
2567
2568 if ((grabber_out_pins_used != 0) && (dst_pins_used != 0))
2569 {
2570 hr = graph->lpVtbl->Connect(graph, grabber_out_pins[0], dst_pins[0]);
2571 if (SUCCEEDED(hr))
2572 {
2573 *ptr_grabber_out = grabber_out_pins[0];
2574 *ptr_dest_in = dst_pins[0];
2575 grabber_out_pins[0] = NULL;
2576 dst_pins[0] = NULL;
2577 }
2578 }
2579
2580 hr = media_control->lpVtbl->Run(media_control);
2581 if (FAILED(hr))
2582 {
2583 dshow_end_session(graph, grabber, *ptr_src_out, *ptr_grabber_in, *ptr_grabber_out, *ptr_dest_in);
2584 GATE_COM_RELEASE(*ptr_src_out); *ptr_src_out = NULL;
2585 GATE_COM_RELEASE(*ptr_grabber_in); *ptr_grabber_in = NULL;
2586 GATE_COM_RELEASE(*ptr_grabber_out); *ptr_grabber_out = NULL;
2587 GATE_COM_RELEASE(*ptr_dest_in); *ptr_dest_in = NULL;
2588 GATE_DEBUG_TRACE("Failed to run MediaControl");
2589 break;
2590 }
2591 result = GATE_RESULT_OK;
2592 } while (0);
2593
2594 dshow_release_pin_array(src_pins, src_pins_used);
2595 dshow_release_pin_array(grabber_in_pins, grabber_in_pins_used);
2596 dshow_release_pin_array(grabber_out_pins, grabber_out_pins_used);
2597 dshow_release_pin_array(dst_pins, dst_pins_used);
2598 GATE_COM_RELEASE(grabber_filter);
2599 GATE_COM_RELEASE(media_control);
2600
2601 return result;
2602 }
2603
2604
2605 typedef struct gate_video_source_dshow_class
2606 {
2607 GATE_INTERFACE_VTBL(gate_video_source) const* vtbl;
2608
2609 gate_atomic_int_t ref_counter;
2610 dshow_video_device_info_t device_info;
2611 gate_win32_com_queue_t com_queue;
2612
2613 IBaseFilter* base_filter; /**< video source */
2614 IGraphBuilder* graph_builder; /**< rendering graph */
2615 ISampleGrabber* sample_grabber; /**< video target frame collector */
2616 IBaseFilter* null_filter; /**< currently unused */
2617
2618 gate_video_format_t video_format;
2619 ISampleGrabberCBImpl sample_grabber_cb; /**< sample grabber callback instance */
2620 } gate_video_source_dshow_t;
2621
2622 static char const* video_source_dshow_get_interface_name(void* obj);
2623 static void video_source_dshow_release(void* obj);
2624 static int video_source_dshow_retain(void* obj);
2625
2626 static char const* video_source_dshow_get_id(void* obj);
2627 static char const* video_source_dshow_get_name(void* obj);
2628 static gate_intptr_t video_source_dshow_get_handle(void* obj);
2629 static gate_size_t video_source_dshow_get_supported_formats(void* obj, gate_video_format_t* format_buffer, gate_size_t format_buffer_count);
2630
2631 static gate_result_t video_source_dshow_open(void* obj, gate_video_format_t const* format);
2632 static gate_result_t video_source_dshow_close(void* obj);
2633
2634 static gate_result_t video_source_dshow_read(void* obj, gate_video_frame_t* frame);
2635
2636 static GATE_INTERFACE_VTBL(gate_video_source) gate_video_source_dshow_vtbl;
2637 static void gate_init_video_source_dshow_vtbl()
2638 {
2639 if (!gate_video_source_dshow_vtbl.get_interface_name)
2640 {
2641 GATE_INTERFACE_VTBL(gate_video_source) const local_vtbl =
2642 {
2643 &video_source_dshow_get_interface_name,
2644 &video_source_dshow_release,
2645 &video_source_dshow_retain,
2646
2647 &video_source_dshow_read,
2648
2649 &video_source_dshow_get_id,
2650 &video_source_dshow_get_name,
2651 &video_source_dshow_get_handle,
2652 &video_source_dshow_get_supported_formats,
2653
2654 &video_source_dshow_open,
2655 &video_source_dshow_close
2656 };
2657 gate_video_source_dshow_vtbl = local_vtbl;
2658 }
2659 }
2660
2661
2662 static void video_source_dshow_destruct(gate_video_source_dshow_t* obj)
2663 {
2664 video_source_dshow_close(obj);
2665 gate_win32_com_queue_stop(&obj->com_queue);
2666 gate_win32_com_queue_destroy(&obj->com_queue);
2667 sample_grabber_cb_impl_destroy(&obj->sample_grabber_cb);
2668 }
2669
2670 static gate_video_source_dshow_t* video_source_dshow_construct(dshow_video_device_info_t const* info)
2671 {
2672 gate_video_source_dshow_t* ret = NULL;
2673 gate_video_source_dshow_t* obj = NULL;
2674 gate_result_t result;
2675
2676 do
2677 {
2678 obj = (gate_video_source_dshow_t*)gate_mem_alloc(sizeof(gate_video_source_dshow_t));
2679 if (obj == NULL)
2680 {
2681 break;
2682 }
2683
2684 gate_mem_clear(obj, sizeof(gate_video_source_dshow_t));
2685
2686 gate_init_video_source_dshow_vtbl();
2687 obj->vtbl = &gate_video_source_dshow_vtbl;
2688 gate_atomic_int_init(&obj->ref_counter, 1);
2689
2690 gate_mem_copy(&obj->device_info, info, sizeof(dshow_video_device_info_t));
2691
2692 result = sample_grabber_cb_impl_init(&obj->sample_grabber_cb);
2693 GATE_BREAK_IF_FAILED(result);
2694
2695 result = gate_win32_com_queue_create(&obj->com_queue);
2696 GATE_BREAK_IF_FAILED(result);
2697
2698 result = gate_win32_com_queue_start(&obj->com_queue);
2699 GATE_BREAK_IF_FAILED(result);
2700
2701 ret = obj;
2702 obj = NULL;
2703 } while (0);
2704
2705 if (obj != NULL)
2706 {
2707 video_source_dshow_destruct(obj);
2708 }
2709 return ret;
2710 }
2711
2712 static gate_result_t video_source_dshow_load(gate_video_source_dshow_t* this_ptr)
2713 {
2714 gate_result_t ret;
2715 gate_string_t device_id = GATE_STRING_INIT_EMPTY;
2716
2717 do
2718 {
2719 if ((this_ptr->base_filter != NULL) && (this_ptr->graph_builder != NULL))
2720 {
2721 /* already loaded */
2722 ret = GATE_RESULT_OK;
2723 }
2724 else
2725 {
2726 gate_string_create_static(&device_id, this_ptr->device_info.uid);
2727 ret = dshow_open_video_device(&device_id, &this_ptr->base_filter, &this_ptr->graph_builder,
2728 &this_ptr->sample_grabber, (ISampleGrabberCB*)&this_ptr->sample_grabber_cb,
2729 &this_ptr->null_filter);
2730 gate_string_release(&device_id);
2731 }
2732 } while (0);
2733 return ret;
2734 }
2735 static void video_source_dshow_unload(gate_video_source_dshow_t* this_ptr)
2736 {
2737 GATE_COM_RELEASE(this_ptr->null_filter);
2738 GATE_COM_RELEASE(this_ptr->sample_grabber);
2739 GATE_COM_RELEASE(this_ptr->graph_builder);
2740 GATE_COM_RELEASE(this_ptr->base_filter);
2741
2742 this_ptr->null_filter = NULL;
2743 this_ptr->sample_grabber = NULL;
2744 this_ptr->graph_builder = NULL;
2745 this_ptr->base_filter = NULL;
2746 }
2747
2748
2749 static char const* video_source_dshow_get_interface_name(void* obj)
2750 {
2751 gate_video_source_dshow_t* this_ptr = (gate_video_source_dshow_t*)obj;
2752 return GATE_INTERFACE_NAME_VIDEO_SOURCE;
2753 }
2754 static void video_source_dshow_release(void* obj)
2755 {
2756 gate_video_source_dshow_t* this_ptr = (gate_video_source_dshow_t*)obj;
2757 if (0 == gate_atomic_int_dec(&this_ptr->ref_counter))
2758 {
2759 video_source_dshow_destruct(this_ptr);
2760 gate_mem_dealloc(obj);
2761 }
2762 }
2763 static int video_source_dshow_retain(void* obj)
2764 {
2765 gate_video_source_dshow_t* this_ptr = (gate_video_source_dshow_t*)obj;
2766 return gate_atomic_int_inc(&this_ptr->ref_counter);
2767 }
2768
2769 static char const* video_source_dshow_get_id(void* obj)
2770 {
2771 gate_video_source_dshow_t* this_ptr = (gate_video_source_dshow_t*)obj;
2772 return this_ptr->device_info.uid;
2773 }
2774 static char const* video_source_dshow_get_name(void* obj)
2775 {
2776 gate_video_source_dshow_t* this_ptr = (gate_video_source_dshow_t*)obj;
2777 return this_ptr->device_info.name;
2778 }
2779 static gate_intptr_t video_source_dshow_get_handle(void* obj)
2780 {
2781 gate_video_source_dshow_t* this_ptr = (gate_video_source_dshow_t*)obj;
2782 return this_ptr->device_info.handle;
2783 }
2784
2785 struct dshow_get_supported_formats_params
2786 {
2787 gate_video_source_dshow_t* video_source;
2788 gate_video_format_t* format_buffer;
2789 gate_size_t format_buffer_count;
2790 gate_size_t return_value;
2791 };
2792
2793 static gate_result_t dshow_get_supported_formats(void* ptr_params)
2794 {
2795 struct dshow_get_supported_formats_params* params = (struct dshow_get_supported_formats_params*)ptr_params;
2796 gate_result_t ret;
2797 IPin* pins[16] = GATE_INIT_EMPTY;
2798 gate_size_t pin_count = sizeof(pins) / sizeof(pins[0]);
2799 gate_size_t pins_used = 0;
2800 gate_size_t index;
2801 PIN_DIRECTION pin_dir = PINDIR_OUTPUT;
2802 gate_bool_t connected_pins = false;
2803 gate_bool_t preview_pins = false;
2804 gate_size_t formats_used = 0;
2805 gate_size_t formats_found;
2806
2807
2808 do
2809 {
2810 ret = video_source_dshow_load(params->video_source);
2811 GATE_BREAK_IF_FAILED(ret);
2812
2813 pins_used = dshow_find_pins(pins, pin_count, params->video_source->base_filter, &pin_dir, &connected_pins, &preview_pins);
2814 for (index = 0; index != pins_used; ++index)
2815 {
2816 formats_found = dshow_read_supported_pin_formats(
2817 pins[index], &params->format_buffer[formats_used], params->format_buffer_count - formats_used);
2818 formats_used += formats_found;
2819 }
2820 params->return_value = formats_used;
2821 } while (0);
2822
2823 for (index = 0; index != pins_used; ++index)
2824 {
2825 GATE_COM_RELEASE(pins[index]);
2826 }
2827 return ret;
2828 }
2829
2830 static gate_size_t video_source_dshow_get_supported_formats(void* obj, gate_video_format_t* format_buffer, gate_size_t format_buffer_count)
2831 {
2832 gate_video_source_dshow_t* this_ptr = (gate_video_source_dshow_t*)obj;
2833 gate_result_t ret = GATE_RESULT_NOTIMPLEMENTED;
2834 gate_result_t func_result = GATE_RESULT_FAILED;
2835 struct dshow_get_supported_formats_params params;
2836
2837 params.video_source = this_ptr;
2838 params.format_buffer = format_buffer;
2839 params.format_buffer_count = format_buffer_count;
2840 params.return_value = 0;
2841
2842 ret = gate_win32_com_queue_execute(&this_ptr->com_queue, &dshow_get_supported_formats, &params, &func_result);
2843 if (GATE_FAILED(ret) || GATE_FAILED(func_result))
2844 {
2845 return 0;
2846 }
2847 else
2848 {
2849 return params.return_value;
2850 }
2851 }
2852
2853 struct dshow_open_params
2854 {
2855 gate_video_source_dshow_t* video_source;
2856 gate_video_format_t const* format;
2857 };
2858
2859 static gate_result_t dshow_open(void* ptr_params)
2860 {
2861 struct dshow_open_params* params = (struct dshow_open_params*)ptr_params;
2862 gate_result_t ret = GATE_RESULT_FAILED;
2863 IMediaControl* ptr_media_control = NULL;
2864 HRESULT hr;
2865 IBaseFilter* grabber_filter = NULL;
2866 IPin* source_output_pin = NULL;
2867 IPin* grabber_input_pin = NULL;
2868
2869 do
2870 {
2871 ret = video_source_dshow_load(params->video_source);
2872 GATE_BREAK_IF_FAILED(ret);
2873
2874 hr = params->video_source->sample_grabber->lpVtbl->QueryInterface(
2875 params->video_source->sample_grabber, &gate_IID_IBaseFilter, (LPVOID*)&grabber_filter);
2876 if (FAILED(hr))
2877 {
2878 ret = GATE_RESULT_NOTAVAILABLE;
2879 break;
2880 }
2881
2882 gate_mem_copy(&params->video_source->video_format, params->format, sizeof(params->video_source->video_format));
2883
2884 ret = dshow_connect_filter_pins(params->video_source->graph_builder,
2885 params->video_source->base_filter,
2886 grabber_filter,
2887 &params->video_source->video_format,
2888 &source_output_pin,
2889 &grabber_input_pin);
2890 GATE_BREAK_IF_FAILED(ret);
2891
2892
2893 hr = params->video_source->graph_builder->lpVtbl->QueryInterface(
2894 params->video_source->graph_builder, &gate_IID_IMediaControl, (void**)&ptr_media_control);
2895 if (FAILED(hr))
2896 {
2897 ret = GATE_RESULT_NOTAVAILABLE;
2898 break;
2899 }
2900
2901 hr = ptr_media_control->lpVtbl->Run(ptr_media_control);
2902 if (FAILED(hr))
2903 {
2904 ret = GATE_RESULT_FAILED;
2905 break;
2906 }
2907
2908 ret = GATE_RESULT_OK;
2909 } while (0);
2910
2911 GATE_COM_RELEASE(ptr_media_control);
2912 GATE_COM_RELEASE(source_output_pin);
2913 GATE_COM_RELEASE(grabber_input_pin);
2914 GATE_COM_RELEASE(grabber_filter);
2915
2916 return ret;
2917 }
2918
2919 static gate_result_t video_source_dshow_open(void* obj, gate_video_format_t const* format)
2920 {
2921 gate_video_source_dshow_t* this_ptr = (gate_video_source_dshow_t*)obj;
2922 gate_result_t ret = GATE_RESULT_NOTIMPLEMENTED;
2923 gate_result_t func_result = GATE_RESULT_FAILED;
2924 struct dshow_open_params params;
2925 params.video_source = this_ptr;
2926 params.format = format;
2927
2928 ret = gate_win32_com_queue_execute(&this_ptr->com_queue, &dshow_open, &params, &func_result);
2929 if (GATE_SUCCEEDED(ret))
2930 {
2931 ret = func_result;
2932 }
2933 return ret;
2934 }
2935
2936 static gate_result_t dshow_close(void* ptr_param)
2937 {
2938 gate_video_source_dshow_t* this_ptr = (gate_video_source_dshow_t*)ptr_param;
2939 gate_result_t ret = GATE_RESULT_FAILED;
2940 IMediaControl* ptr_media_control = NULL;
2941 HRESULT hr;
2942
2943 do
2944 {
2945 if (this_ptr->graph_builder != NULL)
2946 {
2947 hr = this_ptr->graph_builder->lpVtbl->QueryInterface(
2948 this_ptr->graph_builder, &gate_IID_IMediaControl, (void**)&ptr_media_control);
2949 if (SUCCEEDED(hr))
2950 {
2951 hr = ptr_media_control->lpVtbl->Stop(ptr_media_control);
2952 GATE_COM_RELEASE(ptr_media_control);
2953 }
2954 }
2955
2956 if (this_ptr->base_filter)
2957 {
2958 if (this_ptr->graph_builder != NULL)
2959 {
2960 hr = this_ptr->graph_builder->lpVtbl->RemoveFilter(this_ptr->graph_builder, this_ptr->base_filter);
2961 }
2962 GATE_COM_RELEASE(this_ptr->base_filter);
2963 this_ptr->base_filter = NULL;
2964 }
2965
2966 if (this_ptr->sample_grabber)
2967 {
2968 GATE_COM_RELEASE(this_ptr->sample_grabber);
2969 this_ptr->sample_grabber = NULL;
2970 }
2971
2972 if (this_ptr->null_filter)
2973 {
2974 if (this_ptr->graph_builder != NULL)
2975 {
2976 hr = this_ptr->graph_builder->lpVtbl->RemoveFilter(this_ptr->graph_builder, this_ptr->null_filter);
2977 }
2978 GATE_COM_RELEASE(this_ptr->null_filter);
2979 this_ptr->null_filter = NULL;
2980 }
2981
2982 if (this_ptr->graph_builder != NULL)
2983 {
2984 GATE_COM_RELEASE(this_ptr->graph_builder);
2985 this_ptr->graph_builder = NULL;
2986 }
2987
2988 ret = GATE_RESULT_OK;
2989 } while (0);
2990
2991 return ret;
2992 }
2993
2994 static gate_result_t video_source_dshow_close(void* obj)
2995 {
2996 gate_video_source_dshow_t* this_ptr = (gate_video_source_dshow_t*)obj;
2997 gate_result_t ret = GATE_RESULT_NOTIMPLEMENTED;
2998 gate_result_t func_result = GATE_RESULT_FAILED;
2999
3000 ret = gate_win32_com_queue_execute(&this_ptr->com_queue, &dshow_close, this_ptr, &func_result);
3001 if (GATE_SUCCEEDED(ret))
3002 {
3003 ret = func_result;
3004 }
3005 return ret;
3006 }
3007
3008
3009 static gate_result_t video_source_dshow_read(void* obj, gate_video_frame_t* frame)
3010 {
3011 gate_video_source_dshow_t* this_ptr = (gate_video_source_dshow_t*)obj;
3012 gate_result_t ret = GATE_RESULT_NOTIMPLEMENTED;
3013
3014 do
3015 {
3016 if (!this_ptr || !frame)
3017 {
3018 ret = GATE_RESULT_NULLPOINTER;
3019 break;
3020 }
3021 gate_mem_clear(frame, sizeof(gate_video_frame_t));
3022
3023 gate_mem_copy(&frame->format, &this_ptr->video_format, sizeof(gate_video_format_t));
3024
3025 ret = sample_grabber_export_image(&this_ptr->sample_grabber_cb, frame);
3026
3027 } while (0);
3028
3029 return ret;
3030 }
3031
3032 struct dshow_read_async_params
3033 {
3034 gate_video_source_dshow_t* video_source;
3035 gate_delegate_t const* completion;
3036 };
3037
3038 static gate_result_t dshow_read_async(void* ptr_params)
3039 {
3040 struct dshow_read_async_params* params = (struct dshow_read_async_params*)ptr_params;
3041 gate_result_t ret = GATE_RESULT_FAILED;
3042 /* TODO */
3043 return ret;
3044 }
3045
3046 static gate_result_t video_source_dshow_read_async(void* obj, gate_delegate_t const* completion)
3047 {
3048 gate_video_source_dshow_t* this_ptr = (gate_video_source_dshow_t*)obj;
3049 gate_result_t ret = GATE_RESULT_NOTIMPLEMENTED;
3050 gate_result_t func_result = GATE_RESULT_FAILED;
3051
3052 struct dshow_read_async_params params;
3053 params.video_source = this_ptr;
3054 params.completion = completion;
3055
3056 ret = gate_win32_com_queue_execute(&this_ptr->com_queue, &dshow_read_async, &params, &func_result);
3057 if (GATE_SUCCEEDED(ret))
3058 {
3059 ret = func_result;
3060 }
3061 return ret;
3062 }
3063
3064
3065 #endif
3066
3067
3068
3069 gate_result_t gate_video_sources_enum(gate_delegate_t const* callback, void* param)
3070 {
3071 gate_result_t result = GATE_RESULT_OK;
3072 UINT index;
3073 gate_video_source_t* impl = NULL;
3074 do
3075 {
3076
3077 #if defined(GATE_IO_VIDEOSOURCES_USE_DSHOW)
3078 result = GATE_RESULT_OK;
3079
3080 do
3081 {
3082 gate_size_t count;
3083 dshow_video_device_info_t infos[24];
3084 gate_video_source_dshow_t* dshow_impl;
3085
3086 count = dshow_list_video_devices(infos, sizeof(infos) / sizeof(infos[0]));
3087 for (index = 0; index != count; ++index)
3088 {
3089 dshow_impl = video_source_dshow_construct(&infos[index]);
3090 if (dshow_impl != NULL)
3091 {
3092 if (callback != NULL)
3093 {
3094 impl = (gate_video_source_t*)dshow_impl;
3095 result = gate_delegate_invoke(callback, impl, param);
3096 gate_object_release(impl);
3097 }
3098 GATE_BREAK_IF_FAILED(result);
3099 }
3100 }
3101 } while (0);
3102 GATE_BREAK_IF_FAILED(result);
3103 #endif
3104
3105 #if defined(GATE_IO_VIDEOSOURCES_USE_V4W)
3106 do
3107 {
3108 result = load_vfw_functions();
3109 GATE_BREAK_IF_FAILED_TRACE(result, "Failed to load VFW functions");
3110 if (!capGetDriverDescriptionFunc)
3111 {
3112 result = GATE_RESULT_NOTSUPPORTED;
3113 break;
3114 }
3115 for (index = 0; index < 10; ++index)
3116 {
3117 result = get_vfw_source(index, &impl);
3118 if (result == GATE_RESULT_NOMATCH)
3119 {
3120 /* this index is not available */
3121 result = GATE_RESULT_OK;
3122 continue;
3123 }
3124 GATE_BREAK_IF_FAILED(result);
3125
3126 if (callback != NULL)
3127 {
3128 result = gate_delegate_invoke(callback, impl, param);
3129 }
3130 gate_object_release(impl);
3131 GATE_BREAK_IF_FAILED(result);
3132 }
3133 } while (0);
3134 GATE_BREAK_IF_FAILED(result);
3135 #endif
3136
3137
3138 } while (0);
3139 return result;
3140 }
3141
3142 gate_result_t gate_video_source_by_id(char const* id, gate_video_source_t** ptr_source)
3143 {
3144 gate_result_t result = GATE_RESULT_NOMATCH;
3145 gate_size_t id_len = gate_str_length(id);
3146 gate_uint8_t vfw_id = 0;
3147 gate_video_source_t* source = NULL;
3148
3149 do
3150 {
3151 #if defined(GATE_IO_VIDEOSOURCES_USE_V4W)
3152 if (id_len == 4)
3153 {
3154 if ((id[0] == 'v') && (id[1] == 'f') && (id[2] == 'w'))
3155 {
3156 if (gate_str_parse_digit(&id[3], &vfw_id))
3157 {
3158 result = load_vfw_functions();
3159 GATE_BREAK_IF_FAILED_TRACE(result, "Failed to load VFW functions");
3160 result = get_vfw_source(vfw_id, &source);
3161 GATE_BREAK_IF_FAILED(result);
3162 }
3163 }
3164 }
3165 #endif
3166 #if defined(GATE_IO_VIDEOSOURCES_USE_DSHOW)
3167 do
3168 {
3169 gate_size_t index;
3170 gate_size_t count;
3171 dshow_video_device_info_t infos[24];
3172 gate_video_source_dshow_t* dshow_impl;
3173
3174 result = GATE_RESULT_NOMATCH;
3175 count = dshow_list_video_devices(infos, sizeof(infos) / sizeof(infos[0]));
3176 for (index = 0; index != count; ++index)
3177 {
3178 if (0 == gate_str_comp(infos[index].uid, id))
3179 {
3180 result = GATE_RESULT_OK;
3181 if (ptr_source)
3182 {
3183 dshow_impl = video_source_dshow_construct(&infos[index]);
3184 if (dshow_impl != NULL)
3185 {
3186 source = (gate_video_source_t*)dshow_impl;
3187 }
3188 else
3189 {
3190 result = GATE_RESULT_FAILED;
3191 }
3192 }
3193 break;
3194 }
3195 }
3196 } while (0);
3197 #endif
3198
3199 } while (0);
3200
3201 if (source != NULL)
3202 {
3203 if (ptr_source != NULL)
3204 {
3205 *ptr_source = source;
3206 }
3207 else
3208 {
3209 gate_object_release(source);
3210 }
3211 result = GATE_RESULT_OK;
3212 }
3213 return result;
3214 }
3215 #endif /* GATE_IO_VIDEO_WINAPI */
3216
3217
3218
3219
3220
3221
3222
3223 #if defined(GATE_IO_VIDEO_V4L)
3224
3225 # include "gate/files.h"
3226 # include "gate/platforms.h"
3227 # include "gate/threading.h"
3228 # include "gate/libraries.h"
3229
3230 # include <sys/mman.h>
3231 # include <sys/types.h>
3232 # include <sys/stat.h>
3233 # include <string.h>
3234 # include <stdlib.h>
3235 # include <errno.h>
3236 # include <fcntl.h>
3237 # include <linux/videodev2.h>
3238
3239 #if defined(HAVE_LIBV4LCONVERT_HEADER)
3240 # include <libv4lconvert.h> // package: libv4l-dev
3241 #endif
3242 # include <libv4l2.h>
3243
3244
3245 struct gate_v4l_convert_code
3246 {
3247 void const* (*get_default_dev_ops) ();
3248 void* (*create) (int fd);
3249 void* (*create_with_dev_ops) (int fd, void* dev_ops_priv, void const* dev_ops);
3250 void (*destroy) (void* data);
3251 int (*supported_dst_fmt_only) (void* data);
3252 int (*try_format) (void* data, struct v4l2_format* dest_fmt, struct v4l2_format* src_fmt);
3253 int (*enum_fmt) (void* data, struct v4l2_fmtdesc* fmt);
3254 int (*needs_conversion) (void* data, const struct v4l2_format* src_fmt, const struct v4l2_format* dest_fmt);
3255 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);
3256 const char* (*get_error_message) (void* data);
3257 int (*enum_framesizes) (void* data, struct v4l2_frmsizeenum* frmsize);
3258 int (*enum_frameintervals) (void* data, struct v4l2_frmivalenum* frmival);
3259 int (*vidioc_queryctrl) (void* data, void* arg);
3260 int (*vidioc_g_ctrl) (void* data, void* arg);
3261 int (*vidioc_s_ctrl) (void* data, void* arg);
3262 int (*supported_dst_format) (unsigned int pixelformat);
3263 int (*get_fps) (void* data);
3264 void (*set_fps) (void* data, int fps);
3265 };
3266
3267 static struct gate_v4l_convert_code v4l_convert = GATE_INIT_EMPTY;
3268
3269 static gate_result_t gate_vfl_convert_init()
3270 {
3271 static gate_library_t convert_lib = NULL;
3272 static gate_bool_t volatile convert_lib_loaded = false;
3273 static gate_string_t const convert_lib_name = GATE_STRING_INIT_STATIC("libv4lconvert.so");
3274
3275 gate_result_t ret = GATE_RESULT_FAILED;
3276
3277 do
3278 {
3279 if (convert_lib_loaded)
3280 {
3281 ret = GATE_RESULT_OK;
3282 break;
3283 }
3284
3285 if (convert_lib == NULL)
3286 {
3287 ret = gate_library_open(&convert_lib_name, &convert_lib, GATE_LIBRARY_FLAG_DEFAULT);
3288 GATE_BREAK_IF_FAILED(ret);
3289 }
3290 ret = gate_library_get_function_name(convert_lib, "v4lconvert_create", &v4l_convert.create);
3291 GATE_BREAK_IF_FAILED(ret);
3292 ret = gate_library_get_function_name(convert_lib, "v4lconvert_destroy", &v4l_convert.destroy);
3293 GATE_BREAK_IF_FAILED(ret);
3294 ret = gate_library_get_function_name(convert_lib, "v4lconvert_supported_dst_fmt_only",
3295 &v4l_convert.supported_dst_fmt_only);
3296 GATE_BREAK_IF_FAILED(ret);
3297 ret = gate_library_get_function_name(convert_lib, "v4lconvert_try_format", &v4l_convert.try_format);
3298 GATE_BREAK_IF_FAILED(ret);
3299 ret = gate_library_get_function_name(convert_lib, "v4lconvert_enum_fmt", &v4l_convert.enum_fmt);
3300 GATE_BREAK_IF_FAILED(ret);
3301 ret = gate_library_get_function_name(convert_lib, "v4lconvert_needs_conversion", &v4l_convert.needs_conversion);
3302 GATE_BREAK_IF_FAILED(ret);
3303 ret = gate_library_get_function_name(convert_lib, "v4lconvert_convert", &v4l_convert.convert);
3304 GATE_BREAK_IF_FAILED(ret);
3305 ret = gate_library_get_function_name(convert_lib, "v4lconvert_get_error_message", &v4l_convert.get_error_message);
3306 GATE_BREAK_IF_FAILED(ret);
3307 ret = gate_library_get_function_name(convert_lib, "v4lconvert_enum_framesizes", &v4l_convert.enum_framesizes);
3308 GATE_BREAK_IF_FAILED(ret);
3309 ret = gate_library_get_function_name(convert_lib, "v4lconvert_enum_frameintervals", &v4l_convert.enum_frameintervals);
3310 GATE_BREAK_IF_FAILED(ret);
3311 ret = gate_library_get_function_name(convert_lib, "v4lconvert_vidioc_queryctrl", &v4l_convert.vidioc_queryctrl);
3312 GATE_BREAK_IF_FAILED(ret);
3313 ret = gate_library_get_function_name(convert_lib, "v4lconvert_vidioc_g_ctrl", &v4l_convert.vidioc_g_ctrl);
3314 GATE_BREAK_IF_FAILED(ret);
3315 ret = gate_library_get_function_name(convert_lib, "v4lconvert_vidioc_s_ctrl", &v4l_convert.vidioc_s_ctrl);
3316 GATE_BREAK_IF_FAILED(ret);
3317 ret = gate_library_get_function_name(convert_lib, "v4lconvert_supported_dst_format", &v4l_convert.supported_dst_format);
3318 GATE_BREAK_IF_FAILED(ret);
3319 ret = gate_library_get_function_name(convert_lib, "v4lconvert_get_fps", &v4l_convert.get_fps);
3320 GATE_BREAK_IF_FAILED(ret);
3321 ret = gate_library_get_function_name(convert_lib, "v4lconvert_set_fps", &v4l_convert.set_fps);
3322 GATE_BREAK_IF_FAILED(ret);
3323
3324 convert_lib_loaded = true;
3325 } while (0);
3326
3327 return ret;
3328 }
3329
3330
3331
3332 static void gate_video_vfl_release(void* self);
3333 static int gate_video_vfl_retain(void* self);
3334 static char const* gate_video_vfl_get_interface_name(void* self);
3335
3336 static char const* gate_video_vfl_get_id(void* self);
3337 static char const* gate_video_vfl_get_name(void* self);
3338 static gate_intptr_t gate_video_vfl_get_handle(void* self);
3339 static gate_size_t gate_video_vfl_get_supported_formats(void* self, gate_video_format_t* format_buffer, gate_size_t format_buffer_count);
3340
3341 static gate_result_t gate_video_vfl_open(void* self, gate_video_format_t const* format);
3342 static gate_result_t gate_video_vfl_close(void* self);
3343
3344 static gate_result_t gate_video_vfl_read(void* self, gate_video_frame_t* frame);
3345
3346 static GATE_INTERFACE_VTBL(gate_video_source) gate_video_vfl_vtbl_impl;
3347 static void gate_init_video_vfl_vtbl_impl()
3348 {
3349 if (!gate_video_vfl_vtbl_impl.get_interface_name)
3350 {
3351 GATE_INTERFACE_VTBL(gate_video_source) const local_vtbl =
3352 {
3353 &gate_video_vfl_get_interface_name,
3354 &gate_video_vfl_release,
3355 &gate_video_vfl_retain,
3356
3357 &gate_video_vfl_read,
3358
3359 &gate_video_vfl_get_id,
3360 &gate_video_vfl_get_name,
3361 &gate_video_vfl_get_handle,
3362 &gate_video_vfl_get_supported_formats,
3363
3364 &gate_video_vfl_open,
3365 &gate_video_vfl_close
3366 };
3367 gate_video_vfl_vtbl_impl = local_vtbl;
3368 }
3369 }
3370
3371 #define GATE_VIDEO_VFL_STATE_OFFLINE 0
3372 #define GATE_VIDEO_VFL_STATE_STARTING 1
3373 #define GATE_VIDEO_VFL_STATE_RUNNING 2
3374 #define GATE_VIDEO_VFL_STATE_STOPPING 3
3375 #define GATE_VIDEO_VFL_STATE_ERROR 4
3376
3377
3378 #define GATE_VIDEO_VFL_TYPE_NONE 0
3379 #define GATE_VIDEO_VFL_TYPE_USERPTR 1
3380 #define GATE_VIDEO_VFL_TYPE_MMAP 2
3381 #define GATE_VIDEO_VFL_TYPE_READ 3
3382
3383 typedef struct
3384 {
3385 void* data;
3386 gate_size_t length;
3387 } gate_video_vfl_buffer_t;
3388
3389 typedef struct
3390 {
3391 GATE_INTERFACE_VTBL(gate_video_source) const* vtbl;
3392
3393 gate_atomic_int_t ref_counter;
3394 char id[256];
3395 char name[256];
3396 gate_intptr_t handle;
3397 gate_video_format_t selected_format;
3398
3399 struct v4l2_format source_format;
3400 struct v4l2_format target_format;
3401 void* converter;
3402
3403 gate_atomic_int_t capture_state;
3404 gate_mutex_t capture_mutex;
3405 gate_thread_t capture_thread;
3406 gate_thread_id_t capture_thread_id;
3407 int capture_type;
3408 gate_size_t capture_frame_size;
3409 gate_video_vfl_buffer_t capture_buffers[4];
3410 gate_size_t capture_buffers_used;
3411 gate_video_vfl_buffer_t capture_current_image;
3412
3413 } gate_video_vfl_impl_t;
3414
3415 static void gate_video_vfl_destruct(gate_video_vfl_impl_t* impl)
3416 {
3417 gate_video_vfl_close(impl);
3418
3419 gate_mutex_destroy(&impl->capture_mutex);
3420 }
3421
3422 static void gate_video_vfl_release(void* self)
3423 {
3424 gate_video_vfl_impl_t* impl = (gate_video_vfl_impl_t*)self;
3425 if (gate_atomic_int_dec(&impl->ref_counter) == 0)
3426 {
3427 gate_video_vfl_destruct(impl);
3428 gate_mem_dealloc(impl);
3429 }
3430 }
3431 static int gate_video_vfl_retain(void* self)
3432 {
3433 gate_video_vfl_impl_t* impl = (gate_video_vfl_impl_t*)self;
3434 return gate_atomic_int_inc(&impl->ref_counter);
3435 }
3436 static char const* gate_video_vfl_get_interface_name(void* self)
3437 {
3438 (void)self;
3439 return GATE_INTERFACE_NAME_VIDEO_SOURCE;
3440 }
3441
3442 static char const* gate_video_vfl_get_id(void* self)
3443 {
3444 gate_video_vfl_impl_t* impl = (gate_video_vfl_impl_t*)self;
3445 return impl->id;
3446 }
3447 static char const* gate_video_vfl_get_name(void* self)
3448 {
3449 gate_video_vfl_impl_t* impl = (gate_video_vfl_impl_t*)self;
3450 return impl->name;
3451 }
3452 static gate_intptr_t gate_video_vfl_get_handle(void* self)
3453 {
3454 gate_video_vfl_impl_t* impl = (gate_video_vfl_impl_t*)self;
3455 return impl->handle;
3456 }
3457 static gate_size_t gate_video_vfl_get_supported_formats(void* self, gate_video_format_t* format_buffer, gate_size_t format_buffer_count)
3458 {
3459 gate_size_t ret = 0;
3460 gate_video_vfl_impl_t* impl = (gate_video_vfl_impl_t*)self;
3461 int fd = -1;
3462 struct v4l2_format fmt;
3463 struct v4l2_fmtdesc descr;
3464 gate_uint32_t index;
3465 int result;
3466
3467 do
3468 {
3469 fd = gate_posix_open(impl->id, O_RDONLY, 0);
3470 if (fd == -1)
3471 {
3472 break;
3473 }
3474 gate_mem_clear(&fmt, sizeof(fmt));
3475 fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
3476 if (-1 == gate_posix_ioctl(fd, VIDIOC_G_FMT, &fmt))
3477 {
3478 break;
3479 }
3480
3481 for (index = 0; format_buffer_count > 0; ++index)
3482 {
3483 descr.index = index;
3484 descr.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
3485 result = gate_posix_ioctl(fd, VIDIOC_ENUM_FMT, &descr);
3486 if (result != 0)
3487 {
3488 break;
3489 }
3490
3491 format_buffer->bits_per_pixel = fmt.fmt.pix.bytesperline / fmt.fmt.pix.width;
3492 format_buffer->frames_per_second = 0;
3493 format_buffer->encoding_id = descr.pixelformat;
3494 format_buffer->compression = descr.flags;
3495 format_buffer->width = fmt.fmt.pix.width;
3496 format_buffer->height = fmt.fmt.pix.height;
3497
3498 --format_buffer_count;
3499 ++format_buffer;
3500 ++ret;
3501 }
3502 } while (0);
3503
3504 if (fd != -1)
3505 {
3506 gate_posix_close(fd);
3507 }
3508
3509 return ret;
3510 }
3511
3512 static gate_result_t gate_video_vfl_buffer_create(gate_video_vfl_buffer_t* buffer, gate_size_t length)
3513 {
3514 buffer->data = gate_mem_alloc(length);
3515 if (buffer->data == NULL)
3516 {
3517 return GATE_RESULT_OUTOFMEMORY;
3518 }
3519 else
3520 {
3521 buffer->length = length;
3522 return GATE_RESULT_OK;
3523 }
3524 }
3525 static void gate_video_vfl_buffer_destroy(gate_video_vfl_buffer_t* buffer)
3526 {
3527 if (buffer->data != NULL)
3528 {
3529 gate_mem_dealloc(buffer->data);
3530 buffer->data = NULL;
3531 }
3532 buffer->length = 0;
3533 }
3534
3535 static void gate_video_vfl_unmap_buffers(gate_video_vfl_buffer_t* buffer, gate_size_t count)
3536 {
3537 while (count-- != 0)
3538 {
3539 munmap(buffer->data, buffer->length);
3540 ++buffer;
3541 }
3542 }
3543
3544 static gate_result_t gate_video_vfl_shutdown_session(gate_video_vfl_impl_t* impl)
3545 {
3546 gate_result_t ret = GATE_RESULT_OK;
3547 enum v4l2_buf_type buf_type;
3548 int fd = impl->handle;
3549
3550 impl->handle = -1;
3551
3552 switch (impl->capture_type)
3553 {
3554 case GATE_VIDEO_VFL_TYPE_USERPTR:
3555 {
3556 /* disable streaming */
3557 buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
3558 if (-1 == gate_posix_ioctl(fd, VIDIOC_STREAMOFF, &buf_type))
3559 {
3560 /* failed, but we can't do anything here ... */
3561 GATE_DEBUG_TRACE("gate_posix_ioctl(VIDIOC_STREAMOFF) failed");
3562 ret = GATE_RESULT_FAILED;
3563 }
3564 /* memory can be reused, it is managed by the lifetime of this VflCaptureDriver instance */
3565 break;
3566 }
3567 case GATE_VIDEO_VFL_TYPE_MMAP:
3568 {
3569 /* disable streaming */
3570 buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
3571 if (-1 == gate_posix_ioctl(fd, VIDIOC_STREAMOFF, &buf_type))
3572 {
3573 /* failed, but we can't do anything here ... */
3574 ret = GATE_RESULT_FAILED;
3575 }
3576 /* deallocate memory */
3577 gate_video_vfl_unmap_buffers(impl->capture_buffers, impl->capture_buffers_used);
3578 gate_mem_clear(impl->capture_buffers, sizeof(impl->capture_buffers));
3579 impl->capture_buffers_used = 0;
3580 break;
3581 }
3582 case GATE_VIDEO_VFL_TYPE_READ:
3583 {
3584 /* nothing to do */
3585 break;
3586 }
3587 }
3588
3589 gate_posix_close(fd);
3590 return ret;
3591 }
3592
3593 static gate_result_t gate_video_vfl_thread(void* ptr)
3594 {
3595 gate_result_t ret = GATE_RESULT_OK;
3596 gate_video_vfl_impl_t* impl = (gate_video_vfl_impl_t*)ptr;
3597
3598 int fd = (int)impl->handle;
3599 int capture_type = impl->capture_type;
3600 gate_size_t image_size = impl->source_format.fmt.pix.sizeimage;
3601 struct v4l2_buffer buf;
3602
3603 struct timeval tv;
3604 fd_set fds;
3605 int result;
3606 gate_result_t res;
3607 gate_video_vfl_buffer_t const* current_buffer;
3608 ssize_t bytes_read;
3609
3610 do
3611 {
3612 ret = gate_video_vfl_buffer_create(&impl->capture_current_image, image_size);
3613 GATE_BREAK_IF_FAILED(ret);
3614
3615 if (GATE_VIDEO_VFL_STATE_STARTING != gate_atomic_int_xchg_if(&impl->capture_state,
3616 GATE_VIDEO_VFL_STATE_STARTING, GATE_VIDEO_VFL_STATE_RUNNING))
3617 {
3618 break;
3619 }
3620
3621 /* capture stage */
3622 while (gate_atomic_int_get(&impl->capture_state) == GATE_VIDEO_VFL_STATE_RUNNING)
3623 {
3624 FD_ZERO(&fds);
3625 FD_SET(fd, &fds);
3626
3627 tv.tv_sec = 0;
3628 tv.tv_usec = 250000;
3629
3630 result = select(fd + 1, &fds, NULL, NULL, &tv);
3631 if (-1 == result)
3632 {
3633 gate_atomic_int_set(&impl->capture_state, GATE_VIDEO_VFL_STATE_ERROR);
3634 /* critical error */
3635 break;
3636 }
3637
3638 if (0 == result)
3639 {
3640 /* no frame or data retrieved */
3641 continue;
3642 }
3643
3644 switch (capture_type)
3645 {
3646 case GATE_VIDEO_VFL_TYPE_USERPTR:
3647 {
3648 gate_mem_clear(&buf, sizeof(buf));
3649 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
3650 buf.memory = V4L2_MEMORY_USERPTR;
3651 if (0 == gate_posix_ioctl(fd, VIDIOC_DQBUF, &buf))
3652 {
3653 /*this->processImageData((char*)buf.m.userptr, buf.bytesused, &buf);*/
3654 res = gate_mutex_acquire(&impl->capture_mutex);
3655 if (GATE_SUCCEEDED(res))
3656 {
3657 gate_mem_copy(impl->capture_current_image.data, (char const*)buf.m.userptr,
3658 impl->capture_frame_size);
3659 gate_mutex_release(&impl->capture_mutex);
3660 }
3661 gate_posix_ioctl(fd, VIDIOC_QBUF, &buf);
3662 }
3663 break;
3664 }
3665 case GATE_VIDEO_VFL_TYPE_MMAP:
3666 {
3667 gate_mem_clear(&buf, sizeof(buf));
3668 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
3669 buf.memory = V4L2_MEMORY_MMAP;
3670 if (0 == gate_posix_ioctl(fd, VIDIOC_DQBUF, &buf))
3671 {
3672 if (buf.index < sizeof(impl->capture_buffers) / sizeof(impl->capture_buffers[0]))
3673 {
3674 current_buffer = &impl->capture_buffers[buf.index];
3675 /*this->processImageData((char*)info.Data, info.Length, NULL);*/
3676 res = gate_mutex_acquire(&impl->capture_mutex);
3677 if (GATE_SUCCEEDED(res))
3678 {
3679 gate_mem_copy(impl->capture_current_image.data, current_buffer->data, impl->capture_frame_size);
3680 gate_mutex_release(&impl->capture_mutex);
3681 }
3682 }
3683 gate_posix_ioctl(fd, VIDIOC_QBUF, &buf);
3684 }
3685 break;
3686 }
3687 case GATE_VIDEO_VFL_TYPE_READ:
3688 {
3689 res = gate_mutex_acquire(&impl->capture_mutex);
3690 if (GATE_SUCCEEDED(res))
3691 {
3692 bytes_read = gate_posix_read(fd, impl->capture_current_image.data, impl->capture_frame_size);
3693 if (bytes_read < 0)
3694 {
3695 /* read errors are ignored */
3696 }
3697 else
3698 {
3699 /* data directly copied to image buffer */
3700 }
3701 gate_mutex_release(&impl->capture_mutex);
3702 }
3703 break;
3704 }
3705 }
3706
3707 } /* while(running) */
3708
3709 /* shutdown stage */
3710 gate_video_vfl_shutdown_session(impl);
3711
3712 if (impl->converter && (v4l_convert.destroy != NULL))
3713 {
3714 v4l_convert.destroy(impl->converter);
3715 impl->converter = NULL;
3716 }
3717
3718 } while (0);
3719
3720 gate_video_vfl_buffer_destroy(&impl->capture_current_image);
3721
3722 return ret;
3723 }
3724
3725 static gate_result_t gate_video_vfl_open(void* self, gate_video_format_t const* format)
3726 {
3727 gate_result_t ret = GATE_RESULT_FAILED;
3728 gate_video_vfl_impl_t* impl = (gate_video_vfl_impl_t*)self;
3729 int fd = -1;
3730 struct v4l2_capability cap;
3731 struct v4l2_requestbuffers req;
3732 struct v4l2_format fmt;
3733 struct v4l2_buffer buf;
3734 gate_uint32_t index;
3735 enum v4l2_buf_type buf_type;
3736
3737 do
3738 {
3739 if (GATE_VIDEO_VFL_STATE_OFFLINE != gate_atomic_int_xchg_if(&impl->capture_state, GATE_VIDEO_VFL_STATE_OFFLINE, GATE_VIDEO_VFL_STATE_STARTING))
3740 {
3741 return GATE_RESULT_INVALIDSTATE;
3742 }
3743
3744
3745 fd = gate_posix_open(impl->id, O_RDWR | O_NONBLOCK, 0);
3746 if (fd == -1)
3747 {
3748 ret = GATE_RESULT_NOTAVAILABLE;
3749 break;
3750 }
3751
3752 if (-1 == gate_posix_ioctl(fd, VIDIOC_QUERYCAP, &cap))
3753 {
3754 ret = GATE_RESULT_NOTREADY;
3755 break;
3756 }
3757
3758 if ((cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) == 0)
3759 {
3760 ret = GATE_RESULT_NOTSUPPORTED;
3761 break;
3762 }
3763
3764 gate_mem_clear(&fmt, sizeof(fmt));
3765 fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
3766 if (-1 == gate_posix_ioctl(fd, VIDIOC_G_FMT, &fmt))
3767 {
3768 ret = GATE_RESULT_INVALIDHEADER;
3769 break;
3770 }
3771
3772 ret = gate_vfl_convert_init();
3773 if (GATE_SUCCEEDED(ret))
3774 {
3775 do
3776 {
3777 impl->converter = v4l_convert.create(fd);
3778 if (impl->converter == NULL)
3779 {
3780 break;
3781 }
3782 gate_mem_copy(&impl->target_format, &fmt, sizeof(fmt));
3783 impl->target_format.fmt.pix.pixelformat = V4L2_PIX_FMT_RGB24;
3784
3785 if (v4l_convert.try_format != NULL)
3786 {
3787 v4l_convert.try_format(impl->converter, &impl->target_format, &fmt);
3788 }
3789
3790 } while (0);
3791 }
3792
3793 gate_mem_copy(&impl->source_format, &fmt, sizeof(fmt));
3794 if (-1 == gate_posix_ioctl(fd, VIDIOC_G_FMT, &impl->source_format))
3795 {
3796 ret = GATE_RESULT_FAILED;
3797 break;
3798 }
3799
3800 impl->capture_frame_size = impl->source_format.fmt.pix.sizeimage;
3801
3802 /* find best access method */
3803 do
3804 {
3805 impl->capture_type = GATE_VIDEO_VFL_TYPE_NONE;
3806
3807 if ((cap.capabilities & V4L2_CAP_STREAMING) == V4L2_CAP_STREAMING)
3808 {
3809 /* video source supports streaming into memory */
3810 gate_mem_clear(&req, sizeof(req));
3811 req.count = sizeof(impl->capture_buffers) / sizeof(impl->capture_buffers[0]);
3812 req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
3813 req.memory = V4L2_MEMORY_MMAP;
3814
3815 if (0 == gate_posix_ioctl(fd, VIDIOC_REQBUFS, &req))
3816 {
3817 /* the driver supports streaming to mmap-blocks */
3818 if (req.count >= 2)
3819 {
3820 for (index = 0; index < req.count; ++index)
3821 {
3822 gate_mem_clear(&buf, sizeof(buf));
3823 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
3824 buf.memory = V4L2_MEMORY_MMAP;
3825 buf.index = index;
3826 if (-1 == gate_posix_ioctl(fd, VIDIOC_QUERYBUF, &buf))
3827 {
3828 break;
3829 }
3830
3831 impl->capture_buffers[index].length = buf.length;
3832 impl->capture_buffers[index].data = mmap(NULL, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, buf.m.offset);
3833
3834 if (MAP_FAILED == impl->capture_buffers[index].data)
3835 {
3836 break;
3837 }
3838
3839 if (-1 == gate_posix_ioctl(fd, VIDIOC_QBUF, &buf))
3840 {
3841 break;
3842 }
3843 }
3844
3845 if (index < req.count)
3846 {
3847 /* error */
3848 if (index > 0)
3849 {
3850 gate_video_vfl_unmap_buffers(impl->capture_buffers, index - 1);
3851 }
3852 break;
3853 }
3854 impl->capture_buffers_used = req.count;
3855
3856 buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
3857 if (-1 == gate_posix_ioctl(fd, VIDIOC_STREAMON, &buf_type))
3858 {
3859 gate_video_vfl_unmap_buffers(impl->capture_buffers, index - 1);
3860 break;
3861 }
3862 }
3863
3864 impl->capture_type = GATE_VIDEO_VFL_TYPE_MMAP;
3865 break; /* type detected */
3866 }
3867
3868 /* try userptr: */
3869 do
3870 {
3871 gate_mem_clear(impl->capture_buffers, sizeof(impl->capture_buffers));
3872 gate_mem_clear(&req, sizeof(req));
3873 req.count = 4;
3874 req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
3875 req.memory = V4L2_MEMORY_USERPTR;
3876
3877 if (0 != gate_posix_ioctl(fd, VIDIOC_REQBUFS, &req))
3878 {
3879 /* the driver does NOT supports streaming to process-allocated memory */
3880 break;
3881 }
3882
3883 for (index = 0; index < req.count; ++index)
3884 {
3885 impl->capture_buffers[index].data = gate_mem_alloc(impl->capture_frame_size);
3886 if (impl->capture_buffers[index].data == NULL)
3887 {
3888 break;
3889 }
3890 impl->capture_buffers[index].length = impl->capture_frame_size;
3891
3892 gate_mem_clear(&buf, sizeof(buf));
3893 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
3894 buf.memory = V4L2_MEMORY_USERPTR;
3895 buf.index = index;
3896 buf.m.userptr = (unsigned long)(gate_uintptr_t)impl->capture_buffers[index].data;
3897 buf.length = impl->capture_frame_size;
3898
3899 if (-1 == gate_posix_ioctl(fd, VIDIOC_QBUF, &buf))
3900 {
3901 break;
3902 }
3903 }
3904
3905 if (index < req.count)
3906 {
3907 /* error detected */
3908 for (index = 0; index < req.count; ++index)
3909 {
3910 if (impl->capture_buffers[index].data != NULL)
3911 {
3912 gate_mem_dealloc(impl->capture_buffers[index].data);
3913 }
3914 }
3915 break;
3916 }
3917 impl->capture_buffers_used = req.count;
3918 buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
3919 if (-1 == gate_posix_ioctl(fd, VIDIOC_STREAMON, &buf_type))
3920 {
3921 break;
3922 }
3923
3924 impl->capture_type = GATE_VIDEO_VFL_TYPE_USERPTR;
3925 } while (0);
3926
3927 if (impl->capture_type == GATE_VIDEO_VFL_TYPE_USERPTR)
3928 {
3929 break;
3930 }
3931 }
3932
3933 if ((cap.capabilities & V4L2_CAP_READWRITE) == V4L2_CAP_READWRITE)
3934 {
3935 // the capture device does support read() access
3936 impl->capture_type = GATE_VIDEO_VFL_TYPE_READ;
3937 break;
3938 }
3939
3940 /* no access type detected */
3941
3942 } while (0);
3943
3944 if (impl->capture_type == GATE_VIDEO_VFL_TYPE_NONE)
3945 {
3946 ret = GATE_RESULT_NOTSUPPORTED;
3947 break;
3948 }
3949
3950 impl->handle = fd;
3951 fd = -1;
3952 ret = gate_thread_start_code(&gate_video_vfl_thread, impl, &impl->capture_thread, &impl->capture_thread_id);
3953 if (GATE_FAILED(ret))
3954 {
3955 gate_video_vfl_shutdown_session(impl);
3956 }
3957
3958 } while (0);
3959
3960 if (fd != -1)
3961 {
3962 gate_posix_close(fd);
3963 }
3964
3965 if (GATE_FAILED(ret))
3966 {
3967 gate_atomic_int_set(&impl->capture_state, GATE_VIDEO_VFL_STATE_OFFLINE);
3968 }
3969
3970 return ret;
3971 }
3972 static gate_result_t gate_video_vfl_close(void* self)
3973 {
3974 gate_result_t ret = GATE_RESULT_FAILED;
3975 gate_video_vfl_impl_t* impl = (gate_video_vfl_impl_t*)self;
3976 gate_result_t thread_result;
3977
3978 if (GATE_VIDEO_VFL_STATE_RUNNING == gate_atomic_int_xchg_if(&impl->capture_state,
3979 GATE_VIDEO_VFL_STATE_RUNNING, GATE_VIDEO_VFL_STATE_STOPPING))
3980 {
3981 ret = gate_thread_join(&impl->capture_thread, &thread_result);
3982 }
3983 else
3984 {
3985 ret = GATE_RESULT_INVALIDSTATE;
3986 }
3987
3988 return ret;
3989 }
3990
3991 static gate_result_t gate_video_vfl_read(void* self, gate_video_frame_t* frame)
3992 {
3993 gate_result_t ret = GATE_RESULT_NOTIMPLEMENTED;
3994 gate_video_vfl_impl_t* impl = (gate_video_vfl_impl_t*)self;
3995 void* frame_data = NULL;
3996 gate_result_t res = gate_mutex_acquire(&impl->capture_mutex);
3997 struct v4l2_format fmt;
3998 gate_size_t frame_data_size;
3999 int result = -1;
4000 gate_color_t* ptr_frame_pixels;
4001
4002 if (GATE_SUCCEEDED(res))
4003 {
4004 do
4005 {
4006 if (frame == NULL)
4007 {
4008 ret = GATE_RESULT_NULLPOINTER;
4009 break;
4010 }
4011 frame_data_size = impl->target_format.fmt.pix.sizeimage;
4012 if (frame_data_size < impl->source_format.fmt.pix.sizeimage)
4013 {
4014 frame_data_size = impl->source_format.fmt.pix.sizeimage;
4015 }
4016
4017 frame_data = gate_mem_alloc(frame_data_size);
4018 if (frame_data == NULL)
4019 {
4020 ret = GATE_RESULT_OUTOFMEMORY;
4021 break;
4022 }
4023
4024 if (impl->converter && (v4l_convert.convert != NULL))
4025 {
4026 result = v4l_convert.convert(impl->converter, &impl->source_format, &impl->target_format,
4027 impl->capture_current_image.data, impl->capture_current_image.length,
4028 frame_data, impl->target_format.fmt.pix.sizeimage);
4029 if (result >= 0)
4030 {
4031 gate_mem_copy(&fmt, &impl->target_format, sizeof(fmt));
4032 break;
4033 }
4034 }
4035 gate_mem_copy(&fmt, &impl->source_format, sizeof(fmt));
4036 gate_mem_copy(frame_data, impl->capture_current_image.data, impl->capture_current_image.length);
4037 } while (0);
4038 gate_mutex_release(&impl->capture_mutex);
4039 }
4040
4041 if (frame_data != NULL)
4042 {
4043 do
4044 {
4045 frame->format.width = fmt.fmt.pix.width;
4046 frame->format.height = fmt.fmt.pix.height;
4047 frame->format.bits_per_pixel = fmt.fmt.pix.bytesperline * 8 / fmt.fmt.pix.width;
4048 frame->format.compression = 0;
4049 frame->format.encoding_id = 0;
4050 frame->format.frames_per_second = 0;
4051
4052 if (NULL == gate_rasterimage_create(&frame->image, GATE_IMAGE_PIXELFORMAT_RGBA,
4053 frame->format.width, frame->format.height, NULL))
4054 {
4055 ret = GATE_RESULT_OUTOFMEMORY;
4056 break;
4057 }
4058
4059 ptr_frame_pixels = (gate_color_t*)gate_rasterimage_get_line_ptr(&frame->image, 0);
4060
4061 switch (fmt.fmt.pix.pixelformat)
4062 {
4063 case V4L2_PIX_FMT_RGB24:
4064 {
4065 gate_color_load_rgb_24(ptr_frame_pixels, frame->format.width * frame->format.height, frame_data);
4066 ret = GATE_RESULT_OK;
4067 break;
4068 }
4069 case V4L2_PIX_FMT_RGB32:
4070 {
4071 gate_color_load_rgb_32(ptr_frame_pixels, frame->format.width * frame->format.height, frame_data);
4072 ret = GATE_RESULT_OK;
4073 break;
4074 }
4075 case V4L2_PIX_FMT_SPCA501:
4076 {
4077 gate_color_load_yuv2_16(ptr_frame_pixels, frame->format.width * frame->format.height, frame_data);
4078 ret = GATE_RESULT_OK;
4079 break;
4080 }
4081 default:
4082 {
4083 ret = GATE_RESULT_NOTSUPPORTED;
4084 break;
4085 }
4086 }
4087
4088 } while (0);
4089 }
4090
4091 if (frame_data != NULL)
4092 {
4093 gate_mem_dealloc(frame_data);
4094 }
4095
4096 return ret;
4097 }
4098
4099
4100 struct gate_v4l_enum_param
4101 {
4102 gate_delegate_t const* callback;
4103 void* param;
4104 };
4105
4106 static gate_video_vfl_impl_t* gate_v4l_create_device(char const* path)
4107 {
4108 gate_video_vfl_impl_t* impl = NULL;
4109 int fd = -1;
4110 struct v4l2_capability cap;
4111 int result;
4112 gate_result_t res;
4113
4114 do
4115 {
4116 fd = gate_posix_open(path, O_RDONLY /*O_RDWR*/, 0);
4117 if (fd == -1)
4118 {
4119 break;
4120 }
4121
4122 gate_mem_clear(&cap, sizeof(cap));
4123
4124 result = gate_posix_ioctl(fd, VIDIOC_QUERYCAP, &cap);
4125 if (result != 0)
4126 {
4127 break;
4128 }
4129
4130 impl = (gate_video_vfl_impl_t*)gate_mem_alloc(sizeof(gate_video_vfl_impl_t));
4131 if (impl == NULL)
4132 {
4133 break;
4134 }
4135
4136 gate_mem_clear(impl, sizeof(gate_video_vfl_impl_t));
4137 gate_init_video_vfl_vtbl_impl();
4138 impl->vtbl = &gate_video_vfl_vtbl_impl;
4139 gate_atomic_int_init(&impl->ref_counter, 1);
4140 gate_str_print_text(impl->id, sizeof(impl->id), path, gate_str_length(path));
4141 gate_str_print_text(impl->name, sizeof(impl->name),
4142 (char const*)&cap.card[0],
4143 gate_str_length_max((char const*)&cap.card[0], sizeof(cap.card)));
4144 impl->handle = -1;
4145
4146 res = gate_mutex_create(&impl->capture_mutex);
4147 if (GATE_FAILED(res))
4148 {
4149 gate_mem_dealloc(impl);
4150 impl = NULL;
4151 }
4152 } while (0);
4153
4154 if (fd != -1)
4155 {
4156 gate_posix_close(fd);
4157 }
4158
4159 return impl;
4160 }
4161
4162 static gate_bool_t gate_v4l_enum_callback(gate_file_entry_t const* entry, void* userparam)
4163 {
4164 struct gate_v4l_enum_param* param = (struct gate_v4l_enum_param*)userparam;
4165 gate_video_vfl_impl_t* impl = NULL;
4166
4167 do
4168 {
4169 if (!gate_str_starts_with(entry->name, gate_str_length(entry->name), "video", 5))
4170 {
4171 break;
4172 }
4173
4174 impl = gate_v4l_create_device(entry->path);
4175 if (impl != NULL)
4176 {
4177 gate_delegate_invoke(param->callback, impl, param->param);
4178 gate_object_release(impl);
4179 }
4180 } while (0);
4181
4182 return true;
4183 }
4184
4185 gate_result_t gate_video_sources_enum(gate_delegate_t const* callback, void* param)
4186 {
4187 static gate_string_t dev_directory = { "/dev/", 5, NULL };
4188 gate_result_t result;
4189 struct gate_v4l_enum_param cb_param = { callback, param };
4190 result = gate_file_list(&dev_directory, &gate_v4l_enum_callback, &cb_param);
4191 return result;
4192 }
4193
4194 gate_result_t gate_video_source_by_id(char const* id, gate_video_source_t** ptr_source)
4195 {
4196 gate_result_t result = GATE_RESULT_NOTIMPLEMENTED;
4197
4198 gate_video_vfl_impl_t* impl = gate_v4l_create_device(id);
4199 if (impl == NULL)
4200 {
4201 result = GATE_RESULT_NOTAVAILABLE;
4202 }
4203 else
4204 {
4205 if (ptr_source != NULL)
4206 {
4207 *ptr_source = (gate_video_source_t*)impl;
4208 }
4209 else
4210 {
4211 gate_object_release(impl);
4212 }
4213 result = GATE_RESULT_OK;
4214 }
4215 return result;
4216 }
4217
4218
4219 #endif /* GATE_IO_VIDEO_V4L */
4220
4221
4222
4223 #if defined(GATE_IO_VIDEO_ANDROID)
4224
4225 /*
4226 #include <android/storage_manager.h>
4227 #include <jni.h>
4228 #include <android/configuration.h>
4229 */
4230
4231 gate_result_t gate_video_sources_enum(gate_delegate_t const* callback, void* param)
4232 {
4233 (void)callback;
4234 (void)param;
4235 return GATE_RESULT_NOTIMPLEMENTED;
4236 }
4237
4238 gate_result_t gate_video_source_by_id(char const* id, gate_video_source_t** ptr_source)
4239 {
4240 (void)id;
4241 (void)ptr_source;
4242 return GATE_RESULT_NOTIMPLEMENTED;
4243 }
4244
4245 #endif /* GATE_IO_VIDEO_ANDROID */
4246
4247
4248
4249 #if defined(GATE_IO_VIDEO_NO_IMPL)
4250
4251 ✗ gate_result_t gate_video_sources_enum(gate_delegate_t const* callback, void* param)
4252 {
4253 (void)callback;
4254 (void)param;
4255 ✗ return GATE_RESULT_NOTIMPLEMENTED;
4256 }
4257
4258 ✗ gate_result_t gate_video_source_by_id(char const* id, gate_video_source_t** ptr_source)
4259 {
4260 (void)id;
4261 (void)ptr_source;
4262 ✗ return GATE_RESULT_NOTIMPLEMENTED;
4263 }
4264
4265 #endif /* GATE_IO_VIDEO_NO_IMPL */
4266
4267