GCC Code Coverage Report


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