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(¶ms, sizeof(params)); |
682 |
|
|
|
683 |
|
|
success = gate_capCaptureGetSetup(impl->hwnd, ¶ms, 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, ¶ms, 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, ¶ms, 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, ¶m); |
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, ¤t_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, ¬_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], ¶ms->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, ¶ms, &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(¶ms->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 |
|
|
¶ms->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, ¶ms, &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, ¶ms, &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 |
|
|
|