source: mediastreamer2/linphone/mediastreamer2/src/winvideo.c @ 41:528e9ebd8b36

Last change on this file since 41:528e9ebd8b36 was 41:528e9ebd8b36, checked in by smorlat <smorlat@…>, 5 years ago
  • on windows, start capture using driver's default pixel format.

It appears some drivers don't like trying multiple formats.

git-svn-id: svn+ssh://svn.savannah.nongnu.org/linphone/trunk@44 3f6dc0c8-ddfe-455d-9043-3cd528dc4637

File size: 15.1 KB
Line 
1/*
2mediastreamer2 library - modular sound and video processing and streaming
3Copyright (C) 2006  Simon MORLAT (simon.morlat@linphone.org)
4
5This program is free software; you can redistribute it and/or
6modify it under the terms of the GNU General Public License
7as published by the Free Software Foundation; either version 2
8of the License, or (at your option) any later version.
9
10This program is distributed in the hope that it will be useful,
11but WITHOUT ANY WARRANTY; without even the implied warranty of
12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with this program; if not, write to the Free Software
17Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18*/
19
20#include "mediastreamer2/msvideo.h"
21#include "mediastreamer2/msticker.h"
22#include "mediastreamer2/msv4l.h"
23#include "Vfw.h"
24#include <winuser.h>
25#include <Windows.h>
26
27#include "nowebcam.h"
28#include "mediastreamer2/mswebcam.h"
29
30#ifndef _MSC_VER
31#include "vfw-missing.h"
32#endif
33
34#define AMD_HACK2
35
36typedef struct V4wState{
37#ifdef AMD_HACK2
38        ms_thread_t thread;
39        ms_mutex_t thread_lock;
40        ms_cond_t thread_cond;
41        bool_t thread_running;
42#endif
43
44        char dev[512];
45        int devidx;
46        HWND capvideo;
47        MSVideoSize vsize;
48        int pix_fmt;
49        mblk_t *mire[10];
50        queue_t rq;
51        ms_mutex_t mutex;
52        int frame_ind;
53        int frame_max;
54        float fps;
55        float start_time;
56        int frame_count;
57        bool_t running;
58        bool_t startwith_yuv_bug; /* avoid bug with USB vimicro cards. */
59        bool_t started;
60        bool_t autostarted;
61}V4wState;
62
63static void dummy(void*p){
64}
65
66LRESULT CALLBACK VideoStreamCallback(HWND hWnd, LPVIDEOHDR lpVHdr)
67{
68        V4wState *s;
69        mblk_t *buf;
70        int size;
71       
72        s = (V4wState *)capGetUserData(hWnd);
73        if (s==NULL)
74                return FALSE;
75
76        size = lpVHdr->dwBufferLength;
77        if (size>0 && s->running){
78                buf = esballoc(lpVHdr->lpData,size,0,dummy);
79                buf->b_wptr+=size; 
80               
81                ms_mutex_lock(&s->mutex);
82                putq(&s->rq, buf);
83                ms_mutex_unlock(&s->mutex);
84        }
85        return TRUE ;
86}
87
88static bool_t try_format(V4wState *s, BITMAPINFO *videoformat, MSPixFmt pixfmt){
89        capGetVideoFormat(s->capvideo, videoformat, sizeof(BITMAPINFO));
90        videoformat->bmiHeader.biSizeImage = 0;
91        videoformat->bmiHeader.biWidth  = s->vsize.width;
92        videoformat->bmiHeader.biHeight = s->vsize.height;
93        switch(pixfmt){
94                case MS_YUV420P:
95                        videoformat->bmiHeader.biBitCount = 12;
96                        videoformat->bmiHeader.biCompression=MAKEFOURCC('I','4','2','0');
97                break;
98                case MS_YUY2:
99                        videoformat->bmiHeader.biBitCount = 16;
100                        videoformat->bmiHeader.biCompression=MAKEFOURCC('Y','U','Y','2');
101                break;
102                case MS_RGB24:
103                        videoformat->bmiHeader.biBitCount = 24;
104                        videoformat->bmiHeader.biCompression=BI_RGB;
105                break;
106                default:
107                        return FALSE;
108        }
109        return capSetVideoFormat(s->capvideo, videoformat, sizeof(BITMAPINFO));
110}
111
112static int v4w_open_videodevice(V4wState *s)
113{
114        CAPTUREPARMS capparam ;
115        BITMAPINFO videoformat;
116        char compname[5];
117        int i;
118        MSPixFmt driver_last;
119        char dev[80];
120        char ver[80];
121        compname[4]='\0';
122
123        for (i = 0; i < 9; i++){
124                if (capGetDriverDescription(i, dev, sizeof (dev),
125                        ver, sizeof (ver)))
126                {
127                        snprintf(s->dev, sizeof(s->dev), "%s/%s",dev,ver);
128                        ms_message("v4w: detected %s",s->dev);
129                        s->devidx=i;
130                        break;
131                }
132        }
133        if (s->capvideo==NULL)
134        {
135                s->capvideo = capCreateCaptureWindow("Capture Window",WS_CHILD /* WS_OVERLAPPED */
136                        ,0,0,s->vsize.width,s->vsize.height,HWND_MESSAGE, 0) ;
137                if (s->capvideo==NULL)
138                {
139                        ms_warning("v4w: could not create capture windows");
140                        return -1;
141                }
142        }
143
144        if(!capDriverConnect(s->capvideo,s->devidx ))
145        {
146                ms_warning("v4w: could not connect to capture driver");
147                DestroyWindow(s->capvideo);
148                s->capvideo=NULL;
149                s->pix_fmt=MS_YUV420P; /* no webcam stuff */
150                return -1;
151        }
152        /*
153        capPreviewRate(s->capvideo,s->fps) ;
154        if(!capPreview (s->capvideo, 1))
155        {
156                ms_warning("v4w: cannot start video preview");
157                capDriverDisconnect(s->capvideo);
158                DestroyWindow(s->capvideo);
159                s->capvideo=NULL;
160                return -1;
161        }
162        */
163        capCaptureGetSetup(s->capvideo,&capparam,sizeof(capparam)) ;
164        capparam.dwRequestMicroSecPerFrame = 100000 ;
165        // detach capture from application
166        capparam.fYield                    = TRUE ;
167        capparam.fMakeUserHitOKToCapture   = FALSE;
168        capparam.fAbortLeftMouse           = FALSE;
169        capparam.fAbortRightMouse          = FALSE;
170        capparam.wPercentDropForError      = 90 ;
171        capparam.fCaptureAudio             = FALSE ;
172        capparam.fAbortRightMouse       = FALSE;
173        capparam.fAbortLeftMouse        = FALSE;
174        capparam.AVStreamMaster            = AVSTREAMMASTER_NONE ;
175
176        if (!capCaptureSetSetup(s->capvideo,&capparam,sizeof(capparam))){
177                ms_error("capCaptureSetSetup failed.");
178        }
179        capSetUserData(s->capvideo, s);
180        capGetVideoFormat(s->capvideo, &videoformat, sizeof(BITMAPINFO));
181        /* "orig planes = " disp->videoformat.bmiHeader.biPlanes */
182        /* "orig bitcount = " disp->videoformat.bmiHeader.biBitCount */
183        /* "orig compression = " disp->videoformat.bmiHeader.biCompression */
184        memcpy(compname,&videoformat.bmiHeader.biCompression,4);
185        ms_message("v4w: camera's current format is %s", compname);
186
187        driver_last=ms_fourcc_to_pix_fmt(videoformat.bmiHeader.biCompression);
188
189        if (s->startwith_yuv_bug==TRUE && try_format(s,&videoformat,MS_RGB24)){
190                s->pix_fmt=MS_RGB24;
191                ms_message("Using RGB24");
192        }else if (driver_last!=MS_PIX_FMT_UNKNOWN && try_format(s,&videoformat,driver_last)){
193                ms_message("Using driver last setting");
194                s->pix_fmt=driver_last;
195        }else if (try_format(s,&videoformat,MS_YUV420P)){
196                s->pix_fmt=MS_YUV420P;
197                ms_message("Using YUV420P");
198        }else if (try_format(s,&videoformat,MS_RGB24)){
199                s->pix_fmt=MS_RGB24;
200                ms_message("Using RGB24");
201                s->startwith_yuv_bug=TRUE;
202        }else if (try_format(s,&videoformat,MS_YUY2)){
203                s->pix_fmt=MS_YUY2;
204                ms_message("Using YUY2");
205        }else{
206                ms_error("v4w: Failed to set any video format.");
207                capDriverDisconnect (s->capvideo);
208                DestroyWindow(s->capvideo);
209                s->capvideo=NULL;
210                return -1;
211        }
212        if (!capSetCallbackOnVideoStream(s->capvideo, VideoStreamCallback))
213        {
214                ms_error("v4w: fail to set capture callback");
215                capDriverDisconnect (s->capvideo);
216                DestroyWindow(s->capvideo);
217                s->capvideo=NULL;
218                return -1;
219        }
220        if (!capCaptureSequenceNoFile(s->capvideo)){
221                ms_error("v4w: fail to start capture");
222                capDriverDisconnect (s->capvideo);
223                capSetCallbackOnVideoStream(s->capvideo, NULL);
224                DestroyWindow(s->capvideo);
225                s->capvideo=NULL;
226        }
227        return 0;
228}
229
230static void v4w_init(MSFilter *f){
231        V4wState *s=(V4wState *)ms_new0(V4wState,1);
232        int idx;
233        s->vsize.width=MS_VIDEO_SIZE_CIF_W;
234        s->vsize.height=MS_VIDEO_SIZE_CIF_H;
235        s->pix_fmt=MS_RGB24;
236
237        s->capvideo=NULL;
238        qinit(&s->rq);
239        for (idx=0;idx<10;idx++)
240        {
241                s->mire[idx]=NULL;
242        }
243        ms_mutex_init(&s->mutex,NULL);
244        s->start_time=0;
245        s->frame_count=-1;
246        s->fps=15;
247        s->started=FALSE;
248        s->autostarted=FALSE;
249
250#ifdef AMD_HACK2
251        /* avoid bug with USB vimicro cards:
252                How can I detect that this problem exist?
253        */
254        s->startwith_yuv_bug=FALSE;
255#endif
256
257#ifdef AMD_HACK2
258        s->thread = NULL;
259        ms_mutex_init(&s->thread_lock,NULL);
260        ms_cond_init(&s->thread_cond,NULL);
261        s->thread_running = FALSE;
262#endif
263
264        f->data=s;
265}
266
267static int _v4w_start(V4wState *s, void *arg)
268{
269        int i;
270        s->frame_count=-1;
271        i = v4w_open_videodevice(s);
272        if (i==0 && s->startwith_yuv_bug==TRUE)
273        {
274                /* reopen device directly with MS_RGB24 */
275                if (s->capvideo){
276                        capSetUserData(s->capvideo, (long) 0);
277                        capCaptureStop(s->capvideo);
278                        capCaptureAbort(s->capvideo);
279                        capDriverDisconnect(s->capvideo);
280                        capSetCallbackOnVideoStream(s->capvideo, NULL);
281                        flushq(&s->rq,0);
282                        ms_message("v4w: destroying capture window");
283                        DestroyWindow(s->capvideo);
284                        ms_message("v4w: capture window destroyed");
285                        s->capvideo=NULL;
286                }
287                i = v4w_open_videodevice(s);
288        }
289        return i;
290}
291
292static int _v4w_stop(V4wState *s, void *arg){
293        s->frame_count=-1;
294        if (s->capvideo){
295                capCaptureStop(s->capvideo);
296                Sleep(1000);
297                //capCaptureAbort(s->capvideo);
298                capSetCallbackOnVideoStream(s->capvideo, NULL);
299                //SendMessage(s->capvideo, WM_CLOSE, 0, 0);
300                capDriverDisconnect(s->capvideo);
301                capSetUserData(s->capvideo, (long) 0);
302                flushq(&s->rq,0);
303                ms_message("v4w: destroying capture window");
304                DestroyWindow(s->capvideo);
305                ms_message("v4w: capture window destroyed");
306                s->capvideo=NULL;
307        }
308#if 0
309        if (s->capvideo){
310                CAPSTATUS status;
311                capCaptureStop(s->capvideo);
312                capDriverDisconnect(s->capvideo);
313                capCaptureAbort(s->capvideo);
314
315                capSetCallbackOnVideoStream(s->capvideo, NULL);
316                while (1)
317                {
318                        if (capGetStatus(s->capvideo, &status, sizeof(status)))
319                        {
320                                if (status.fCapturingNow==FALSE)
321                                        break;
322                                Sleep(10);
323                                ms_message("still capturing");
324                        }
325                }
326                DestroyWindow(s->capvideo);
327                s->capvideo=NULL;
328        }
329#endif
330        return 0;
331}
332
333#ifdef AMD_HACK2
334
335void * 
336v4w_thread(void *arg)
337{
338        V4wState *s=(V4wState*)arg;
339    MSG msg;
340       
341        ms_mutex_lock(&s->thread_lock);
342        _v4w_start(s, NULL);
343        ms_cond_signal(&s->thread_cond);
344        ms_mutex_unlock(&s->thread_lock);
345
346        while(s->thread_running)
347        {
348                BOOL fGotMessage;
349                if((fGotMessage = PeekMessage(&msg, (HWND) s->capvideo, 0, 0, PM_REMOVE)) != 0)
350                {
351                  TranslateMessage(&msg); 
352                  DispatchMessage(&msg);
353                }
354                else
355                        Sleep(10);
356        }
357
358        ms_mutex_lock(&s->thread_lock);
359        _v4w_stop(s, NULL);
360        ms_cond_signal(&s->thread_cond);
361        ms_mutex_unlock(&s->thread_lock);
362        ms_thread_exit(NULL);
363        return NULL;
364}
365
366
367static int v4w_start(MSFilter *f, void *arg){
368        V4wState *s=(V4wState*)f->data;
369        s->thread_running=TRUE;
370        ms_thread_create(&s->thread,NULL,v4w_thread,s);
371        ms_mutex_lock(&s->thread_lock);
372        ms_cond_wait(&s->thread_cond,&s->thread_lock);
373        ms_mutex_unlock(&s->thread_lock);
374        s->started=TRUE;
375        return 0;
376}
377
378static int v4w_stop(MSFilter *f, void *arg){
379        V4wState *s=(V4wState*)f->data;
380        ms_mutex_lock(&s->thread_lock);
381        s->thread_running=FALSE;
382        //SendMessage(s->capvideo, WM_CLOSE, 0, 0);
383        ms_cond_wait(&s->thread_cond,&s->thread_lock);
384        ms_mutex_unlock(&s->thread_lock);
385        ms_thread_join(s->thread,NULL);
386        s->started=FALSE;
387        return 0;
388}
389
390#else
391
392static int v4w_start(MSFilter *f, void *arg){
393        V4wState *s=(V4wState*)f->data;
394        _v4w_start(s, NULL);
395        s->started=TRUE;
396        return 0;
397}
398
399static int v4w_stop(MSFilter *f, void *arg){
400        V4wState *s=(V4wState*)f->data;
401        _v4w_stop(s, NULL);
402        s->started=FALSE;
403        return 0;
404}
405
406#endif
407
408static void v4w_uninit(MSFilter *f){
409        V4wState *s=(V4wState*)f->data;
410        int idx;
411        flushq(&s->rq,0);
412        ms_mutex_destroy(&s->mutex);
413        for (idx=0;idx<10;idx++)
414        {
415                if (s->mire[idx]==NULL)
416                        break;
417                freemsg(s->mire[idx]);
418        }
419        if (s->capvideo!=NULL)
420        {
421                ms_message("v4w: destroying capture window");
422                DestroyWindow(s->capvideo);
423                ms_message("v4w: capture window destroyed");
424                s->capvideo=NULL;
425        }
426#ifdef AMD_HACK2
427        ms_cond_destroy(&s->thread_cond);
428        ms_mutex_destroy(&s->thread_lock);
429#endif
430        ms_free(s);
431}
432
433static mblk_t * v4w_make_nowebcam(V4wState *s){
434        int idx;
435        int count;
436        if (s->mire[0]==NULL && s->frame_ind==0){
437                /* load several images to fake a movie */
438                for (idx=0;idx<10;idx++)
439                {
440                        s->mire[idx]=ms_load_nowebcam(&s->vsize, idx);
441                        if (s->mire[idx]==NULL)
442                                break;
443                }
444                if (idx==0)
445                        s->mire[0]=ms_load_nowebcam(&s->vsize, -1);
446        }
447        for (count=0;count<10;count++)
448        {
449                if (s->mire[count]==NULL)
450                        break;
451        }
452
453        s->frame_ind++;
454        if (count==0)
455                return NULL;
456
457        idx = s->frame_ind%count;
458        if (s->mire[idx]!=NULL)
459                return s->mire[idx];
460        return s->mire[0];
461}
462
463static void v4w_preprocess(MSFilter * obj){
464        V4wState *s=(V4wState*)obj->data;
465        if (!s->started) {
466                ms_message("V4W auto-started.");
467                v4w_start(obj,NULL);
468                s->autostarted=TRUE;
469        }
470        s->running=TRUE;
471        if (s->capvideo==NULL)
472                s->fps=1;
473}
474
475static void v4w_postprocess(MSFilter * obj){
476        V4wState *s=(V4wState*)obj->data;
477        s->running=FALSE;
478        if (s->autostarted){
479                v4w_stop(obj,NULL);
480        }
481}
482
483static void v4w_process(MSFilter * obj){
484        V4wState *s=(V4wState*)obj->data;
485        mblk_t *m;
486        uint32_t timestamp;
487        int cur_frame;
488
489        if (s->frame_count==-1){
490                s->start_time=obj->ticker->time;
491                s->frame_count=0;
492        }
493
494
495        cur_frame=((obj->ticker->time-s->start_time)*s->fps/1000.0);
496        if (cur_frame>s->frame_count){
497                mblk_t *om=NULL;
498                ms_mutex_lock(&s->mutex);
499                /*keep the most recent frame if several frames have been captured */
500                if (s->capvideo!=NULL){
501                        while((m=getq(&s->rq))!=NULL){
502                                if (om!=NULL) freemsg(om);
503                                om=m;
504                        }
505                }else {
506                        mblk_t *nowebcam = v4w_make_nowebcam(s);
507                        if (nowebcam!=NULL)
508                                om=dupmsg(nowebcam);
509                }
510                ms_mutex_unlock(&s->mutex);
511                if (om!=NULL){
512                        timestamp=obj->ticker->time*90;/* rtp uses a 90000 Hz clockrate for video*/
513                        mblk_set_timestamp_info(om,timestamp);
514                        ms_queue_put(obj->outputs[0],om);
515                }
516                s->frame_count++;
517        }
518}
519
520static int v4w_set_fps(MSFilter *f, void *arg){
521        V4wState *s=(V4wState*)f->data;
522        s->fps=*((float*)arg);
523        return 0;
524}
525
526static int v4w_get_pix_fmt(MSFilter *f,void *arg){
527        V4wState *s=(V4wState*)f->data;
528        if (!s->started) {
529                ms_message("V4W auto-started in v4w_get_pix_fmt()");
530                v4w_start(f,NULL);
531                s->autostarted=TRUE;
532        }
533        *((MSPixFmt*)arg) = (MSPixFmt)s->pix_fmt;
534        return 0;
535}
536
537static int v4w_set_vsize(MSFilter *f, void *arg){
538        V4wState *s=(V4wState*)f->data;
539        s->vsize=*((MSVideoSize*)arg);
540        return 0;
541}
542
543static int v4w_get_vsize(MSFilter *f, void *arg){
544        V4wState *s=(V4wState*)f->data;
545        MSVideoSize *vs=(MSVideoSize*)arg;
546        vs->width=s->vsize.width;
547        vs->height=s->vsize.height;
548        return 0;
549}
550
551static MSFilterMethod methods[]={
552        {       MS_FILTER_SET_FPS       ,       v4w_set_fps     },
553        {       MS_FILTER_GET_PIX_FMT   ,       v4w_get_pix_fmt },
554        {       MS_FILTER_SET_VIDEO_SIZE, v4w_set_vsize },
555        {       MS_FILTER_GET_VIDEO_SIZE, v4w_get_vsize },
556        {       MS_V4L_START                    ,       v4w_start               },
557        {       MS_V4L_STOP                     ,       v4w_stop                },
558        {       0                                                               ,       NULL                    }
559};
560
561#ifdef _MSC_VER
562
563MSFilterDesc ms_v4w_desc={
564        MS_V4L_ID,
565        "MSV4w",
566        "A video4windows compatible source filter to stream pictures.",
567        MS_FILTER_OTHER,
568        NULL,
569        0,
570        1,
571        v4w_init,
572        v4w_preprocess,
573        v4w_process,
574        v4w_postprocess,
575        v4w_uninit,
576        methods
577};
578
579#else
580
581MSFilterDesc ms_v4w_desc={
582        .id=MS_V4L_ID,
583        .name="MSV4w",
584        .text="A video4windows compatible source filter to stream pictures.",
585        .ninputs=0,
586        .noutputs=1,
587        .category=MS_FILTER_OTHER,
588        .init=v4w_init,
589        .preprocess=v4w_preprocess,
590        .process=v4w_process,
591        .postprocess=v4w_postprocess,
592        .uninit=v4w_uninit,
593        .methods=methods
594};
595
596#endif
597
598MS_FILTER_DESC_EXPORT(ms_v4w_desc)
599
600static void ms_v4w_detect(MSWebCamManager *obj);
601
602static void ms_v4w_cam_init(MSWebCam *cam){
603}
604
605
606static MSFilter *ms_v4w_create_reader(MSWebCam *obj){
607        MSFilter *f= ms_filter_new_from_desc(&ms_v4w_desc);
608        V4wState *s=(V4wState*)f->data;
609        s->devidx=(int)obj->data;
610        return f;
611}
612
613MSWebCamDesc ms_v4w_cam_desc={
614        "VideoForWindows grabber",
615        &ms_v4w_detect,
616        &ms_v4w_cam_init,
617        &ms_v4w_create_reader,
618        NULL
619};
620
621static void ms_v4w_detect(MSWebCamManager *obj){
622        int i;
623        char dev[80];
624        char ver[80];
625        char name[160];
626        MSWebCam *cam;
627        for (i = 0; i < 9; i++){
628                if (capGetDriverDescription(i, dev, sizeof (dev),
629                        ver, sizeof (ver))){
630                        HWND hwnd=capCreateCaptureWindow("Capture Window",WS_CHILD /* WS_OVERLAPPED */
631                                ,0,0,352,288,HWND_MESSAGE, 0) ;
632                        if (hwnd==NULL) break;
633                        if(!capDriverConnect(hwnd,i )){
634                                ms_warning("v4w: could not connect to capture driver, no webcam connected.");
635                                DestroyWindow(hwnd);
636                                break;
637                        }else{
638                                capDriverDisconnect(hwnd);
639                                DestroyWindow(hwnd);
640                        }
641                        snprintf(name, sizeof(name), "%s/%s",dev,ver);
642                        cam=ms_web_cam_new(&ms_v4w_cam_desc);
643                        cam->data=(void*)i;/*store the device index */
644                        cam->name=ms_strdup(name);
645                        ms_web_cam_manager_add_cam(obj,cam);
646                }
647        }
648}
649
Note: See TracBrowser for help on using the repository browser.