wineoss: Renamed the dlls/winmm/wineoss directory to dlls/wineoss.drv.
[wine] / dlls / qcap / v4l.c
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
22  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
23  */
24
25 #include "config.h"
26 #include "wine/port.h"
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
76 typedef void (* Renderer)(Capture *, LPBYTE bufferin, LPBYTE stream);
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
114 static void renderer_RGB(Capture *capBox, LPBYTE bufferin, LPBYTE stream);
115 static void renderer_YUV(Capture *capBox, LPBYTE bufferin, LPBYTE stream);
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
139 const int fallback_V4l[] = { 4, 5, 7, 8, 9, 13, 15, 14, 16, 11, -1 };
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);
227     DeleteCriticalSection(&capBox->CritSect);
228     CoTaskMemFree(capBox);
229     return S_OK;
230 }
231
232 HRESULT qcap_driver_set_format(Capture *capBox, AM_MEDIA_TYPE * mT)
233 {
234     int newheight, newwidth;
235     struct video_window window;
236     VIDEOINFOHEADER *format;
237
238     TRACE("%p\n", capBox);
239
240     format = (VIDEOINFOHEADER *) mT->pbFormat;
241     if (format->bmiHeader.biBitCount != 24 ||
242         format->bmiHeader.biCompression != BI_RGB)
243     {
244         FIXME("unsupported media type %d %d\n", format->bmiHeader.biBitCount,
245               format->bmiHeader.biCompression );
246         return VFW_E_INVALIDMEDIATYPE;
247     }
248
249     newwidth = format->bmiHeader.biWidth;
250     newheight = format->bmiHeader.biHeight;
251
252     TRACE("%p -> (%p) - %d %d\n", capBox, mT, newwidth, newheight);
253
254     if (capBox->height == newheight && capBox->width == newwidth)
255         return S_OK;
256
257     if(-1 == xioctl(capBox->fd, VIDIOCGWIN, &window))
258     {
259         ERR("ioctl(VIDIOCGWIN) failed (%d)\n", errno);
260         return E_FAIL;
261     }
262     window.width = newwidth;
263     window.height = newheight;
264     if (xioctl(capBox->fd, VIDIOCSWIN, &window) == -1)
265     {
266         TRACE("using software resize: %dx%d -> %dx%d\n",
267                window.width, window.height, capBox->width, capBox->height);
268         capBox->swresize = TRUE;
269     }
270     else
271     {
272         capBox->height = window.height;
273         capBox->width = window.width;
274         capBox->swresize = FALSE;
275     }
276     capBox->outputwidth = window.width;
277     capBox->outputheight = window.height;
278     return S_OK;
279 }
280
281 HRESULT qcap_driver_get_format(Capture *capBox, AM_MEDIA_TYPE ** mT)
282 {
283     VIDEOINFOHEADER *vi;
284
285     mT[0] = CoTaskMemAlloc(sizeof(AM_MEDIA_TYPE));
286     if (!mT[0])
287         return E_OUTOFMEMORY;
288     vi = CoTaskMemAlloc(sizeof(VIDEOINFOHEADER));
289     mT[0]->cbFormat = sizeof(VIDEOINFOHEADER);
290     if (!vi)
291     {
292         CoTaskMemFree(mT[0]);
293         return E_OUTOFMEMORY;
294     }
295     memcpy(&mT[0]->majortype, &MEDIATYPE_Video, sizeof(GUID));
296     memcpy(&mT[0]->subtype, &MEDIASUBTYPE_RGB24, sizeof(GUID));
297     memcpy(&mT[0]->formattype, &FORMAT_VideoInfo, sizeof(GUID));
298     mT[0]->bFixedSizeSamples = TRUE;
299     mT[0]->bTemporalCompression = FALSE;
300     mT[0]->pUnk = NULL;
301     mT[0]->lSampleSize = capBox->outputwidth * capBox->outputheight * capBox->bitDepth / 8;
302     TRACE("Output format: %dx%d - %d bits = %u KB\n", capBox->outputwidth,
303           capBox->outputheight, capBox->bitDepth, mT[0]->lSampleSize/1024);
304     vi->rcSource.left = 0; vi->rcSource.top = 0;
305     vi->rcTarget.left = 0; vi->rcTarget.top = 0;
306     vi->rcSource.right = capBox->width; vi->rcSource.bottom = capBox->height;
307     vi->rcTarget.right = capBox->outputwidth; vi->rcTarget.bottom = capBox->outputheight;
308     vi->dwBitRate = capBox->fps * mT[0]->lSampleSize;
309     vi->dwBitErrorRate = 0;
310     vi->AvgTimePerFrame = (LONGLONG)10000000.0 / (LONGLONG)capBox->fps;
311     vi->bmiHeader.biSize = 40;
312     vi->bmiHeader.biWidth = capBox->outputwidth;
313     vi->bmiHeader.biHeight = capBox->outputheight;
314     vi->bmiHeader.biPlanes = 1;
315     vi->bmiHeader.biBitCount = 24;
316     vi->bmiHeader.biCompression = BI_RGB;
317     vi->bmiHeader.biSizeImage = mT[0]->lSampleSize;
318     vi->bmiHeader.biClrUsed = vi->bmiHeader.biClrImportant = 0;
319     vi->bmiHeader.biXPelsPerMeter = 100;
320     vi->bmiHeader.biYPelsPerMeter = 100;
321     mT[0]->pbFormat = (void *)vi;
322     dump_AM_MEDIA_TYPE(mT[0]);
323     return S_OK;
324 }
325
326 HRESULT qcap_driver_get_prop_range( Capture *capBox, long Property, long *pMin,
327             long *pMax, long *pSteppingDelta, long *pDefault, long *pCapsFlags )
328 {
329     TRACE("%p -> %ld %p %p %p %p %p\n", capBox, Property,
330           pMin, pMax, pSteppingDelta, pDefault, pCapsFlags);
331
332     switch (Property)
333     {
334     case VideoProcAmp_Brightness:
335         *pDefault = capBox->dbrightness;
336         break;
337     case VideoProcAmp_Contrast:
338         *pDefault = capBox->dcontrast;
339         break;
340     case VideoProcAmp_Hue:
341         *pDefault = capBox->dhue;
342         break;
343     case VideoProcAmp_Saturation:
344         *pDefault = capBox->dcolour;
345         break;
346     default:
347         FIXME("Not implemented %ld\n", Property);
348         return E_NOTIMPL;
349     }
350     *pMin = 0;
351     *pMax = 65535;
352     *pSteppingDelta = 65536/256;
353     *pCapsFlags = VideoProcAmp_Flags_Manual;
354     return S_OK;
355 }
356
357 HRESULT qcap_driver_get_prop( Capture *capBox, long Property, long *lValue, long *Flags )
358 {
359     TRACE("%p -> %ld %p %p\n", capBox, Property, lValue, Flags);
360
361     switch (Property)
362     {
363     case VideoProcAmp_Brightness:
364         *lValue = capBox->pict.brightness;
365         break;
366     case VideoProcAmp_Contrast:
367         *lValue = capBox->pict.contrast;
368         break;
369     case VideoProcAmp_Hue:
370         *lValue = capBox->pict.hue;
371         break;
372     case VideoProcAmp_Saturation:
373         *lValue = capBox->pict.colour;
374         break;
375     default:
376         FIXME("Not implemented %ld\n", Property);
377         return E_NOTIMPL;
378     }
379     *Flags = VideoProcAmp_Flags_Manual;
380     return S_OK;
381 }
382
383 HRESULT qcap_driver_set_prop(Capture *capBox, long Property, long lValue, long Flags)
384 {
385     TRACE("%p -> %ld %ld %ld\n", capBox, Property, lValue, Flags);
386
387     switch (Property)
388     {
389     case VideoProcAmp_Brightness:
390         capBox->pict.brightness = lValue;
391         break;
392     case VideoProcAmp_Contrast:
393         capBox->pict.contrast = lValue;
394         break;
395     case VideoProcAmp_Hue:
396         capBox->pict.hue = lValue;
397         break;
398     case VideoProcAmp_Saturation:
399         capBox->pict.colour = lValue;
400         break;
401     default:
402         FIXME("Not implemented %ld\n", Property);
403         return E_NOTIMPL;
404     }
405
406     if (xioctl(capBox->fd, VIDIOCSPICT, &capBox->pict) == -1)
407     {
408         ERR("ioctl(VIDIOCSPICT) failed (%d)\n",errno);
409         return E_FAIL;
410     }
411     return S_OK;
412 }
413
414 static void renderer_RGB(Capture *capBox, LPBYTE bufferin, LPBYTE stream)
415 {
416     int depth = renderlist_V4l[capBox->pict.palette].depth;
417     int size = capBox->height * capBox->width * depth / 8;
418     int pointer, offset;
419
420     switch (depth)
421     {
422     case 24:
423         memcpy(bufferin, stream, size);
424         break;
425     case 32:
426         pointer = 0;
427         offset = 1;
428         while (pointer + offset <= size)
429         {
430             bufferin[pointer] = stream[pointer + offset];
431             pointer++;
432             bufferin[pointer] = stream[pointer + offset];
433             pointer++;
434             bufferin[pointer] = stream[pointer + offset];
435             pointer++;
436             offset++;
437         }
438         break;
439     default:
440         ERR("Unknown bit depth %d\n", depth);
441         return;
442     }
443 }
444
445 static void renderer_YUV(Capture *capBox, LPBYTE bufferin, LPBYTE stream)
446 {
447     enum YUV_Format format;
448
449     switch (capBox->pict.palette)
450     {
451     case  7: /* YUV422  -  same as YUYV */
452     case  8: /* YUYV    */
453         format = YUYV;
454         break;
455     case  9: /* UYVY    */
456         format = UYVY;
457         break;
458     case 11: /* YUV411  */
459         format = UYYVYY;
460         break;
461     case 13: /* YUV422P */
462         format = YUVP_421;
463         break;
464     case 14: /* YUV411P */
465         format = YUVP_441;
466         break;
467     case 15: /* YUV420P */
468         format = YUVP_422;
469         break;
470     case 16: /* YUV410P */
471         format = YUVP_444;
472         break;
473     default:
474         ERR("Unknown palette %d\n", capBox->pict.palette);
475         return;
476     }
477     YUV_To_RGB24(format, bufferin, stream, capBox->width, capBox->height);
478 }
479
480 static void Resize(Capture * capBox, LPBYTE output, LPBYTE input)
481 {
482     /* the whole image needs to be reversed,
483        because the dibs are messed up in windows */
484     if (!capBox->swresize)
485     {
486         int depth = capBox->bitDepth / 8;
487         int inoffset = 0, outoffset = capBox->height * capBox->width * depth;
488         int ow = capBox->width * depth;
489         while (outoffset > 0)
490         {
491             int x;
492             outoffset -= ow;
493             for (x = 0; x < ow; x++)
494                 output[outoffset + x] = input[inoffset + x];
495             inoffset += ow;
496         }
497     }
498     else
499     {
500         HDC dc_s, dc_d;
501         HBITMAP bmp_s, bmp_d;
502         int depth = capBox->bitDepth / 8;
503         int inoffset = 0, outoffset = (capBox->outputheight) * capBox->outputwidth * depth;
504         int ow = capBox->outputwidth * depth;
505         LPBYTE myarray;
506
507         /* FIXME: Improve software resizing: add error checks and optimize */
508
509         myarray = CoTaskMemAlloc(capBox->outputwidth * capBox->outputheight * depth);
510         dc_s = CreateCompatibleDC(NULL);
511         dc_d = CreateCompatibleDC(NULL);
512         bmp_s = CreateBitmap(capBox->width, capBox->height, 1, capBox->bitDepth, input);
513         bmp_d = CreateBitmap(capBox->outputwidth, capBox->outputheight, 1, capBox->bitDepth, NULL);
514         SelectObject(dc_s, bmp_s);
515         SelectObject(dc_d, bmp_d);
516         StretchBlt(dc_d, 0, 0, capBox->outputwidth, capBox->outputheight,
517                    dc_s, 0, 0, capBox->width, capBox->height, SRCCOPY);
518         GetBitmapBits(bmp_d, capBox->outputwidth * capBox->outputheight * depth, myarray);
519         while (outoffset > 0)
520         {
521             int i;
522
523             outoffset -= ow;
524             for (i = 0; i < ow; i++)
525                 output[outoffset + i] = myarray[inoffset + i];
526             inoffset += ow;
527         }
528         CoTaskMemFree(myarray);
529         DeleteObject(dc_s);
530         DeleteObject(dc_d);
531         DeleteObject(bmp_s);
532         DeleteObject(bmp_d);
533     }
534 }
535
536 static void V4l_GetFrame(Capture * capBox, unsigned char ** pInput)
537 {
538     if (capBox->pmap)
539     {
540         if (xioctl(capBox->fd, VIDIOCSYNC, &capBox->grab_buf[capBox->curframe]) == -1)
541             WARN("Syncing ioctl failed: %d\n", errno);
542
543         *pInput = capBox->pmap + capBox->gb_buffers.offsets[capBox->curframe];
544     }
545     else
546     {
547         int retval;
548         while ((retval = read(capBox->fd, capBox->grab_data, capBox->imagesize)) == -1)
549             if (errno != EAGAIN) break;
550         if (retval == -1)
551             WARN("Error occurred while reading from device: %s\n", strerror(errno));
552         *pInput = (unsigned char*) capBox->grab_data;
553     }
554 }
555
556 static void V4l_FreeFrame(Capture * capBox)
557 {
558     TRACE("\n");
559     if (capBox->mmap)
560     {
561         if (xioctl(capBox->fd, VIDIOCMCAPTURE, &capBox->grab_buf[capBox->curframe]) == -1)
562            ERR("Freeing frame for capture failed: %s\n", strerror(errno));
563     }
564     if (++capBox->curframe == capBox->buffers)
565         capBox->curframe = 0;
566 }
567
568 static DWORD WINAPI ReadThread(LPVOID lParam)
569 {
570     Capture * capBox = (Capture *)lParam;
571     HRESULT hr;
572     IMediaSample *pSample = NULL;
573     unsigned long framecount = 0;
574     unsigned char *pTarget, *pInput, *pOutput;
575
576     hr = V4l_Prepare(capBox);
577     if (FAILED(hr))
578         goto fail;
579
580     pOutput = CoTaskMemAlloc(capBox->width * capBox->height * capBox->bitDepth / 8);
581     capBox->curframe = 0;
582     do {
583         V4l_FreeFrame(capBox);
584     } while (capBox->curframe != 0);
585
586     while (1)
587     {
588         EnterCriticalSection(&capBox->CritSect);
589         if (capBox->stopped)
590             break;
591         hr = OutputPin_GetDeliveryBuffer((OutputPin *)capBox->pOut, &pSample, NULL, NULL, 0);
592         if (SUCCEEDED(hr))
593         {
594             int len;
595             
596             if (!capBox->swresize)
597                 len = capBox->height * capBox->width * capBox->bitDepth / 8;
598             else
599                 len = capBox->outputheight * capBox->outputwidth * capBox->bitDepth / 8;
600             IMediaSample_SetActualDataLength(pSample, len);
601
602             len = IMediaSample_GetActualDataLength(pSample);
603             TRACE("Data length: %d KB\n", len / 1024);
604
605             IMediaSample_GetPointer(pSample, &pTarget);
606             /* FIXME: Check return values.. */
607             V4l_GetFrame(capBox, &pInput);
608             capBox->renderer(capBox, pOutput, pInput);
609             Resize(capBox, pTarget, pOutput);
610             hr = OutputPin_SendSample((OutputPin *)capBox->pOut, pSample);
611             TRACE("%p -> Frame %lu: %x\n", capBox, ++framecount, hr);
612             IMediaSample_Release(pSample);
613             V4l_FreeFrame(capBox);
614         }
615         LeaveCriticalSection(&capBox->CritSect);
616         if (FAILED(hr) && hr != VFW_E_NOT_CONNECTED)
617         {
618             ERR("Received error: %x\n", hr);
619             goto cfail;
620         }
621     }
622     LeaveCriticalSection(&capBox->CritSect);
623     CoTaskMemFree(pOutput);
624
625     return 0;
626
627 cfail:
628     CoTaskMemFree(pOutput);
629     V4l_Unprepare(capBox);
630     LeaveCriticalSection(&capBox->CritSect);
631
632 fail:
633     capBox->thread = 0; capBox->stopped = 1;
634     FIXME("Stop IFilterGraph\n");
635     return 0;
636 }
637
638 HRESULT qcap_driver_run(Capture *capBox, FILTER_STATE *state)
639 {
640     HANDLE thread;
641     HRESULT hr;
642
643     TRACE("%p -> (%p)\n", capBox, state); 
644
645     if (*state == State_Running) return S_OK;
646
647     EnterCriticalSection(&capBox->CritSect);
648
649     capBox->stopped = 0;
650
651     if (*state == State_Stopped)
652     {
653         *state = State_Running;
654         if (!capBox->iscommitted++)
655         {
656             IMemAllocator * pAlloc = NULL;
657             ALLOCATOR_PROPERTIES ap, actual;
658             OutputPin *out;
659
660             ap.cBuffers = 3;
661             if (!capBox->swresize)
662                 ap.cbBuffer = capBox->width * capBox->height;
663             else
664                 ap.cbBuffer = capBox->outputwidth * capBox->outputheight;
665             ap.cbBuffer = (ap.cbBuffer * capBox->bitDepth) / 8;
666             ap.cbAlign = 1;
667             ap.cbPrefix = 0;
668
669             out = (OutputPin *)capBox->pOut;
670             hr = IMemInputPin_GetAllocator(out->pMemInputPin, &pAlloc);
671
672             if (SUCCEEDED(hr))
673                 hr = IMemAllocator_SetProperties(pAlloc, &ap, &actual);
674
675             if (SUCCEEDED(hr))
676                 hr = IMemAllocator_Commit(pAlloc);
677
678             if (pAlloc)
679                 IMemAllocator_Release(pAlloc);
680
681             TRACE("Committing allocator: %x\n", hr);
682         }
683
684         thread = CreateThread(NULL, 0, ReadThread, capBox, 0, NULL);
685         if (thread)
686         {
687             capBox->thread = thread;
688             SetThreadPriority(thread, THREAD_PRIORITY_LOWEST);
689             LeaveCriticalSection(&capBox->CritSect);
690             return S_OK;
691         }
692         ERR("Creating thread failed.. %u\n", GetLastError());
693         LeaveCriticalSection(&capBox->CritSect);
694         return E_FAIL;
695     }
696
697     ResumeThread(capBox->thread);
698     *state = State_Running;
699     LeaveCriticalSection(&capBox->CritSect);
700     return S_OK;
701 }
702
703 HRESULT qcap_driver_pause(Capture *capBox, FILTER_STATE *state)
704 {
705     TRACE("%p -> (%p)\n", capBox, state);     
706
707     if (*state == State_Paused)
708         return S_OK;
709     if (*state == State_Stopped)
710         qcap_driver_run(capBox, state);
711
712     EnterCriticalSection(&capBox->CritSect);
713     *state = State_Paused;
714     SuspendThread(capBox->thread);
715     LeaveCriticalSection(&capBox->CritSect);
716
717     return S_OK;
718 }
719
720 HRESULT qcap_driver_stop(Capture *capBox, FILTER_STATE *state)
721 {
722     TRACE("%p -> (%p)\n", capBox, state);
723
724     if (*state == State_Stopped)
725         return S_OK;
726
727     EnterCriticalSection(&capBox->CritSect);
728
729     if (capBox->thread)
730     {
731         if (*state == State_Paused)
732             ResumeThread(capBox->thread);
733         capBox->stopped = 1;
734         capBox->thread = 0;
735         if (capBox->iscommitted)
736         {
737             IMemInputPin *pMem = NULL;
738             IMemAllocator * pAlloc = NULL;
739             IPin *pConnect = NULL;
740             HRESULT hr;
741
742             capBox->iscommitted = 0;
743
744             hr = IPin_ConnectedTo(capBox->pOut, &pConnect);
745
746             if (SUCCEEDED(hr))
747                 hr = IPin_QueryInterface(pConnect, &IID_IMemInputPin, (void **) &pMem);
748
749             if (SUCCEEDED(hr))
750                 hr = IMemInputPin_GetAllocator(pMem, &pAlloc);
751
752             if (SUCCEEDED(hr))
753                 hr = IMemAllocator_Decommit(pAlloc);
754
755             if (pAlloc)
756                 IMemAllocator_Release(pAlloc);
757
758             if (pMem)
759                 IMemInputPin_Release(pMem);
760
761             if (pConnect)
762                 IPin_Release(pConnect);
763
764             if (hr != S_OK && hr != VFW_E_NOT_COMMITTED)
765                 WARN("Decommitting allocator: %x\n", hr);
766         }
767         V4l_Unprepare(capBox);
768     }
769
770     *state = State_Stopped;
771     LeaveCriticalSection(&capBox->CritSect);
772     return S_OK;
773 }
774
775 Capture * qcap_driver_init( IPin *pOut, USHORT card )
776 {
777     Capture * capBox = NULL;
778     char device[20];
779     struct video_capability capa;
780     struct video_picture pict;
781     struct video_window window;
782
783     YUV_Init();
784
785     capBox = CoTaskMemAlloc(sizeof(Capture));
786     if (!capBox)
787         goto error;
788
789     /* capBox->vtbl = &defboxVtbl; */
790
791     InitializeCriticalSection( &capBox->CritSect );
792
793     sprintf(device, "/dev/video%i", card);
794     TRACE("opening %s\n", device);
795     capBox->fd = open(device, O_RDWR | O_NONBLOCK);
796     if (capBox->fd == -1)
797     {
798         WARN("open failed (%d)\n", errno);
799         goto error;
800     }
801
802     memset(&capa, 0, sizeof(capa));
803
804     if (xioctl(capBox->fd, VIDIOCGCAP, &capa) == -1)
805     {
806         WARN("ioctl(VIDIOCGCAP) failed (%d)\n", errno);
807         goto error;
808     }
809
810     if (!(capa.type & VID_TYPE_CAPTURE))
811     {
812         WARN("not a video capture device\n");
813         goto error;
814     }
815
816     TRACE("%d inputs on %s\n", capa.channels, capa.name );
817
818     if (xioctl(capBox->fd, VIDIOCGPICT, &pict) == -1)
819     {
820         ERR("ioctl(VIDIOCGPICT) failed (%d)\n", errno );
821         goto error;
822     }
823
824     TRACE("depth %d palette %d (%s) hue %d color %d contrast %d\n",
825           pict.depth, pict.palette, renderlist_V4l[pict.palette].name,
826           pict.hue, pict.colour, pict.contrast );
827
828     capBox->dbrightness = pict.brightness;
829     capBox->dcolour = pict.colour;
830     capBox->dhue = pict.hue;
831     capBox->dcontrast = pict.contrast;
832
833     if (!renderlist_V4l[pict.palette].renderer)
834     {
835         int palet = pict.palette, i;
836
837         TRACE("No renderer available for %s, falling back to defaults\n",
838              renderlist_V4l[pict.palette].name);
839         capBox->renderer = NULL;
840         for (i = 0; fallback_V4l[i] >=0 ; i++)
841         {
842             int n = fallback_V4l[i];
843
844             if (renderlist_V4l[n].renderer == NULL)
845                 continue;
846
847             pict.depth = renderlist_V4l[n].depth;
848             pict.palette = n;
849             if (xioctl(capBox->fd, VIDIOCSPICT, &pict) == -1)
850             {
851                 TRACE("Could not render with %s (%d)\n",
852                       renderlist_V4l[n].name, n);
853                 continue;
854             }
855             TRACE("using renderer %s (%d)\n", 
856                   renderlist_V4l[n].name, n);
857             capBox->renderer = renderlist_V4l[n].renderer;
858             break;
859         }
860
861         if (!capBox->renderer)
862         {
863             ERR("video format %s isn't available\n",
864                  renderlist_V4l[palet].name);
865             goto error;
866         }
867     }
868     else
869     {
870         TRACE("Using the suggested format\n");
871         capBox->renderer = renderlist_V4l[pict.palette].renderer;
872     }
873     memcpy(&capBox->pict, &pict, sizeof(struct video_picture));
874
875     memset(&window, 0, sizeof(window));
876     if (xioctl(capBox->fd, VIDIOCGWIN, &window) == -1)
877     {
878         WARN("VIDIOCGWIN failed (%d)\n", errno);
879         goto error;
880     }
881
882     capBox->height = capBox->outputheight = window.height;
883     capBox->width = capBox->outputwidth = window.width;
884     capBox->swresize = FALSE;
885     capBox->bitDepth = 24;
886     capBox->pOut = pOut;
887     capBox->fps = 3;
888     capBox->stopped = 0;
889     capBox->curframe = 0;
890     capBox->iscommitted = 0;
891
892     TRACE("format: %d bits - %d x %d\n", capBox->bitDepth, capBox->width, capBox->height);
893
894     return (Capture*) capBox;
895
896 error:
897     if (capBox)
898         qcap_driver_destroy( (Capture*) capBox );
899
900     return NULL;
901 }
902
903 #else
904
905 Capture * qcap_driver_init( IPin *pOut, USHORT card )
906 {
907     const char msg[] = 
908         "The v4l headers were not available at compile time,\n"
909         "so video capture support is not available.\n";
910     MESSAGE(msg);
911     return NULL;
912 }
913
914 #define FAIL_WITH_ERR \
915     ERR("v4l absent: shouldn't be called\n"); \
916     return E_NOTIMPL
917
918 HRESULT qcap_driver_destroy(Capture *capBox)
919 {
920     FAIL_WITH_ERR;
921 }
922
923 HRESULT qcap_driver_set_format(Capture *capBox, AM_MEDIA_TYPE * mT)
924 {
925     FAIL_WITH_ERR;
926 }
927
928 HRESULT qcap_driver_get_format(Capture *capBox, AM_MEDIA_TYPE ** mT)
929 {
930     FAIL_WITH_ERR;
931 }
932
933 HRESULT qcap_driver_get_prop_range( Capture *capBox, long Property, long *pMin,
934           long *pMax, long *pSteppingDelta, long *pDefault, long *pCapsFlags )
935 {
936     FAIL_WITH_ERR;
937 }
938
939 HRESULT qcap_driver_get_prop(Capture *capBox, long Property, long *lValue, long *Flags)
940 {
941     FAIL_WITH_ERR;
942 }
943
944 HRESULT qcap_driver_set_prop(Capture *capBox, long Property, long lValue, long Flags)
945 {
946     FAIL_WITH_ERR;
947 }
948
949 HRESULT qcap_driver_run(Capture *capBox, FILTER_STATE *state)
950 {
951     FAIL_WITH_ERR;
952 }
953
954 HRESULT qcap_driver_pause(Capture *capBox, FILTER_STATE *state)
955 {
956     FAIL_WITH_ERR;
957 }
958
959 HRESULT qcap_driver_stop(Capture *capBox, FILTER_STATE *state)
960 {
961     FAIL_WITH_ERR;
962 }
963
964 #endif /* HAVE_LINUX_VIDEODEV_H */