GCC Code Coverage Report


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