]> sjero.net Git - linphone/blob - linphone/mediastreamer2/src/wincevideods.c
7160d601ebc5464f486631cbceefc76b238a4fad
[linphone] / linphone / mediastreamer2 / src / wincevideods.c
1 /*
2 mediastreamer2 library - modular sound and video processing and streaming
3 Copyright (C) 2006  Simon MORLAT (simon.morlat@linphone.org)
4
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either version 2
8 of the License, or (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18 */
19
20 #ifdef VIDEO_ENABLED
21
22 /* need to link with "dmoguids.lib strmiids.lib strmbase.lib atls.lib" */
23
24 #define _CE_ALLOW_SINGLE_THREADED_OBJECTS_IN_MTA
25
26 #include "mediastreamer2/msvideo.h"
27 #include "mediastreamer2/msticker.h"
28 #include "mediastreamer2/msv4l.h"
29
30 #include <aygshell.h>
31 #include "nowebcam.h"
32 #if 0
33 #include <ffmpeg/avcodec.h>
34 #endif
35
36 #include <dshow.h>
37 #include <dmodshow.h>
38 #include <dmoreg.h>
39
40 #include <streams.h>
41 //#include <initguid.h>
42 #include "dxfilter.h"
43 #if 0
44 #include <qedit.h>
45 #endif
46 #include <atlbase.h>
47 #include <atlcom.h>
48
49 typedef struct V4wState{
50
51         char dev[512];
52         int devidx;
53
54         CComPtr<IGraphBuilder> m_pGraph;
55         CComPtr<ICaptureGraphBuilder2> m_pBuilder;
56         CComPtr<IMediaControl> m_pControl;
57         CDXFilter *m_pDXFilter;
58         CComPtr<IBaseFilter> m_pIDXFilter;      
59         CComPtr<IBaseFilter> m_pNullRenderer;
60         CComPtr<IBaseFilter> m_pDeviceFilter;
61         DWORD rotregvalue;
62
63         MSVideoSize vsize;
64         int pix_fmt;
65         mblk_t *mire[10];
66         queue_t rq;
67         ms_mutex_t mutex;
68         int frame_ind;
69         int frame_max;
70         float fps;
71         float start_time;
72         int frame_count;
73         bool_t running;
74         bool_t startwith_yuv_bug; /* avoid bug with USB vimicro cards. */
75 }V4wState;
76
77 static V4wState *s_callback=NULL;
78
79 static void dummy(void*p){
80 }
81
82 HRESULT ( Callback)(IMediaSample* pSample, REFERENCE_TIME* sTime, REFERENCE_TIME* eTime, BOOL changed)
83 {
84         BYTE *byte_buf=NULL;
85         mblk_t *buf;
86
87         V4wState *s = s_callback;
88         if (s==NULL)
89                 return S_OK;
90
91         HRESULT hr = pSample->GetPointer(&byte_buf);
92         if (FAILED(hr))
93         {
94                 return S_OK;
95         }
96
97         int size = pSample->GetActualDataLength();
98         if (size>+1000)
99         {
100                 buf=allocb(size,0);
101                 memcpy(buf->b_wptr, byte_buf, size);
102                 if (s->pix_fmt==MS_RGB24)
103                 {
104                         /* Conversion from top down bottom up (BGR to RGB and flip) */
105                         unsigned long Index,nPixels;
106                         unsigned char *blue;
107                         unsigned char tmp;
108                         short iPixelSize;
109
110                         blue=buf->b_wptr;
111
112                         nPixels=s->vsize.width*s->vsize.height;
113                         iPixelSize=24/8;
114                  
115                         for(Index=0;Index!=nPixels;Index++)  // For each pixel
116                         {
117                                 tmp=*blue;
118                                 *blue=*(blue+2);
119                                 *(blue+2)=tmp;
120                                 blue+=iPixelSize;
121                         }
122  
123                         unsigned char *pLine1, *pLine2;
124                         int iLineLen,iIndex;
125
126                         iLineLen=s->vsize.width*iPixelSize;
127                         pLine1=buf->b_wptr;
128                         pLine2=&(buf->b_wptr)[iLineLen * (s->vsize.height - 1)];
129
130                         for( ;pLine1<pLine2;pLine2-=(iLineLen*2))
131                         {
132                                 for(iIndex=0;iIndex!=iLineLen;pLine1++,pLine2++,iIndex++)
133                                 {
134                                         tmp=*pLine1;
135                                         *pLine1=*pLine2;
136                                         *pLine2=tmp;       
137                                 }
138                         }
139                 }
140                 buf->b_wptr+=size;  
141                 
142                 ms_mutex_lock(&s->mutex);
143                 putq(&s->rq, buf);
144                 ms_mutex_unlock(&s->mutex);
145
146         }
147         return S_OK;
148 }
149
150 HRESULT GetFirstCameraDriver( WCHAR *pwzName ) {
151   HRESULT hr = S_OK;
152   HANDLE handle = NULL;
153   DEVMGR_DEVICE_INFORMATION di;
154   GUID guidCamera = { 0xCB998A05, 0x122C, 0x4166, 0x84, 0x6A,
155                       0x93, 0x3E, 0x4D, 0x7E, 0x3C, 0x86 };
156
157   if( pwzName == NULL ) {
158     return E_POINTER;
159   }
160
161   di.dwSize = sizeof(di);
162
163   handle = FindFirstDevice( DeviceSearchByGuid, &guidCamera, &di );
164   if(( handle == NULL ) || ( di.hDevice == NULL )) {
165           return S_FALSE;
166   }
167
168   StringCchCopy( pwzName, MAX_PATH, di.szLegacyName );
169  
170   FindClose( handle );
171   return hr;
172 }
173
174 struct VAR_LIST
175 {
176     VARIANT var;
177     VAR_LIST *pNext;
178     BSTR pBSTRName;
179 };
180
181 class CPropertyBag : public IPropertyBag
182 {  
183 public:
184     CPropertyBag();
185     ~CPropertyBag();
186     
187     HRESULT STDMETHODCALLTYPE
188     Read(
189         LPCOLESTR pszPropName, 
190         VARIANT *pVar, 
191         IErrorLog *pErrorLog
192         );
193     
194     
195     HRESULT STDMETHODCALLTYPE
196     Write(
197         LPCOLESTR pszPropName, 
198         VARIANT *pVar
199         );
200         
201     ULONG STDMETHODCALLTYPE AddRef();        
202     ULONG STDMETHODCALLTYPE Release();        
203     HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppv);   
204
205 private:
206      ULONG _refCount;
207      VAR_LIST *pVar;
208 };
209
210 CPropertyBag::CPropertyBag() : _refCount(1), pVar(0)
211 {       
212 }
213
214 CPropertyBag::~CPropertyBag()
215 {
216     VAR_LIST *pTemp = pVar;
217     HRESULT hr = S_OK;
218     
219     while(pTemp)
220     {
221         VAR_LIST *pDel = pTemp;
222         VariantClear(&pTemp->var);
223         SysFreeString(pTemp->pBSTRName);
224         pTemp = pTemp->pNext;
225         delete pDel;
226     }
227
228 }
229
230 HRESULT STDMETHODCALLTYPE
231 CPropertyBag::Read(LPCOLESTR pszPropName, 
232                        VARIANT *_pVar, 
233                        IErrorLog *pErrorLog)
234 {
235     VAR_LIST *pTemp = pVar;
236     HRESULT hr = S_OK;
237     
238     while(pTemp)
239     {
240         if(0 == wcscmp(pszPropName, pTemp->pBSTRName))
241         {
242             hr = VariantCopy(_pVar, &pTemp->var);
243             break;
244         }
245         pTemp = pTemp->pNext;
246     }
247     return hr;
248 }
249
250
251 HRESULT STDMETHODCALLTYPE
252 CPropertyBag::Write(LPCOLESTR pszPropName, 
253                             VARIANT *_pVar)
254 {
255     HRESULT hr = S_OK;
256     VAR_LIST *pTemp = new VAR_LIST();
257     ASSERT(pTemp);
258
259     if( !pTemp )
260     {
261         return E_OUTOFMEMORY;
262     }
263
264     VariantInit(&pTemp->var);
265     pTemp->pBSTRName = SysAllocString(pszPropName);
266     pTemp->pNext = pVar;
267     pVar = pTemp;
268     return VariantCopy(&pTemp->var, _pVar);
269 }
270
271 ULONG STDMETHODCALLTYPE 
272 CPropertyBag::AddRef() 
273 {
274     return InterlockedIncrement((LONG *)&_refCount);
275 }
276
277 ULONG STDMETHODCALLTYPE 
278 CPropertyBag::Release() 
279 {
280     ASSERT(_refCount != 0xFFFFFFFF);
281     ULONG ret = InterlockedDecrement((LONG *)&_refCount);    
282     if(!ret) 
283         delete this; 
284     return ret;
285 }
286
287 HRESULT STDMETHODCALLTYPE 
288 CPropertyBag::QueryInterface(REFIID riid, void** ppv) 
289 {
290     if(!ppv) 
291         return E_POINTER;
292     if(riid == IID_IPropertyBag) 
293         *ppv = static_cast<IPropertyBag*>(this);        
294     else 
295         return *ppv = 0, E_NOINTERFACE;
296     
297     return AddRef(), S_OK;      
298 }
299
300 static int v4w_open_videodevice(V4wState *s, int format, MSVideoSize *vsize)
301 {
302         // Initialize COM
303         CoInitialize(NULL);
304
305         // get a Graph
306         HRESULT hr=s->m_pGraph.CoCreateInstance(CLSID_FilterGraph);
307         if(FAILED(hr))
308         {
309                 return -1;
310         }
311
312         // get a CaptureGraphBuilder2
313 #if !defined(_WIN32_WCE)
314         hr=s->m_pBuilder.CoCreateInstance(CLSID_CaptureGraphBuilder2);
315 #else
316         hr=s->m_pBuilder.CoCreateInstance(CLSID_CaptureGraphBuilder);
317 #endif
318         if(FAILED(hr))
319         {
320                 return -2;
321         }
322
323         // connect capture graph builder with the graph
324         s->m_pBuilder->SetFiltergraph(s->m_pGraph);
325
326         // get mediacontrol so we can start and stop the filter graph
327         hr=s->m_pGraph.QueryInterface(&(s->m_pControl));
328         if(FAILED(hr))
329         {
330                 return -3;
331         }
332
333         // get DXFilter
334         s->m_pDXFilter = new CDXFilter(NULL, &hr, FALSE);
335         if(s->m_pDXFilter==NULL)
336         {
337                 return -4;
338         }
339         s->m_pDXFilter->AddRef();
340         if(FAILED(hr))
341         {
342                 return -4;
343         }
344
345         CMediaType mt;
346         mt.SetType(&MEDIATYPE_Video);
347
348         if (format==MS_YUV420P)
349         {
350                 GUID m = (GUID)FOURCCMap(MAKEFOURCC('I','4','2','0'));
351                 mt.SetSubtype(&m);
352                 mt.SetSubtype(&MEDIASUBTYPE_YV12);
353         }
354         else //if (format==MS_RGB24)
355         {
356                 mt.SetSubtype(&MEDIASUBTYPE_RGB24);
357         }
358
359         //mt.SetSubtype(&MEDIASUBTYPE_IYUV);
360         //mt.SetSubtype(&MEDIASUBTYPE_YUYV);
361         //mt.SetSubtype(&MEDIASUBTYPE_RGB24);
362         //mt.SetSampleSize();
363         mt.formattype = FORMAT_VideoInfo;
364         mt.SetTemporalCompression(FALSE);
365
366         VIDEOINFO *pvi = (VIDEOINFO *)
367         mt.AllocFormatBuffer(sizeof(VIDEOINFO));
368         if (NULL == pvi)
369                 return E_OUTOFMEMORY;
370         ZeroMemory(pvi, sizeof(VIDEOINFO));
371         if (format==MS_YUV420P)
372         {
373                 pvi->bmiHeader.biCompression = MAKEFOURCC('I','4','2','0');
374                 pvi->bmiHeader.biCompression = MAKEFOURCC('Y','V','1','2');
375                 pvi->bmiHeader.biBitCount = 12;
376         }
377         else
378         {
379                 pvi->bmiHeader.biCompression = BI_RGB;
380                 pvi->bmiHeader.biBitCount = 24;
381         }
382         pvi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
383         pvi->bmiHeader.biWidth = vsize->width;
384         pvi->bmiHeader.biHeight = vsize->height;
385         pvi->bmiHeader.biPlanes = 1;
386         pvi->bmiHeader.biSizeImage = GetBitmapSize(&pvi->bmiHeader);
387         pvi->bmiHeader.biClrImportant = 0;
388         mt.SetSampleSize(pvi->bmiHeader.biSizeImage);
389         mt.SetFormat((BYTE*)pvi, sizeof(VIDEOINFO));
390
391         hr = s->m_pDXFilter->SetAcceptedMediaType(&mt);
392         if(FAILED(hr))
393         {
394                 return -5;
395         }
396
397         hr = s->m_pDXFilter->SetCallback(Callback); 
398         if(FAILED(hr))
399         {
400                 return -6;
401         }
402
403         hr = s->m_pDXFilter->QueryInterface(IID_IBaseFilter,
404          (LPVOID *)&s->m_pIDXFilter);
405         if(FAILED(hr))
406         {
407                 return -7;
408         }
409
410         hr = s->m_pGraph->AddFilter(s->m_pIDXFilter, L"DXFilter Filter");
411         if(FAILED(hr))
412         {
413                 return -8;
414         }
415
416 #ifdef WM6
417         ICreateDevEnum *pCreateDevEnum = NULL;
418         IEnumMoniker *pEnumMoniker = NULL;
419         IMoniker *pMoniker = NULL;
420
421         ULONG nFetched = 0;
422
423         hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, 
424                 IID_ICreateDevEnum, (PVOID *)&pCreateDevEnum);
425         if(FAILED(hr))
426         {
427                 return -9;
428         }
429
430         hr = pCreateDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory,
431                 &pEnumMoniker, 0);
432         if (FAILED(hr) || pEnumMoniker == NULL) {
433                 //printf("no device\n");
434                 return -10;
435         }
436
437         pEnumMoniker->Reset();
438
439         hr = pEnumMoniker->Next(1, &pMoniker, &nFetched);
440         if(FAILED(hr) || pMoniker==NULL)
441         {
442                 return -11;
443         }
444
445         hr = pMoniker->BindToObject(0, 0, IID_IBaseFilter, (void**)&s->m_pDeviceFilter );
446         if(FAILED(hr))
447         {
448                 return -12;
449         }
450
451         s->m_pGraph->AddFilter(s->m_pDeviceFilter, L"Device Filter");
452
453         pMoniker->Release();
454         pEnumMoniker->Release();
455         pCreateDevEnum->Release();
456 #else
457         WCHAR wzDeviceName[ MAX_PATH + 1 ];
458         CComVariant   varCamName;
459         CPropertyBag PropBag;
460     CComPtr<IPersistPropertyBag>    pPropertyBag;
461         GetFirstCameraDriver(wzDeviceName);
462
463         hr = s->m_pDeviceFilter.CoCreateInstance( CLSID_VideoCapture ); 
464         if (FAILED(hr))
465         {
466                 return -8;
467         }
468
469         s->m_pDeviceFilter.QueryInterface( &pPropertyBag );
470         varCamName = wzDeviceName;
471         if(( varCamName.vt == VT_BSTR ) == NULL ) {
472           return E_OUTOFMEMORY;
473         }
474         PropBag.Write( L"VCapName", &varCamName );   
475         pPropertyBag->Load( &PropBag, NULL );
476         pPropertyBag.Release();
477
478         hr = s->m_pGraph->AddFilter( s->m_pDeviceFilter, L"Video capture source" );
479 #endif
480
481         if (FAILED(hr))
482         {
483                 return -8;
484         }
485
486         // get null renderer
487         s->m_pNullRenderer = NULL;
488 #if 0
489         hr=s->m_pNullRenderer.CoCreateInstance(CLSID_NullRenderer);
490         if(FAILED(hr))
491         {
492                 return -13;
493         }
494 #endif
495         if (s->m_pNullRenderer!=NULL)
496         {
497                 s->m_pGraph->AddFilter(s->m_pNullRenderer, L"Null Renderer");
498         }
499
500         hr = s->m_pBuilder->RenderStream(&PIN_CATEGORY_PREVIEW,
501                 &MEDIATYPE_Video, s->m_pDeviceFilter, s->m_pIDXFilter, s->m_pNullRenderer);
502         if (FAILED(hr))
503         {
504                 //hr = s->m_pBuilder->RenderStream(&PIN_CATEGORY_CAPTURE,
505                 //      &MEDIATYPE_Video, s->m_pDeviceFilter, s->m_pIDXFilter, s->m_pNullRenderer);
506                 if (FAILED(hr))
507                 {
508                         return -14;
509                 }
510         }
511         
512         //m_pDXFilter->SetBufferSamples(TRUE);
513
514
515                 // Create the System Device Enumerator.
516 IFilterMapper *pMapper = NULL;
517 //IEnumMoniker *pEnum = NULL;
518 IEnumRegFilters *pEnum = NULL;
519
520 hr = CoCreateInstance(CLSID_FilterMapper, 
521     NULL, CLSCTX_INPROC, IID_IFilterMapper, 
522     (void **) &pMapper);
523
524 if (FAILED(hr))
525 {
526     // Error handling omitted for clarity.
527 }
528
529 GUID arrayInTypes[2];
530 arrayInTypes[0] = MEDIATYPE_Video;
531 arrayInTypes[1] = MEDIASUBTYPE_dvsd;
532
533 hr = pMapper->EnumMatchingFilters(
534         &pEnum,
535         MERIT_HW_COMPRESSOR, // Minimum merit.
536         FALSE,               // At least one input pin?
537         MEDIATYPE_NULL,
538         MEDIASUBTYPE_NULL,
539         FALSE,              // Must be a renderer?
540         FALSE,               // At least one output pin?
541         MEDIATYPE_NULL,                  
542         MEDIASUBTYPE_NULL);              
543
544 // Enumerate the monikers.
545 //IMoniker *pMoniker;
546 REGFILTER *pMoniker;
547 ULONG cFetched;
548 while (pEnum->Next(1, &pMoniker, &cFetched) == S_OK)
549 {
550     IPropertyBag *pPropBag = NULL;
551 #if 0
552         hr = pMoniker->BindToStorage(0, 0, IID_IPropertyBag, 
553        (void **)&pPropBag);
554
555     if (SUCCEEDED(hr))
556     {
557         // To retrieve the friendly name of the filter, do the following:
558         VARIANT varName;
559         VariantInit(&varName);
560         hr = pPropBag->Read(L"FriendlyName", &varName, 0);
561         if (SUCCEEDED(hr))
562         {
563             // Display the name in your UI somehow.
564         }
565         VariantClear(&varName);
566
567         // To create an instance of the filter, do the following:
568         IBaseFilter *pFilter;
569         hr = pMoniker->BindToObject(NULL, NULL, IID_IBaseFilter, (void**)&pFilter);
570         // Now add the filter to the graph. Remember to release pFilter later.
571     
572         // Clean up.
573         pPropBag->Release();
574     }
575     pMoniker->Release();
576 #endif
577
578 }
579
580 // Clean up.
581 pMapper->Release();
582 pEnum->Release();
583
584
585
586
587         s_callback = s;
588         hr = s->m_pControl->Run();
589         if(FAILED(hr))
590         {
591                 return -15;
592         }
593
594         s->rotregvalue=1;
595         s->pix_fmt = format;
596         s->vsize.height = vsize->height;
597         s->vsize.width = vsize->width;
598         return 0;
599 }
600
601 static void v4w_init(MSFilter *f){
602         V4wState *s=(V4wState *)ms_new0(V4wState,1);
603         int idx;
604         s->vsize.width=MS_VIDEO_SIZE_CIF_W;
605         s->vsize.height=MS_VIDEO_SIZE_CIF_H;
606         //s->pix_fmt=MS_RGB24;
607         s->pix_fmt=MS_YUV420P;
608
609         s->rotregvalue = 0;
610         s->m_pGraph=NULL;
611         s->m_pBuilder=NULL;
612         s->m_pControl=NULL;
613         s->m_pDXFilter=NULL;
614         s->m_pIDXFilter=NULL;
615         s->m_pDeviceFilter=NULL;
616
617         qinit(&s->rq);
618         for (idx=0;idx<10;idx++)
619         {
620                 s->mire[idx]=NULL;
621         }
622         ms_mutex_init(&s->mutex,NULL);
623         s->start_time=0;
624         s->frame_count=-1;
625         s->fps=15;
626
627         f->data=s;
628 }
629
630 static int try_format(V4wState *s, int format, MSVideoSize *vsize)
631 {
632         int i = v4w_open_videodevice(s, format, vsize);
633         if (i==-14)
634         {
635                 if (s->m_pNullRenderer!=NULL)
636                         s->m_pGraph->RemoveFilter(s->m_pNullRenderer);
637                 if (s->m_pIDXFilter!=NULL)
638                         s->m_pGraph->RemoveFilter(s->m_pIDXFilter);
639                 if (s->m_pDeviceFilter!=NULL)
640                         s->m_pGraph->RemoveFilter(s->m_pDeviceFilter);
641                 s->m_pBuilder=NULL;
642                 s->m_pControl=NULL;
643                 s->m_pIDXFilter=NULL;
644                 if (s->m_pDXFilter!=NULL)
645                         s->m_pDXFilter->Release();
646                 s->m_pDXFilter=NULL;
647                 s->m_pGraph=NULL;
648                 s->m_pNullRenderer=NULL;
649                 s->m_pDeviceFilter=NULL;
650                 CoUninitialize();
651         }
652         return i;
653 }
654
655 static int _v4w_start(V4wState *s, void *arg)
656 {
657         MSVideoSize try_vsize;
658         int tryformat;
659         int i;
660         s->frame_count=-1;
661
662         if (s->pix_fmt==MS_YUV420P)
663                 tryformat = MS_RGB24;
664         else if (s->pix_fmt==MS_RGB24)
665                 tryformat = MS_YUV420P;
666
667         try_vsize.height = s->vsize.height;
668         try_vsize.width = s->vsize.width;
669         i = try_format(s, s->pix_fmt, &try_vsize);
670         if (i==-14)
671         {
672                 /* try second format with same size */
673                 i = try_format(s, tryformat, &try_vsize);
674         }
675
676         /* try both format with CIF size */
677         if (i==-14 && s->vsize.height!=MS_VIDEO_SIZE_CIF_H)
678         {
679                 try_vsize.height = MS_VIDEO_SIZE_CIF_H;
680                 try_vsize.width = MS_VIDEO_SIZE_CIF_W;
681                 i = try_format(s, s->pix_fmt, &try_vsize);
682                 if (i==-14)
683                 {
684                         i = try_format(s, tryformat, &try_vsize);
685                 }
686         }
687         if (i==-14 && s->vsize.height!=MS_VIDEO_SIZE_QCIF_H)
688         {
689                 try_vsize.height = MS_VIDEO_SIZE_QCIF_H;
690                 try_vsize.width = MS_VIDEO_SIZE_QCIF_W;
691                 i = try_format(s, s->pix_fmt, &try_vsize);
692                 if (i==-14)
693                 {
694                         i = try_format(s, tryformat, &try_vsize);
695                 }
696         }
697         if (i==-14 && s->vsize.height!=MS_VIDEO_SIZE_VGA_H)
698         {
699                 try_vsize.height = MS_VIDEO_SIZE_VGA_H;
700                 try_vsize.width = MS_VIDEO_SIZE_VGA_W;
701                 i = try_format(s, s->pix_fmt, &try_vsize);
702                 if (i==-14)
703                 {
704                         i = try_format(s, tryformat, &try_vsize);
705                 }
706         }
707
708         if (i==-14 && s->vsize.height!=MS_VIDEO_SIZE_QVGA_H)
709         {
710                 try_vsize.height = MS_VIDEO_SIZE_QVGA_H;
711                 try_vsize.width = MS_VIDEO_SIZE_QVGA_W;
712                 i = try_format(s, s->pix_fmt, &try_vsize);
713                 if (i==-14)
714                 {
715                         i = try_format(s, tryformat, &try_vsize);
716                 }
717         }
718
719         if (i==0)
720         {
721                 if (s->pix_fmt==MS_YUV420P)
722                         ms_message("Using YUV420P");
723                 else if (s->pix_fmt==MS_RGB24)
724                         ms_message("Using RGB24");
725         }
726
727         if (s->rotregvalue==0){
728                 //RemoveGraphFromRot(s->rotregvalue);           
729                 if (s->m_pNullRenderer!=NULL)
730                         s->m_pGraph->RemoveFilter(s->m_pNullRenderer);
731                 if (s->m_pIDXFilter!=NULL)
732                         s->m_pGraph->RemoveFilter(s->m_pIDXFilter);
733                 if (s->m_pDeviceFilter!=NULL)
734                         s->m_pGraph->RemoveFilter(s->m_pDeviceFilter);
735                 s->m_pBuilder=NULL;
736                 s->m_pControl=NULL;
737                 s->m_pIDXFilter=NULL;
738                 if (s->m_pDXFilter!=NULL)
739                         s->m_pDXFilter->Release();
740                 s->m_pDXFilter=NULL;
741                 s->m_pGraph=NULL;
742                 s->m_pNullRenderer=NULL;
743                 s->m_pDeviceFilter=NULL;
744                 CoUninitialize();
745                 s_callback = NULL;
746                 flushq(&s->rq,0);
747                 ms_message("v4w: graph not started (err=%i)", i);
748                 s->rotregvalue=0;
749         }
750         return i;
751 }
752
753 static int _v4w_stop(V4wState *s, void *arg){
754         s->frame_count=-1;
755         if (s->rotregvalue>0){
756                 HRESULT hr = s->m_pControl->Stop();
757                 if(FAILED(hr))
758                 {
759                         ms_message("v4w: could not stop graph");
760                 }
761                 if (s->m_pNullRenderer!=NULL)
762                         s->m_pGraph->RemoveFilter(s->m_pNullRenderer);
763                 if (s->m_pIDXFilter!=NULL)
764                         s->m_pGraph->RemoveFilter(s->m_pIDXFilter);
765                 if (s->m_pDeviceFilter!=NULL)
766                         s->m_pGraph->RemoveFilter(s->m_pDeviceFilter);
767                 //RemoveGraphFromRot(s->rotregvalue);
768                 s->m_pBuilder=NULL;
769                 s->m_pControl=NULL;
770                 s->m_pIDXFilter=NULL;
771                 if (s->m_pDXFilter!=NULL)
772                         s->m_pDXFilter->Release();
773                 s->m_pDXFilter=NULL;
774                 s->m_pGraph=NULL;
775                 s->m_pNullRenderer=NULL;
776                 s->m_pDeviceFilter=NULL;
777                 CoUninitialize();
778                 s_callback = NULL;
779                 flushq(&s->rq,0);
780                 ms_message("v4w: graph destroyed");
781                 s->rotregvalue=0;
782         }
783         return 0;
784 }
785
786
787 static int v4w_start(MSFilter *f, void *arg){
788         V4wState *s=(V4wState*)f->data;
789         _v4w_start(s, NULL);
790         return 0;
791 }
792
793 static int v4w_stop(MSFilter *f, void *arg){
794         V4wState *s=(V4wState*)f->data;
795         _v4w_stop(s, NULL);
796         return 0;
797 }
798
799
800 static void v4w_uninit(MSFilter *f){
801         V4wState *s=(V4wState*)f->data;
802         int idx;
803         flushq(&s->rq,0);
804         ms_mutex_destroy(&s->mutex);
805         for (idx=0;idx<10;idx++)
806         {
807                 if (s->mire[idx]==NULL)
808                         break;
809                 freemsg(s->mire[idx]);
810         }
811         if (s->rotregvalue>0){
812                 HRESULT hr = s->m_pControl->Stop();
813                 if(FAILED(hr))
814                 {
815                         ms_message("v4w: could not stop graph");
816                 }
817                 if (s->m_pNullRenderer!=NULL)
818                         s->m_pGraph->RemoveFilter(s->m_pNullRenderer);
819                 if (s->m_pIDXFilter!=NULL)
820                         s->m_pGraph->RemoveFilter(s->m_pIDXFilter);
821                 if (s->m_pDeviceFilter!=NULL)
822                         s->m_pGraph->RemoveFilter(s->m_pDeviceFilter);
823                 //RemoveGraphFromRot(s->rotregvalue);
824                 s->m_pBuilder=NULL;
825                 s->m_pControl=NULL;
826                 s->m_pIDXFilter=NULL;
827                 if (s->m_pDXFilter!=NULL)
828                         s->m_pDXFilter->Release();
829                 s->m_pDXFilter=NULL;
830                 s->m_pGraph=NULL;
831                 s->m_pNullRenderer=NULL;
832                 s->m_pDeviceFilter=NULL;
833                 CoUninitialize();
834                 s_callback = NULL;
835                 flushq(&s->rq,0);
836                 ms_message("v4w: graph destroyed");
837                 s->rotregvalue=0;
838         }
839         ms_free(s);
840 }
841
842 static mblk_t * v4w_make_nowebcam(V4wState *s){
843 #if defined(_WIN32_WCE)
844         return NULL;
845 #else
846         int idx;
847         int count;
848         if (s->mire[0]==NULL && s->frame_ind==0){
849                 /* load several images to fake a movie */
850                 for (idx=0;idx<10;idx++)
851                 {
852                         s->mire[idx]=ms_load_nowebcam(&s->vsize, idx);
853                         if (s->mire[idx]==NULL)
854                                 break;
855                 }
856                 if (idx==0)
857                         s->mire[0]=ms_load_nowebcam(&s->vsize, -1);
858         }
859         for (count=0;count<10;count++)
860         {
861                 if (s->mire[count]==NULL)
862                         break;
863         }
864
865         s->frame_ind++;
866         if (count==0)
867                 return NULL;
868
869         idx = s->frame_ind%count;
870         if (s->mire[idx]!=NULL)
871                 return s->mire[idx];
872         return s->mire[0];
873 #endif
874 }
875
876 static void v4w_preprocess(MSFilter * obj){
877         V4wState *s=(V4wState*)obj->data;
878         s->running=TRUE;
879         if (s->rotregvalue==0)
880                 s->fps=1;
881 }
882
883 static void v4w_postprocess(MSFilter * obj){
884         V4wState *s=(V4wState*)obj->data;
885         s->running=FALSE;
886 }
887
888 static void v4w_process(MSFilter * obj){
889         V4wState *s=(V4wState*)obj->data;
890         mblk_t *m;
891         uint32_t timestamp;
892         int cur_frame;
893
894         if (s->frame_count==-1){
895                 s->start_time=obj->ticker->time;
896                 s->frame_count=0;
897         }
898
899
900         cur_frame=((obj->ticker->time-s->start_time)*s->fps/1000.0);
901         if (cur_frame>s->frame_count){
902                 mblk_t *om=NULL;
903                 ms_mutex_lock(&s->mutex);
904                 /*keep the most recent frame if several frames have been captured */
905                 if (s->rotregvalue!=0){
906                         while((m=getq(&s->rq))!=NULL){
907                                 if (om!=NULL) freemsg(om);
908                                 om=m;
909                         }
910                 }else {
911                         mblk_t *nowebcam = v4w_make_nowebcam(s);
912                         if (nowebcam!=NULL)
913                                 om=dupmsg(nowebcam);
914                 }
915                 ms_mutex_unlock(&s->mutex);
916                 if (om!=NULL){
917                         timestamp=obj->ticker->time*90;/* rtp uses a 90000 Hz clockrate for video*/
918                         mblk_set_timestamp_info(om,timestamp);
919                         ms_queue_put(obj->outputs[0],om);
920                         /*ms_message("picture sent");*/
921                 }
922                 s->frame_count++;
923         }
924 }
925
926
927
928 static int v4w_set_fps(MSFilter *f, void *arg){
929         V4wState *s=(V4wState*)f->data;
930         s->fps=*((float*)arg);
931         return 0;
932 }
933
934 static int v4w_get_pix_fmt(MSFilter *f,void *arg){
935         V4wState *s=(V4wState*)f->data;
936         *((MSPixFmt*)arg) = (MSPixFmt)s->pix_fmt;
937         return 0;
938 }
939
940 static int v4w_set_vsize(MSFilter *f, void *arg){
941         V4wState *s=(V4wState*)f->data;
942         s->vsize=*((MSVideoSize*)arg);
943         return 0;
944 }
945
946 static int v4w_get_vsize(MSFilter *f, void *arg){
947         V4wState *s=(V4wState*)f->data;
948         MSVideoSize *vs=(MSVideoSize*)arg;
949         vs->width=s->vsize.width;
950         vs->height=s->vsize.height;
951         return 0;
952 }
953
954 static MSFilterMethod methods[]={
955         {       MS_FILTER_SET_FPS       ,       v4w_set_fps     },
956         {       MS_FILTER_GET_PIX_FMT   ,       v4w_get_pix_fmt },
957         {       MS_FILTER_SET_VIDEO_SIZE, v4w_set_vsize },
958         {       MS_FILTER_GET_VIDEO_SIZE, v4w_get_vsize },
959         {       MS_V4L_START                    ,       v4w_start               },
960         {       MS_V4L_STOP                     ,       v4w_stop                },
961         {       0                                                               ,       NULL                    }
962 };
963
964 #ifdef _MSC_VER
965
966 MSFilterDesc ms_v4w_desc={
967         MS_V4L_ID,
968         "MSV4w",
969         N_("A video4windows compatible source filter to stream pictures."),
970         MS_FILTER_OTHER,
971         NULL,
972         0,
973         1,
974         v4w_init,
975         v4w_preprocess,
976         v4w_process,
977         v4w_postprocess,
978         v4w_uninit,
979         methods
980 };
981
982 #else
983
984 MSFilterDesc ms_v4w_desc={
985         .id=MS_V4L_ID,
986         .name="MSV4w",
987         .text=N_("A video4windows compatible source filter to stream pictures."),
988         .ninputs=0,
989         .noutputs=1,
990         .category=MS_FILTER_OTHER,
991         .init=v4w_init,
992         .preprocess=v4w_preprocess,
993         .process=v4w_process,
994         .postprocess=v4w_postprocess,
995         .uninit=v4w_uninit,
996         .methods=methods
997 };
998
999 #endif
1000
1001 MS_FILTER_DESC_EXPORT(ms_v4w_desc)
1002
1003 #endif