Commit | Line | Data |
---|---|---|
b58f0a3f ML |
1 | /* |
2 | * DirectShow capture services (QCAP.DLL) | |
3 | * | |
4 | * Copyright 2005 Maarten Lankhorst | |
5 | * | |
6 | * This file contains the part of the vfw capture interface that | |
7 | * does the actual Video4Linux(1/2) stuff required for capturing | |
8 | * and setting/getting media format.. | |
9 | * | |
10 | * This library is free software; you can redistribute it and/or | |
11 | * modify it under the terms of the GNU Lesser General Public | |
12 | * License as published by the Free Software Foundation; either | |
13 | * version 2.1 of the License, or (at your option) any later version. | |
14 | * | |
15 | * This library is distributed in the hope that it will be useful, | |
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
18 | * Lesser General Public License for more details. | |
19 | * | |
20 | * You should have received a copy of the GNU Lesser General Public | |
21 | * License along with this library; if not, write to the Free Software | |
360a3f91 | 22 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA |
b58f0a3f ML |
23 | */ |
24 | ||
25 | #include "config.h" | |
49b7fdcf | 26 | #include "wine/port.h" |
b58f0a3f ML |
27 | |
28 | #define NONAMELESSSTRUCT | |
29 | #define NONAMELESSUNION | |
30 | #define COBJMACROS | |
31 | ||
32 | #include <stdarg.h> | |
33 | #include "windef.h" | |
34 | #include "winbase.h" | |
35 | #include "wtypes.h" | |
36 | #include "wingdi.h" | |
37 | #include "winuser.h" | |
38 | #include "dshow.h" | |
39 | #include "vfwmsgs.h" | |
40 | #include "amvideo.h" | |
41 | #include "wine/debug.h" | |
42 | ||
43 | #include "capture.h" | |
44 | #include "qcap_main.h" | |
45 | #include "pin.h" | |
46 | ||
47 | #include <stdio.h> | |
48 | #include <fcntl.h> | |
49 | ||
50 | #ifdef HAVE_SYS_IOCTL_H | |
51 | #include <sys/ioctl.h> | |
52 | #endif | |
53 | #ifdef HAVE_SYS_MMAN_H | |
54 | #include <sys/mman.h> | |
55 | #endif | |
56 | #ifdef HAVE_SYS_ERRNO_H | |
57 | #include <sys/errno.h> | |
58 | #endif | |
59 | #ifdef HAVE_SYS_TIME_H | |
60 | #include <sys/time.h> | |
61 | #endif | |
62 | #ifdef HAVE_ASM_TYPES_H | |
63 | #include <asm/types.h> | |
64 | #endif | |
65 | #ifdef HAVE_LINUX_VIDEODEV_H | |
66 | #include <linux/videodev.h> | |
67 | #endif | |
68 | #ifdef HAVE_UNISTD_H | |
69 | #include <unistd.h> | |
70 | #endif | |
71 | ||
72 | WINE_DEFAULT_DEBUG_CHANNEL(qcap_v4l); | |
73 | ||
74 | #ifdef HAVE_LINUX_VIDEODEV_H | |
75 | ||
48dcc3fc | 76 | typedef void (* Renderer)(const Capture *, LPBYTE bufferin, const BYTE *stream); |
b58f0a3f ML |
77 | |
78 | struct _Capture | |
79 | { | |
80 | UINT width, height, bitDepth, fps, outputwidth, outputheight; | |
81 | BOOL swresize; | |
82 | ||
83 | CRITICAL_SECTION CritSect; | |
84 | ||
85 | IPin *pOut; | |
86 | int fd, mmap; | |
87 | int iscommitted, stopped; | |
88 | struct video_picture pict; | |
89 | int dbrightness, dhue, dcolour, dcontrast; | |
90 | ||
91 | /* mmap (V4l1) */ | |
92 | struct video_mmap *grab_buf; | |
93 | struct video_mbuf gb_buffers; | |
94 | unsigned char *pmap; | |
95 | int buffers; | |
96 | ||
97 | /* read (V4l1) */ | |
98 | int imagesize; | |
99 | char * grab_data; | |
100 | ||
101 | int curframe; | |
102 | ||
103 | HANDLE thread; | |
104 | Renderer renderer; | |
105 | }; | |
106 | ||
107 | struct renderlist | |
108 | { | |
109 | int depth; | |
110 | const char* name; | |
111 | Renderer renderer; | |
112 | }; | |
113 | ||
48dcc3fc AT |
114 | static void renderer_RGB(const Capture *capBox, LPBYTE bufferin, const BYTE *stream); |
115 | static void renderer_YUV(const Capture *capBox, LPBYTE bufferin, const BYTE *stream); | |
b58f0a3f ML |
116 | |
117 | static const struct renderlist renderlist_V4l[] = { | |
118 | { 0, "NULL renderer", NULL }, | |
119 | { 8, "Gray scales", NULL }, /* 1, Don't support */ | |
120 | { 0, "High 240 cube (BT848)", NULL }, /* 2, Don't support */ | |
121 | { 16, "16 bit RGB (565)", NULL }, /* 3, Don't support */ | |
122 | { 24, "24 bit RGB values", renderer_RGB }, /* 4, Supported, */ | |
123 | { 32, "32 bit RGB values", renderer_RGB }, /* 5, Supported */ | |
124 | { 16, "15 bit RGB (555)", NULL }, /* 6, Don't support */ | |
125 | { 16, "YUV 422 (Not P)", renderer_YUV }, /* 7, Supported */ | |
126 | { 16, "YUYV (Not P)", renderer_YUV }, /* 8, Supported */ | |
127 | { 16, "UYVY (Not P)", renderer_YUV }, /* 9, Supported */ | |
128 | { 16, "YUV 420 (Not P)", NULL }, /* 10, Not supported, if I had to guess it's YYUYYV */ | |
129 | { 12, "YUV 411 (Not P)", renderer_YUV }, /* 11, Supported */ | |
130 | { 0, "Raw capturing (BT848)", NULL }, /* 12, Don't support */ | |
131 | { 16, "YUV 422 (Planar)", renderer_YUV }, /* 13, Supported */ | |
132 | { 12, "YUV 411 (Planar)", renderer_YUV }, /* 14, Supported */ | |
133 | { 12, "YUV 420 (Planar)", renderer_YUV }, /* 15, Supported */ | |
134 | { 10, "YUV 410 (Planar)", renderer_YUV }, /* 16, Supported */ | |
135 | /* FIXME: add YUV420 support */ | |
136 | { 0, NULL, NULL }, | |
137 | }; | |
138 | ||
97a22034 | 139 | static const int fallback_V4l[] = { 4, 5, 7, 8, 9, 13, 15, 14, 16, 11, -1 }; |
b58f0a3f ML |
140 | /* Fallback: First try raw formats (Should try yuv first perhaps?), then yuv */ |
141 | ||
142 | /* static const Capture defbox; */ | |
143 | ||
144 | static int xioctl(int fd, int request, void * arg) | |
145 | { | |
146 | int r; | |
147 | ||
148 | do { | |
149 | r = ioctl (fd, request, arg); | |
150 | } while (-1 == r && EINTR == errno); | |
151 | ||
152 | return r; | |
153 | } | |
154 | ||
155 | /* Prepare the capture buffers */ | |
156 | static HRESULT V4l_Prepare(Capture *capBox) | |
157 | { | |
158 | TRACE("%p: Preparing for %dx%d resolution\n", capBox, capBox->width, capBox->height); | |
159 | ||
160 | /* Try mmap */ | |
161 | capBox->mmap = 0; | |
162 | if (xioctl(capBox->fd, VIDIOCGMBUF, &capBox->gb_buffers) != -1 && | |
163 | capBox->gb_buffers.frames) | |
164 | { | |
165 | capBox->buffers = capBox->gb_buffers.frames; | |
166 | if (capBox->gb_buffers.frames > 1) | |
167 | capBox->buffers = 1; | |
168 | TRACE("%p: Using %d/%d buffers\n", capBox, | |
169 | capBox->buffers, capBox->gb_buffers.frames); | |
170 | ||
171 | capBox->pmap = mmap( 0, capBox->gb_buffers.size, PROT_READ|PROT_WRITE, | |
172 | MAP_SHARED, capBox->fd, 0 ); | |
173 | if (capBox->pmap != MAP_FAILED) | |
174 | { | |
175 | int i; | |
176 | ||
177 | capBox->grab_buf = CoTaskMemAlloc(sizeof(struct video_mmap) * capBox->buffers); | |
178 | if (!capBox->grab_buf) | |
179 | { | |
180 | munmap(capBox->pmap, capBox->gb_buffers.size); | |
181 | return E_OUTOFMEMORY; | |
182 | } | |
183 | ||
184 | /* Setup mmap capture buffers. */ | |
185 | for (i = 0; i < capBox->buffers; i++) | |
186 | { | |
187 | capBox->grab_buf[i].format = capBox->pict.palette; | |
188 | capBox->grab_buf[i].frame = i; | |
189 | capBox->grab_buf[i].width = capBox->width; | |
190 | capBox->grab_buf[i].height = capBox->height; | |
191 | } | |
192 | capBox->mmap = 1; | |
193 | } | |
194 | } | |
195 | if (!capBox->mmap) | |
196 | { | |
197 | capBox->buffers = 1; | |
198 | capBox->imagesize = renderlist_V4l[capBox->pict.palette].depth * | |
199 | capBox->height * capBox->width / 8; | |
200 | capBox->grab_data = CoTaskMemAlloc(capBox->imagesize); | |
201 | if (!capBox->grab_data) | |
202 | return E_OUTOFMEMORY; | |
203 | } | |
204 | TRACE("Using mmap: %d\n", capBox->mmap); | |
205 | return S_OK; | |
206 | } | |
207 | ||
208 | static void V4l_Unprepare(Capture *capBox) | |
209 | { | |
210 | if (capBox->mmap) | |
211 | { | |
212 | for (capBox->curframe = 0; capBox->curframe < capBox->buffers; capBox->curframe++) | |
213 | xioctl(capBox->fd, VIDIOCSYNC, &capBox->grab_buf[capBox->curframe]); | |
214 | munmap(capBox->pmap, capBox->gb_buffers.size); | |
215 | CoTaskMemFree(capBox->grab_buf); | |
216 | } | |
217 | else | |
218 | CoTaskMemFree(capBox->grab_data); | |
219 | } | |
220 | ||
221 | HRESULT qcap_driver_destroy(Capture *capBox) | |
222 | { | |
223 | TRACE("%p\n", capBox); | |
224 | ||
225 | if( capBox->fd != -1 ) | |
226 | close(capBox->fd); | |
614ee831 | 227 | capBox->CritSect.DebugInfo->Spare[0] = 0; |
b58f0a3f ML |
228 | DeleteCriticalSection(&capBox->CritSect); |
229 | CoTaskMemFree(capBox); | |
230 | return S_OK; | |
231 | } | |
232 | ||
233 | HRESULT qcap_driver_set_format(Capture *capBox, AM_MEDIA_TYPE * mT) | |
234 | { | |
235 | int newheight, newwidth; | |
236 | struct video_window window; | |
237 | VIDEOINFOHEADER *format; | |
238 | ||
239 | TRACE("%p\n", capBox); | |
240 | ||
241 | format = (VIDEOINFOHEADER *) mT->pbFormat; | |
242 | if (format->bmiHeader.biBitCount != 24 || | |
243 | format->bmiHeader.biCompression != BI_RGB) | |
244 | { | |
079bb8a5 | 245 | FIXME("unsupported media type %d %d\n", format->bmiHeader.biBitCount, |
b58f0a3f ML |
246 | format->bmiHeader.biCompression ); |
247 | return VFW_E_INVALIDMEDIATYPE; | |
248 | } | |
249 | ||
250 | newwidth = format->bmiHeader.biWidth; | |
251 | newheight = format->bmiHeader.biHeight; | |
252 | ||
253 | TRACE("%p -> (%p) - %d %d\n", capBox, mT, newwidth, newheight); | |
254 | ||
255 | if (capBox->height == newheight && capBox->width == newwidth) | |
256 | return S_OK; | |
257 | ||
258 | if(-1 == xioctl(capBox->fd, VIDIOCGWIN, &window)) | |
259 | { | |
260 | ERR("ioctl(VIDIOCGWIN) failed (%d)\n", errno); | |
261 | return E_FAIL; | |
262 | } | |
263 | window.width = newwidth; | |
264 | window.height = newheight; | |
265 | if (xioctl(capBox->fd, VIDIOCSWIN, &window) == -1) | |
266 | { | |
267 | TRACE("using software resize: %dx%d -> %dx%d\n", | |
268 | window.width, window.height, capBox->width, capBox->height); | |
269 | capBox->swresize = TRUE; | |
270 | } | |
271 | else | |
272 | { | |
273 | capBox->height = window.height; | |
274 | capBox->width = window.width; | |
275 | capBox->swresize = FALSE; | |
276 | } | |
277 | capBox->outputwidth = window.width; | |
278 | capBox->outputheight = window.height; | |
279 | return S_OK; | |
280 | } | |
281 | ||
48dcc3fc | 282 | HRESULT qcap_driver_get_format(const Capture *capBox, AM_MEDIA_TYPE ** mT) |
b58f0a3f ML |
283 | { |
284 | VIDEOINFOHEADER *vi; | |
285 | ||
286 | mT[0] = CoTaskMemAlloc(sizeof(AM_MEDIA_TYPE)); | |
287 | if (!mT[0]) | |
288 | return E_OUTOFMEMORY; | |
289 | vi = CoTaskMemAlloc(sizeof(VIDEOINFOHEADER)); | |
290 | mT[0]->cbFormat = sizeof(VIDEOINFOHEADER); | |
291 | if (!vi) | |
292 | { | |
293 | CoTaskMemFree(mT[0]); | |
294 | return E_OUTOFMEMORY; | |
295 | } | |
296 | memcpy(&mT[0]->majortype, &MEDIATYPE_Video, sizeof(GUID)); | |
297 | memcpy(&mT[0]->subtype, &MEDIASUBTYPE_RGB24, sizeof(GUID)); | |
298 | memcpy(&mT[0]->formattype, &FORMAT_VideoInfo, sizeof(GUID)); | |
299 | mT[0]->bFixedSizeSamples = TRUE; | |
300 | mT[0]->bTemporalCompression = FALSE; | |
301 | mT[0]->pUnk = NULL; | |
302 | mT[0]->lSampleSize = capBox->outputwidth * capBox->outputheight * capBox->bitDepth / 8; | |
079bb8a5 | 303 | TRACE("Output format: %dx%d - %d bits = %u KB\n", capBox->outputwidth, |
b58f0a3f ML |
304 | capBox->outputheight, capBox->bitDepth, mT[0]->lSampleSize/1024); |
305 | vi->rcSource.left = 0; vi->rcSource.top = 0; | |
306 | vi->rcTarget.left = 0; vi->rcTarget.top = 0; | |
307 | vi->rcSource.right = capBox->width; vi->rcSource.bottom = capBox->height; | |
308 | vi->rcTarget.right = capBox->outputwidth; vi->rcTarget.bottom = capBox->outputheight; | |
309 | vi->dwBitRate = capBox->fps * mT[0]->lSampleSize; | |
310 | vi->dwBitErrorRate = 0; | |
311 | vi->AvgTimePerFrame = (LONGLONG)10000000.0 / (LONGLONG)capBox->fps; | |
312 | vi->bmiHeader.biSize = 40; | |
313 | vi->bmiHeader.biWidth = capBox->outputwidth; | |
314 | vi->bmiHeader.biHeight = capBox->outputheight; | |
315 | vi->bmiHeader.biPlanes = 1; | |
316 | vi->bmiHeader.biBitCount = 24; | |
317 | vi->bmiHeader.biCompression = BI_RGB; | |
318 | vi->bmiHeader.biSizeImage = mT[0]->lSampleSize; | |
319 | vi->bmiHeader.biClrUsed = vi->bmiHeader.biClrImportant = 0; | |
320 | vi->bmiHeader.biXPelsPerMeter = 100; | |
321 | vi->bmiHeader.biYPelsPerMeter = 100; | |
322 | mT[0]->pbFormat = (void *)vi; | |
323 | dump_AM_MEDIA_TYPE(mT[0]); | |
324 | return S_OK; | |
325 | } | |
326 | ||
327 | HRESULT qcap_driver_get_prop_range( Capture *capBox, long Property, long *pMin, | |
328 | long *pMax, long *pSteppingDelta, long *pDefault, long *pCapsFlags ) | |
329 | { | |
330 | TRACE("%p -> %ld %p %p %p %p %p\n", capBox, Property, | |
331 | pMin, pMax, pSteppingDelta, pDefault, pCapsFlags); | |
332 | ||
333 | switch (Property) | |
334 | { | |
335 | case VideoProcAmp_Brightness: | |
336 | *pDefault = capBox->dbrightness; | |
337 | break; | |
338 | case VideoProcAmp_Contrast: | |
339 | *pDefault = capBox->dcontrast; | |
340 | break; | |
341 | case VideoProcAmp_Hue: | |
342 | *pDefault = capBox->dhue; | |
343 | break; | |
344 | case VideoProcAmp_Saturation: | |
345 | *pDefault = capBox->dcolour; | |
346 | break; | |
347 | default: | |
348 | FIXME("Not implemented %ld\n", Property); | |
349 | return E_NOTIMPL; | |
350 | } | |
351 | *pMin = 0; | |
352 | *pMax = 65535; | |
353 | *pSteppingDelta = 65536/256; | |
354 | *pCapsFlags = VideoProcAmp_Flags_Manual; | |
355 | return S_OK; | |
356 | } | |
357 | ||
358 | HRESULT qcap_driver_get_prop( Capture *capBox, long Property, long *lValue, long *Flags ) | |
359 | { | |
360 | TRACE("%p -> %ld %p %p\n", capBox, Property, lValue, Flags); | |
361 | ||
362 | switch (Property) | |
363 | { | |
364 | case VideoProcAmp_Brightness: | |
365 | *lValue = capBox->pict.brightness; | |
366 | break; | |
367 | case VideoProcAmp_Contrast: | |
368 | *lValue = capBox->pict.contrast; | |
369 | break; | |
370 | case VideoProcAmp_Hue: | |
371 | *lValue = capBox->pict.hue; | |
372 | break; | |
373 | case VideoProcAmp_Saturation: | |
374 | *lValue = capBox->pict.colour; | |
375 | break; | |
376 | default: | |
377 | FIXME("Not implemented %ld\n", Property); | |
378 | return E_NOTIMPL; | |
379 | } | |
380 | *Flags = VideoProcAmp_Flags_Manual; | |
381 | return S_OK; | |
382 | } | |
383 | ||
384 | HRESULT qcap_driver_set_prop(Capture *capBox, long Property, long lValue, long Flags) | |
385 | { | |
386 | TRACE("%p -> %ld %ld %ld\n", capBox, Property, lValue, Flags); | |
387 | ||
388 | switch (Property) | |
389 | { | |
390 | case VideoProcAmp_Brightness: | |
391 | capBox->pict.brightness = lValue; | |
392 | break; | |
393 | case VideoProcAmp_Contrast: | |
394 | capBox->pict.contrast = lValue; | |
395 | break; | |
396 | case VideoProcAmp_Hue: | |
397 | capBox->pict.hue = lValue; | |
398 | break; | |
399 | case VideoProcAmp_Saturation: | |
400 | capBox->pict.colour = lValue; | |
401 | break; | |
402 | default: | |
403 | FIXME("Not implemented %ld\n", Property); | |
404 | return E_NOTIMPL; | |
405 | } | |
406 | ||
407 | if (xioctl(capBox->fd, VIDIOCSPICT, &capBox->pict) == -1) | |
408 | { | |
409 | ERR("ioctl(VIDIOCSPICT) failed (%d)\n",errno); | |
410 | return E_FAIL; | |
411 | } | |
412 | return S_OK; | |
413 | } | |
414 | ||
48dcc3fc | 415 | static void renderer_RGB(const Capture *capBox, LPBYTE bufferin, const BYTE *stream) |
b58f0a3f ML |
416 | { |
417 | int depth = renderlist_V4l[capBox->pict.palette].depth; | |
418 | int size = capBox->height * capBox->width * depth / 8; | |
419 | int pointer, offset; | |
420 | ||
421 | switch (depth) | |
422 | { | |
423 | case 24: | |
424 | memcpy(bufferin, stream, size); | |
425 | break; | |
426 | case 32: | |
427 | pointer = 0; | |
428 | offset = 1; | |
429 | while (pointer + offset <= size) | |
430 | { | |
431 | bufferin[pointer] = stream[pointer + offset]; | |
432 | pointer++; | |
433 | bufferin[pointer] = stream[pointer + offset]; | |
434 | pointer++; | |
435 | bufferin[pointer] = stream[pointer + offset]; | |
436 | pointer++; | |
437 | offset++; | |
438 | } | |
439 | break; | |
440 | default: | |
441 | ERR("Unknown bit depth %d\n", depth); | |
442 | return; | |
443 | } | |
444 | } | |
445 | ||
48dcc3fc | 446 | static void renderer_YUV(const Capture *capBox, LPBYTE bufferin, const BYTE *stream) |
b58f0a3f ML |
447 | { |
448 | enum YUV_Format format; | |
449 | ||
450 | switch (capBox->pict.palette) | |
451 | { | |
452 | case 7: /* YUV422 - same as YUYV */ | |
453 | case 8: /* YUYV */ | |
454 | format = YUYV; | |
455 | break; | |
456 | case 9: /* UYVY */ | |
457 | format = UYVY; | |
458 | break; | |
459 | case 11: /* YUV411 */ | |
460 | format = UYYVYY; | |
461 | break; | |
462 | case 13: /* YUV422P */ | |
463 | format = YUVP_421; | |
464 | break; | |
465 | case 14: /* YUV411P */ | |
466 | format = YUVP_441; | |
467 | break; | |
468 | case 15: /* YUV420P */ | |
469 | format = YUVP_422; | |
470 | break; | |
471 | case 16: /* YUV410P */ | |
472 | format = YUVP_444; | |
473 | break; | |
474 | default: | |
475 | ERR("Unknown palette %d\n", capBox->pict.palette); | |
476 | return; | |
477 | } | |
478 | YUV_To_RGB24(format, bufferin, stream, capBox->width, capBox->height); | |
479 | } | |
480 | ||
48dcc3fc | 481 | static void Resize(const Capture * capBox, LPBYTE output, const BYTE *input) |
b58f0a3f ML |
482 | { |
483 | /* the whole image needs to be reversed, | |
484 | because the dibs are messed up in windows */ | |
485 | if (!capBox->swresize) | |
486 | { | |
487 | int depth = capBox->bitDepth / 8; | |
488 | int inoffset = 0, outoffset = capBox->height * capBox->width * depth; | |
489 | int ow = capBox->width * depth; | |
490 | while (outoffset > 0) | |
491 | { | |
492 | int x; | |
493 | outoffset -= ow; | |
494 | for (x = 0; x < ow; x++) | |
495 | output[outoffset + x] = input[inoffset + x]; | |
496 | inoffset += ow; | |
497 | } | |
498 | } | |
499 | else | |
500 | { | |
501 | HDC dc_s, dc_d; | |
502 | HBITMAP bmp_s, bmp_d; | |
503 | int depth = capBox->bitDepth / 8; | |
504 | int inoffset = 0, outoffset = (capBox->outputheight) * capBox->outputwidth * depth; | |
505 | int ow = capBox->outputwidth * depth; | |
506 | LPBYTE myarray; | |
507 | ||
508 | /* FIXME: Improve software resizing: add error checks and optimize */ | |
509 | ||
510 | myarray = CoTaskMemAlloc(capBox->outputwidth * capBox->outputheight * depth); | |
511 | dc_s = CreateCompatibleDC(NULL); | |
512 | dc_d = CreateCompatibleDC(NULL); | |
513 | bmp_s = CreateBitmap(capBox->width, capBox->height, 1, capBox->bitDepth, input); | |
514 | bmp_d = CreateBitmap(capBox->outputwidth, capBox->outputheight, 1, capBox->bitDepth, NULL); | |
515 | SelectObject(dc_s, bmp_s); | |
516 | SelectObject(dc_d, bmp_d); | |
517 | StretchBlt(dc_d, 0, 0, capBox->outputwidth, capBox->outputheight, | |
518 | dc_s, 0, 0, capBox->width, capBox->height, SRCCOPY); | |
519 | GetBitmapBits(bmp_d, capBox->outputwidth * capBox->outputheight * depth, myarray); | |
520 | while (outoffset > 0) | |
521 | { | |
522 | int i; | |
523 | ||
524 | outoffset -= ow; | |
525 | for (i = 0; i < ow; i++) | |
526 | output[outoffset + i] = myarray[inoffset + i]; | |
527 | inoffset += ow; | |
528 | } | |
529 | CoTaskMemFree(myarray); | |
530 | DeleteObject(dc_s); | |
531 | DeleteObject(dc_d); | |
532 | DeleteObject(bmp_s); | |
533 | DeleteObject(bmp_d); | |
534 | } | |
535 | } | |
536 | ||
537 | static void V4l_GetFrame(Capture * capBox, unsigned char ** pInput) | |
538 | { | |
539 | if (capBox->pmap) | |
540 | { | |
541 | if (xioctl(capBox->fd, VIDIOCSYNC, &capBox->grab_buf[capBox->curframe]) == -1) | |
542 | WARN("Syncing ioctl failed: %d\n", errno); | |
543 | ||
544 | *pInput = capBox->pmap + capBox->gb_buffers.offsets[capBox->curframe]; | |
545 | } | |
546 | else | |
547 | { | |
548 | int retval; | |
549 | while ((retval = read(capBox->fd, capBox->grab_data, capBox->imagesize)) == -1) | |
550 | if (errno != EAGAIN) break; | |
551 | if (retval == -1) | |
6394a153 | 552 | WARN("Error occurred while reading from device: %s\n", strerror(errno)); |
6dc69f5f | 553 | *pInput = (unsigned char*) capBox->grab_data; |
b58f0a3f ML |
554 | } |
555 | } | |
556 | ||
557 | static void V4l_FreeFrame(Capture * capBox) | |
558 | { | |
559 | TRACE("\n"); | |
560 | if (capBox->mmap) | |
561 | { | |
562 | if (xioctl(capBox->fd, VIDIOCMCAPTURE, &capBox->grab_buf[capBox->curframe]) == -1) | |
563 | ERR("Freeing frame for capture failed: %s\n", strerror(errno)); | |
564 | } | |
565 | if (++capBox->curframe == capBox->buffers) | |
566 | capBox->curframe = 0; | |
567 | } | |
568 | ||
569 | static DWORD WINAPI ReadThread(LPVOID lParam) | |
570 | { | |
571 | Capture * capBox = (Capture *)lParam; | |
572 | HRESULT hr; | |
573 | IMediaSample *pSample = NULL; | |
574 | unsigned long framecount = 0; | |
575 | unsigned char *pTarget, *pInput, *pOutput; | |
576 | ||
577 | hr = V4l_Prepare(capBox); | |
578 | if (FAILED(hr)) | |
579 | goto fail; | |
580 | ||
581 | pOutput = CoTaskMemAlloc(capBox->width * capBox->height * capBox->bitDepth / 8); | |
582 | capBox->curframe = 0; | |
583 | do { | |
584 | V4l_FreeFrame(capBox); | |
585 | } while (capBox->curframe != 0); | |
586 | ||
587 | while (1) | |
588 | { | |
589 | EnterCriticalSection(&capBox->CritSect); | |
590 | if (capBox->stopped) | |
591 | break; | |
592 | hr = OutputPin_GetDeliveryBuffer((OutputPin *)capBox->pOut, &pSample, NULL, NULL, 0); | |
593 | if (SUCCEEDED(hr)) | |
594 | { | |
595 | int len; | |
596 | ||
597 | if (!capBox->swresize) | |
598 | len = capBox->height * capBox->width * capBox->bitDepth / 8; | |
599 | else | |
600 | len = capBox->outputheight * capBox->outputwidth * capBox->bitDepth / 8; | |
601 | IMediaSample_SetActualDataLength(pSample, len); | |
602 | ||
603 | len = IMediaSample_GetActualDataLength(pSample); | |
604 | TRACE("Data length: %d KB\n", len / 1024); | |
605 | ||
606 | IMediaSample_GetPointer(pSample, &pTarget); | |
607 | /* FIXME: Check return values.. */ | |
608 | V4l_GetFrame(capBox, &pInput); | |
609 | capBox->renderer(capBox, pOutput, pInput); | |
610 | Resize(capBox, pTarget, pOutput); | |
611 | hr = OutputPin_SendSample((OutputPin *)capBox->pOut, pSample); | |
079bb8a5 | 612 | TRACE("%p -> Frame %lu: %x\n", capBox, ++framecount, hr); |
b58f0a3f ML |
613 | IMediaSample_Release(pSample); |
614 | V4l_FreeFrame(capBox); | |
615 | } | |
616 | LeaveCriticalSection(&capBox->CritSect); | |
617 | if (FAILED(hr) && hr != VFW_E_NOT_CONNECTED) | |
618 | { | |
079bb8a5 | 619 | ERR("Received error: %x\n", hr); |
b58f0a3f ML |
620 | goto cfail; |
621 | } | |
622 | } | |
623 | LeaveCriticalSection(&capBox->CritSect); | |
624 | CoTaskMemFree(pOutput); | |
625 | ||
626 | return 0; | |
627 | ||
628 | cfail: | |
629 | CoTaskMemFree(pOutput); | |
630 | V4l_Unprepare(capBox); | |
631 | LeaveCriticalSection(&capBox->CritSect); | |
632 | ||
633 | fail: | |
634 | capBox->thread = 0; capBox->stopped = 1; | |
635 | FIXME("Stop IFilterGraph\n"); | |
636 | return 0; | |
637 | } | |
638 | ||
639 | HRESULT qcap_driver_run(Capture *capBox, FILTER_STATE *state) | |
640 | { | |
641 | HANDLE thread; | |
642 | HRESULT hr; | |
643 | ||
644 | TRACE("%p -> (%p)\n", capBox, state); | |
645 | ||
646 | if (*state == State_Running) return S_OK; | |
647 | ||
648 | EnterCriticalSection(&capBox->CritSect); | |
649 | ||
650 | capBox->stopped = 0; | |
651 | ||
652 | if (*state == State_Stopped) | |
653 | { | |
654 | *state = State_Running; | |
655 | if (!capBox->iscommitted++) | |
656 | { | |
657 | IMemAllocator * pAlloc = NULL; | |
658 | ALLOCATOR_PROPERTIES ap, actual; | |
659 | OutputPin *out; | |
660 | ||
661 | ap.cBuffers = 3; | |
662 | if (!capBox->swresize) | |
663 | ap.cbBuffer = capBox->width * capBox->height; | |
664 | else | |
665 | ap.cbBuffer = capBox->outputwidth * capBox->outputheight; | |
666 | ap.cbBuffer = (ap.cbBuffer * capBox->bitDepth) / 8; | |
667 | ap.cbAlign = 1; | |
668 | ap.cbPrefix = 0; | |
669 | ||
670 | out = (OutputPin *)capBox->pOut; | |
671 | hr = IMemInputPin_GetAllocator(out->pMemInputPin, &pAlloc); | |
672 | ||
673 | if (SUCCEEDED(hr)) | |
674 | hr = IMemAllocator_SetProperties(pAlloc, &ap, &actual); | |
675 | ||
676 | if (SUCCEEDED(hr)) | |
677 | hr = IMemAllocator_Commit(pAlloc); | |
678 | ||
679 | if (pAlloc) | |
680 | IMemAllocator_Release(pAlloc); | |
681 | ||
079bb8a5 | 682 | TRACE("Committing allocator: %x\n", hr); |
b58f0a3f ML |
683 | } |
684 | ||
685 | thread = CreateThread(NULL, 0, ReadThread, capBox, 0, NULL); | |
686 | if (thread) | |
687 | { | |
688 | capBox->thread = thread; | |
689 | SetThreadPriority(thread, THREAD_PRIORITY_LOWEST); | |
690 | LeaveCriticalSection(&capBox->CritSect); | |
691 | return S_OK; | |
692 | } | |
597f0b86 | 693 | ERR("Creating thread failed.. %u\n", GetLastError()); |
b58f0a3f ML |
694 | LeaveCriticalSection(&capBox->CritSect); |
695 | return E_FAIL; | |
696 | } | |
697 | ||
698 | ResumeThread(capBox->thread); | |
699 | *state = State_Running; | |
700 | LeaveCriticalSection(&capBox->CritSect); | |
701 | return S_OK; | |
702 | } | |
703 | ||
704 | HRESULT qcap_driver_pause(Capture *capBox, FILTER_STATE *state) | |
705 | { | |
706 | TRACE("%p -> (%p)\n", capBox, state); | |
707 | ||
708 | if (*state == State_Paused) | |
709 | return S_OK; | |
710 | if (*state == State_Stopped) | |
711 | qcap_driver_run(capBox, state); | |
712 | ||
713 | EnterCriticalSection(&capBox->CritSect); | |
714 | *state = State_Paused; | |
715 | SuspendThread(capBox->thread); | |
716 | LeaveCriticalSection(&capBox->CritSect); | |
717 | ||
718 | return S_OK; | |
719 | } | |
720 | ||
721 | HRESULT qcap_driver_stop(Capture *capBox, FILTER_STATE *state) | |
722 | { | |
723 | TRACE("%p -> (%p)\n", capBox, state); | |
724 | ||
725 | if (*state == State_Stopped) | |
726 | return S_OK; | |
727 | ||
728 | EnterCriticalSection(&capBox->CritSect); | |
729 | ||
730 | if (capBox->thread) | |
731 | { | |
732 | if (*state == State_Paused) | |
733 | ResumeThread(capBox->thread); | |
734 | capBox->stopped = 1; | |
735 | capBox->thread = 0; | |
736 | if (capBox->iscommitted) | |
737 | { | |
738 | IMemInputPin *pMem = NULL; | |
739 | IMemAllocator * pAlloc = NULL; | |
740 | IPin *pConnect = NULL; | |
741 | HRESULT hr; | |
742 | ||
743 | capBox->iscommitted = 0; | |
744 | ||
745 | hr = IPin_ConnectedTo(capBox->pOut, &pConnect); | |
746 | ||
747 | if (SUCCEEDED(hr)) | |
748 | hr = IPin_QueryInterface(pConnect, &IID_IMemInputPin, (void **) &pMem); | |
749 | ||
750 | if (SUCCEEDED(hr)) | |
751 | hr = IMemInputPin_GetAllocator(pMem, &pAlloc); | |
752 | ||
753 | if (SUCCEEDED(hr)) | |
754 | hr = IMemAllocator_Decommit(pAlloc); | |
755 | ||
756 | if (pAlloc) | |
757 | IMemAllocator_Release(pAlloc); | |
758 | ||
759 | if (pMem) | |
760 | IMemInputPin_Release(pMem); | |
761 | ||
762 | if (pConnect) | |
763 | IPin_Release(pConnect); | |
764 | ||
765 | if (hr != S_OK && hr != VFW_E_NOT_COMMITTED) | |
079bb8a5 | 766 | WARN("Decommitting allocator: %x\n", hr); |
b58f0a3f ML |
767 | } |
768 | V4l_Unprepare(capBox); | |
769 | } | |
770 | ||
771 | *state = State_Stopped; | |
772 | LeaveCriticalSection(&capBox->CritSect); | |
773 | return S_OK; | |
774 | } | |
775 | ||
776 | Capture * qcap_driver_init( IPin *pOut, USHORT card ) | |
777 | { | |
778 | Capture * capBox = NULL; | |
779 | char device[20]; | |
780 | struct video_capability capa; | |
781 | struct video_picture pict; | |
782 | struct video_window window; | |
783 | ||
784 | YUV_Init(); | |
785 | ||
786 | capBox = CoTaskMemAlloc(sizeof(Capture)); | |
787 | if (!capBox) | |
788 | goto error; | |
789 | ||
790 | /* capBox->vtbl = &defboxVtbl; */ | |
791 | ||
792 | InitializeCriticalSection( &capBox->CritSect ); | |
614ee831 | 793 | capBox->CritSect.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": Capture.CritSect"); |
b58f0a3f ML |
794 | |
795 | sprintf(device, "/dev/video%i", card); | |
796 | TRACE("opening %s\n", device); | |
797 | capBox->fd = open(device, O_RDWR | O_NONBLOCK); | |
798 | if (capBox->fd == -1) | |
799 | { | |
800 | WARN("open failed (%d)\n", errno); | |
801 | goto error; | |
802 | } | |
803 | ||
804 | memset(&capa, 0, sizeof(capa)); | |
805 | ||
806 | if (xioctl(capBox->fd, VIDIOCGCAP, &capa) == -1) | |
807 | { | |
808 | WARN("ioctl(VIDIOCGCAP) failed (%d)\n", errno); | |
809 | goto error; | |
810 | } | |
811 | ||
812 | if (!(capa.type & VID_TYPE_CAPTURE)) | |
813 | { | |
814 | WARN("not a video capture device\n"); | |
815 | goto error; | |
816 | } | |
817 | ||
818 | TRACE("%d inputs on %s\n", capa.channels, capa.name ); | |
819 | ||
820 | if (xioctl(capBox->fd, VIDIOCGPICT, &pict) == -1) | |
821 | { | |
822 | ERR("ioctl(VIDIOCGPICT) failed (%d)\n", errno ); | |
823 | goto error; | |
824 | } | |
825 | ||
826 | TRACE("depth %d palette %d (%s) hue %d color %d contrast %d\n", | |
827 | pict.depth, pict.palette, renderlist_V4l[pict.palette].name, | |
828 | pict.hue, pict.colour, pict.contrast ); | |
829 | ||
830 | capBox->dbrightness = pict.brightness; | |
831 | capBox->dcolour = pict.colour; | |
832 | capBox->dhue = pict.hue; | |
833 | capBox->dcontrast = pict.contrast; | |
834 | ||
835 | if (!renderlist_V4l[pict.palette].renderer) | |
836 | { | |
837 | int palet = pict.palette, i; | |
838 | ||
839 | TRACE("No renderer available for %s, falling back to defaults\n", | |
840 | renderlist_V4l[pict.palette].name); | |
841 | capBox->renderer = NULL; | |
842 | for (i = 0; fallback_V4l[i] >=0 ; i++) | |
843 | { | |
844 | int n = fallback_V4l[i]; | |
845 | ||
846 | if (renderlist_V4l[n].renderer == NULL) | |
847 | continue; | |
848 | ||
849 | pict.depth = renderlist_V4l[n].depth; | |
850 | pict.palette = n; | |
851 | if (xioctl(capBox->fd, VIDIOCSPICT, &pict) == -1) | |
852 | { | |
853 | TRACE("Could not render with %s (%d)\n", | |
854 | renderlist_V4l[n].name, n); | |
855 | continue; | |
856 | } | |
857 | TRACE("using renderer %s (%d)\n", | |
858 | renderlist_V4l[n].name, n); | |
859 | capBox->renderer = renderlist_V4l[n].renderer; | |
860 | break; | |
861 | } | |
862 | ||
863 | if (!capBox->renderer) | |
864 | { | |
865 | ERR("video format %s isn't available\n", | |
866 | renderlist_V4l[palet].name); | |
867 | goto error; | |
868 | } | |
869 | } | |
870 | else | |
871 | { | |
872 | TRACE("Using the suggested format\n"); | |
873 | capBox->renderer = renderlist_V4l[pict.palette].renderer; | |
874 | } | |
875 | memcpy(&capBox->pict, &pict, sizeof(struct video_picture)); | |
876 | ||
877 | memset(&window, 0, sizeof(window)); | |
878 | if (xioctl(capBox->fd, VIDIOCGWIN, &window) == -1) | |
879 | { | |
880 | WARN("VIDIOCGWIN failed (%d)\n", errno); | |
881 | goto error; | |
882 | } | |
883 | ||
884 | capBox->height = capBox->outputheight = window.height; | |
885 | capBox->width = capBox->outputwidth = window.width; | |
886 | capBox->swresize = FALSE; | |
887 | capBox->bitDepth = 24; | |
888 | capBox->pOut = pOut; | |
889 | capBox->fps = 3; | |
890 | capBox->stopped = 0; | |
891 | capBox->curframe = 0; | |
892 | capBox->iscommitted = 0; | |
893 | ||
894 | TRACE("format: %d bits - %d x %d\n", capBox->bitDepth, capBox->width, capBox->height); | |
895 | ||
896 | return (Capture*) capBox; | |
897 | ||
898 | error: | |
899 | if (capBox) | |
900 | qcap_driver_destroy( (Capture*) capBox ); | |
901 | ||
902 | return NULL; | |
903 | } | |
904 | ||
905 | #else | |
906 | ||
907 | Capture * qcap_driver_init( IPin *pOut, USHORT card ) | |
908 | { | |
909 | const char msg[] = | |
910 | "The v4l headers were not available at compile time,\n" | |
911 | "so video capture support is not available.\n"; | |
912 | MESSAGE(msg); | |
913 | return NULL; | |
914 | } | |
915 | ||
916 | #define FAIL_WITH_ERR \ | |
917 | ERR("v4l absent: shouldn't be called\n"); \ | |
918 | return E_NOTIMPL | |
919 | ||
920 | HRESULT qcap_driver_destroy(Capture *capBox) | |
921 | { | |
922 | FAIL_WITH_ERR; | |
923 | } | |
924 | ||
925 | HRESULT qcap_driver_set_format(Capture *capBox, AM_MEDIA_TYPE * mT) | |
926 | { | |
927 | FAIL_WITH_ERR; | |
928 | } | |
929 | ||
48dcc3fc | 930 | HRESULT qcap_driver_get_format(const Capture *capBox, AM_MEDIA_TYPE ** mT) |
b58f0a3f ML |
931 | { |
932 | FAIL_WITH_ERR; | |
933 | } | |
934 | ||
935 | HRESULT qcap_driver_get_prop_range( Capture *capBox, long Property, long *pMin, | |
936 | long *pMax, long *pSteppingDelta, long *pDefault, long *pCapsFlags ) | |
937 | { | |
938 | FAIL_WITH_ERR; | |
939 | } | |
940 | ||
941 | HRESULT qcap_driver_get_prop(Capture *capBox, long Property, long *lValue, long *Flags) | |
942 | { | |
943 | FAIL_WITH_ERR; | |
944 | } | |
945 | ||
946 | HRESULT qcap_driver_set_prop(Capture *capBox, long Property, long lValue, long Flags) | |
947 | { | |
948 | FAIL_WITH_ERR; | |
949 | } | |
950 | ||
951 | HRESULT qcap_driver_run(Capture *capBox, FILTER_STATE *state) | |
952 | { | |
953 | FAIL_WITH_ERR; | |
954 | } | |
955 | ||
956 | HRESULT qcap_driver_pause(Capture *capBox, FILTER_STATE *state) | |
957 | { | |
958 | FAIL_WITH_ERR; | |
959 | } | |
960 | ||
961 | HRESULT qcap_driver_stop(Capture *capBox, FILTER_STATE *state) | |
962 | { | |
963 | FAIL_WITH_ERR; | |
964 | } | |
965 | ||
966 | #endif /* HAVE_LINUX_VIDEODEV_H */ |