source: mediastreamer2/linphone/mediastreamer2/src/winvideo.c @ 34:0a235bea9540

Last change on this file since 34:0a235bea9540 was 34:0a235bea9540, checked in by smorlat <smorlat@…>, 5 years ago

fix yuy2 support

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

File size: 14.8 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        char dev[80];
119        char ver[80];
120        compname[4]='\0';
121
122        for (i = 0; i < 9; i++){
123                if (capGetDriverDescription(i, dev, sizeof (dev),
124                        ver, sizeof (ver)))
125                {
126                        snprintf(s->dev, sizeof(s->dev), "%s/%s",dev,ver);
127                        ms_message("v4w: detected %s",s->dev);
128                        s->devidx=i;
129                        break;
130                }
131        }
132        if (s->capvideo==NULL)
133        {
134                s->capvideo = capCreateCaptureWindow("Capture Window",WS_CHILD /* WS_OVERLAPPED */
135                        ,0,0,s->vsize.width,s->vsize.height,HWND_MESSAGE, 0) ;
136                if (s->capvideo==NULL)
137                {
138                        ms_warning("v4w: could not create capture windows");
139                        return -1;
140                }
141        }
142
143        if(!capDriverConnect(s->capvideo,s->devidx ))
144        {
145                ms_warning("v4w: could not connect to capture driver");
146                DestroyWindow(s->capvideo);
147                s->capvideo=NULL;
148                s->pix_fmt=MS_YUV420P; /* no webcam stuff */
149                return -1;
150        }
151        /*
152        capPreviewRate(s->capvideo,s->fps) ;
153        if(!capPreview (s->capvideo, 1))
154        {
155                ms_warning("v4w: cannot start video preview");
156                capDriverDisconnect(s->capvideo);
157                DestroyWindow(s->capvideo);
158                s->capvideo=NULL;
159                return -1;
160        }
161        */
162        capCaptureGetSetup(s->capvideo,&capparam,sizeof(capparam)) ;
163        capparam.dwRequestMicroSecPerFrame = 100000 ;
164        // detach capture from application
165        capparam.fYield                    = TRUE ;
166        capparam.fMakeUserHitOKToCapture   = FALSE;
167        capparam.fAbortLeftMouse           = FALSE;
168        capparam.fAbortRightMouse          = FALSE;
169        capparam.wPercentDropForError      = 90 ;
170        capparam.fCaptureAudio             = FALSE ;
171        capparam.fAbortRightMouse       = FALSE;
172        capparam.fAbortLeftMouse        = FALSE;
173        capparam.AVStreamMaster            = AVSTREAMMASTER_NONE ;
174
175        if (!capCaptureSetSetup(s->capvideo,&capparam,sizeof(capparam))){
176                ms_error("capCaptureSetSetup failed.");
177        }
178        capSetUserData(s->capvideo, s);
179        capGetVideoFormat(s->capvideo, &videoformat, sizeof(BITMAPINFO));
180        /* "orig planes = " disp->videoformat.bmiHeader.biPlanes */
181        /* "orig bitcount = " disp->videoformat.bmiHeader.biBitCount */
182        /* "orig compression = " disp->videoformat.bmiHeader.biCompression */
183        memcpy(compname,&videoformat.bmiHeader.biCompression,4);
184        ms_message("v4w: camera's current format is %s", compname);
185
186        if (s->startwith_yuv_bug==TRUE && try_format(s,&videoformat,MS_RGB24)){
187                s->pix_fmt=MS_RGB24;
188                ms_message("Using RGB24");
189        }else if (try_format(s,&videoformat,MS_YUV420P)){
190                s->pix_fmt=MS_YUV420P;
191                ms_message("Using YUV420P");
192        }else if (try_format(s,&videoformat,MS_RGB24)){
193                s->pix_fmt=MS_RGB24;
194                ms_message("Using RGB24");
195                s->startwith_yuv_bug=TRUE;
196        }else if (try_format(s,&videoformat,MS_YUY2)){
197                s->pix_fmt=MS_YUY2;
198                ms_message("Using YUY2");
199        }else{
200                ms_error("v4w: Failed to set any video format.");
201                capDriverDisconnect (s->capvideo);
202                DestroyWindow(s->capvideo);
203                s->capvideo=NULL;
204                return -1;
205        }
206        if (!capSetCallbackOnVideoStream(s->capvideo, VideoStreamCallback))
207        {
208                ms_error("v4w: fail to set capture callback");
209                capDriverDisconnect (s->capvideo);
210                DestroyWindow(s->capvideo);
211                s->capvideo=NULL;
212                return -1;
213        }
214        if (!capCaptureSequenceNoFile(s->capvideo)){
215                ms_error("v4w: fail to start capture");
216                capDriverDisconnect (s->capvideo);
217                capSetCallbackOnVideoStream(s->capvideo, NULL);
218                DestroyWindow(s->capvideo);
219                s->capvideo=NULL;
220        }
221        return 0;
222}
223
224static void v4w_init(MSFilter *f){
225        V4wState *s=(V4wState *)ms_new0(V4wState,1);
226        int idx;
227        s->vsize.width=MS_VIDEO_SIZE_CIF_W;
228        s->vsize.height=MS_VIDEO_SIZE_CIF_H;
229        s->pix_fmt=MS_RGB24;
230
231        s->capvideo=NULL;
232        qinit(&s->rq);
233        for (idx=0;idx<10;idx++)
234        {
235                s->mire[idx]=NULL;
236        }
237        ms_mutex_init(&s->mutex,NULL);
238        s->start_time=0;
239        s->frame_count=-1;
240        s->fps=15;
241        s->started=FALSE;
242        s->autostarted=FALSE;
243
244#ifdef AMD_HACK2
245        /* avoid bug with USB vimicro cards:
246                How can I detect that this problem exist?
247        */
248        s->startwith_yuv_bug=FALSE;
249#endif
250
251#ifdef AMD_HACK2
252        s->thread = NULL;
253        ms_mutex_init(&s->thread_lock,NULL);
254        ms_cond_init(&s->thread_cond,NULL);
255        s->thread_running = FALSE;
256#endif
257
258        f->data=s;
259}
260
261static int _v4w_start(V4wState *s, void *arg)
262{
263        int i;
264        s->frame_count=-1;
265        i = v4w_open_videodevice(s);
266        if (i==0 && s->startwith_yuv_bug==TRUE)
267        {
268                /* reopen device directly with MS_RGB24 */
269                if (s->capvideo){
270                        capSetUserData(s->capvideo, (long) 0);
271                        capCaptureStop(s->capvideo);
272                        capCaptureAbort(s->capvideo);
273                        capDriverDisconnect(s->capvideo);
274                        capSetCallbackOnVideoStream(s->capvideo, NULL);
275                        flushq(&s->rq,0);
276                        ms_message("v4w: destroying capture window");
277                        DestroyWindow(s->capvideo);
278                        ms_message("v4w: capture window destroyed");
279                        s->capvideo=NULL;
280                }
281                i = v4w_open_videodevice(s);
282        }
283        return i;
284}
285
286static int _v4w_stop(V4wState *s, void *arg){
287        s->frame_count=-1;
288        if (s->capvideo){
289                capCaptureStop(s->capvideo);
290                Sleep(1000);
291                //capCaptureAbort(s->capvideo);
292                capSetCallbackOnVideoStream(s->capvideo, NULL);
293                //SendMessage(s->capvideo, WM_CLOSE, 0, 0);
294                capDriverDisconnect(s->capvideo);
295                capSetUserData(s->capvideo, (long) 0);
296                flushq(&s->rq,0);
297                ms_message("v4w: destroying capture window");
298                DestroyWindow(s->capvideo);
299                ms_message("v4w: capture window destroyed");
300                s->capvideo=NULL;
301        }
302#if 0
303        if (s->capvideo){
304                CAPSTATUS status;
305                capCaptureStop(s->capvideo);
306                capDriverDisconnect(s->capvideo);
307                capCaptureAbort(s->capvideo);
308
309                capSetCallbackOnVideoStream(s->capvideo, NULL);
310                while (1)
311                {
312                        if (capGetStatus(s->capvideo, &status, sizeof(status)))
313                        {
314                                if (status.fCapturingNow==FALSE)
315                                        break;
316                                Sleep(10);
317                                ms_message("still capturing");
318                        }
319                }
320                DestroyWindow(s->capvideo);
321                s->capvideo=NULL;
322        }
323#endif
324        return 0;
325}
326
327#ifdef AMD_HACK2
328
329void * 
330v4w_thread(void *arg)
331{
332        V4wState *s=(V4wState*)arg;
333    MSG msg;
334       
335        ms_mutex_lock(&s->thread_lock);
336        _v4w_start(s, NULL);
337        ms_cond_signal(&s->thread_cond);
338        ms_mutex_unlock(&s->thread_lock);
339
340        while(s->thread_running)
341        {
342                BOOL fGotMessage;
343                if((fGotMessage = PeekMessage(&msg, (HWND) s->capvideo, 0, 0, PM_REMOVE)) != 0)
344                {
345                  TranslateMessage(&msg); 
346                  DispatchMessage(&msg);
347                }
348                else
349                        Sleep(10);
350        }
351
352        ms_mutex_lock(&s->thread_lock);
353        _v4w_stop(s, NULL);
354        ms_cond_signal(&s->thread_cond);
355        ms_mutex_unlock(&s->thread_lock);
356        ms_thread_exit(NULL);
357        return NULL;
358}
359
360
361static int v4w_start(MSFilter *f, void *arg){
362        V4wState *s=(V4wState*)f->data;
363        s->thread_running=TRUE;
364        ms_thread_create(&s->thread,NULL,v4w_thread,s);
365        ms_mutex_lock(&s->thread_lock);
366        ms_cond_wait(&s->thread_cond,&s->thread_lock);
367        ms_mutex_unlock(&s->thread_lock);
368        s->started=TRUE;
369        return 0;
370}
371
372static int v4w_stop(MSFilter *f, void *arg){
373        V4wState *s=(V4wState*)f->data;
374        ms_mutex_lock(&s->thread_lock);
375        s->thread_running=FALSE;
376        //SendMessage(s->capvideo, WM_CLOSE, 0, 0);
377        ms_cond_wait(&s->thread_cond,&s->thread_lock);
378        ms_mutex_unlock(&s->thread_lock);
379        ms_thread_join(s->thread,NULL);
380        s->started=FALSE;
381        return 0;
382}
383
384#else
385
386static int v4w_start(MSFilter *f, void *arg){
387        V4wState *s=(V4wState*)f->data;
388        _v4w_start(s, NULL);
389        s->started=TRUE;
390        return 0;
391}
392
393static int v4w_stop(MSFilter *f, void *arg){
394        V4wState *s=(V4wState*)f->data;
395        _v4w_stop(s, NULL);
396        s->started=FALSE;
397        return 0;
398}
399
400#endif
401
402static void v4w_uninit(MSFilter *f){
403        V4wState *s=(V4wState*)f->data;
404        int idx;
405        flushq(&s->rq,0);
406        ms_mutex_destroy(&s->mutex);
407        for (idx=0;idx<10;idx++)
408        {
409                if (s->mire[idx]==NULL)
410                        break;
411                freemsg(s->mire[idx]);
412        }
413        if (s->capvideo!=NULL)
414        {
415                ms_message("v4w: destroying capture window");
416                DestroyWindow(s->capvideo);
417                ms_message("v4w: capture window destroyed");
418                s->capvideo=NULL;
419        }
420#ifdef AMD_HACK2
421        ms_cond_destroy(&s->thread_cond);
422        ms_mutex_destroy(&s->thread_lock);
423#endif
424        ms_free(s);
425}
426
427static mblk_t * v4w_make_nowebcam(V4wState *s){
428        int idx;
429        int count;
430        if (s->mire[0]==NULL && s->frame_ind==0){
431                /* load several images to fake a movie */
432                for (idx=0;idx<10;idx++)
433                {
434                        s->mire[idx]=ms_load_nowebcam(&s->vsize, idx);
435                        if (s->mire[idx]==NULL)
436                                break;
437                }
438                if (idx==0)
439                        s->mire[0]=ms_load_nowebcam(&s->vsize, -1);
440        }
441        for (count=0;count<10;count++)
442        {
443                if (s->mire[count]==NULL)
444                        break;
445        }
446
447        s->frame_ind++;
448        if (count==0)
449                return NULL;
450
451        idx = s->frame_ind%count;
452        if (s->mire[idx]!=NULL)
453                return s->mire[idx];
454        return s->mire[0];
455}
456
457static void v4w_preprocess(MSFilter * obj){
458        V4wState *s=(V4wState*)obj->data;
459        if (!s->started) {
460                ms_message("V4W auto-started.");
461                v4w_start(obj,NULL);
462                s->autostarted=TRUE;
463        }
464        s->running=TRUE;
465        if (s->capvideo==NULL)
466                s->fps=1;
467}
468
469static void v4w_postprocess(MSFilter * obj){
470        V4wState *s=(V4wState*)obj->data;
471        s->running=FALSE;
472        if (s->autostarted){
473                v4w_stop(obj,NULL);
474        }
475}
476
477static void v4w_process(MSFilter * obj){
478        V4wState *s=(V4wState*)obj->data;
479        mblk_t *m;
480        uint32_t timestamp;
481        int cur_frame;
482
483        if (s->frame_count==-1){
484                s->start_time=obj->ticker->time;
485                s->frame_count=0;
486        }
487
488
489        cur_frame=((obj->ticker->time-s->start_time)*s->fps/1000.0);
490        if (cur_frame>s->frame_count){
491                mblk_t *om=NULL;
492                ms_mutex_lock(&s->mutex);
493                /*keep the most recent frame if several frames have been captured */
494                if (s->capvideo!=NULL){
495                        while((m=getq(&s->rq))!=NULL){
496                                if (om!=NULL) freemsg(om);
497                                om=m;
498                        }
499                }else {
500                        mblk_t *nowebcam = v4w_make_nowebcam(s);
501                        if (nowebcam!=NULL)
502                                om=dupmsg(nowebcam);
503                }
504                ms_mutex_unlock(&s->mutex);
505                if (om!=NULL){
506                        timestamp=obj->ticker->time*90;/* rtp uses a 90000 Hz clockrate for video*/
507                        mblk_set_timestamp_info(om,timestamp);
508                        ms_queue_put(obj->outputs[0],om);
509                }
510                s->frame_count++;
511        }
512}
513
514static int v4w_set_fps(MSFilter *f, void *arg){
515        V4wState *s=(V4wState*)f->data;
516        s->fps=*((float*)arg);
517        return 0;
518}
519
520static int v4w_get_pix_fmt(MSFilter *f,void *arg){
521        V4wState *s=(V4wState*)f->data;
522        if (!s->started) {
523                ms_message("V4W auto-started in v4w_get_pix_fmt()");
524                v4w_start(f,NULL);
525                s->autostarted=TRUE;
526        }
527        *((MSPixFmt*)arg) = (MSPixFmt)s->pix_fmt;
528        return 0;
529}
530
531static int v4w_set_vsize(MSFilter *f, void *arg){
532        V4wState *s=(V4wState*)f->data;
533        s->vsize=*((MSVideoSize*)arg);
534        return 0;
535}
536
537static int v4w_get_vsize(MSFilter *f, void *arg){
538        V4wState *s=(V4wState*)f->data;
539        MSVideoSize *vs=(MSVideoSize*)arg;
540        vs->width=s->vsize.width;
541        vs->height=s->vsize.height;
542        return 0;
543}
544
545static MSFilterMethod methods[]={
546        {       MS_FILTER_SET_FPS       ,       v4w_set_fps     },
547        {       MS_FILTER_GET_PIX_FMT   ,       v4w_get_pix_fmt },
548        {       MS_FILTER_SET_VIDEO_SIZE, v4w_set_vsize },
549        {       MS_FILTER_GET_VIDEO_SIZE, v4w_get_vsize },
550        {       MS_V4L_START                    ,       v4w_start               },
551        {       MS_V4L_STOP                     ,       v4w_stop                },
552        {       0                                                               ,       NULL                    }
553};
554
555#ifdef _MSC_VER
556
557MSFilterDesc ms_v4w_desc={
558        MS_V4L_ID,
559        "MSV4w",
560        "A video4windows compatible source filter to stream pictures.",
561        MS_FILTER_OTHER,
562        NULL,
563        0,
564        1,
565        v4w_init,
566        v4w_preprocess,
567        v4w_process,
568        v4w_postprocess,
569        v4w_uninit,
570        methods
571};
572
573#else
574
575MSFilterDesc ms_v4w_desc={
576        .id=MS_V4L_ID,
577        .name="MSV4w",
578        .text="A video4windows compatible source filter to stream pictures.",
579        .ninputs=0,
580        .noutputs=1,
581        .category=MS_FILTER_OTHER,
582        .init=v4w_init,
583        .preprocess=v4w_preprocess,
584        .process=v4w_process,
585        .postprocess=v4w_postprocess,
586        .uninit=v4w_uninit,
587        .methods=methods
588};
589
590#endif
591
592MS_FILTER_DESC_EXPORT(ms_v4w_desc)
593
594static void ms_v4w_detect(MSWebCamManager *obj);
595
596static void ms_v4w_cam_init(MSWebCam *cam){
597}
598
599
600static MSFilter *ms_v4w_create_reader(MSWebCam *obj){
601        MSFilter *f= ms_filter_new_from_desc(&ms_v4w_desc);
602        V4wState *s=(V4wState*)f->data;
603        s->devidx=(int)obj->data;
604        return f;
605}
606
607MSWebCamDesc ms_v4w_cam_desc={
608        "VideoForWindows grabber",
609        &ms_v4w_detect,
610        &ms_v4w_cam_init,
611        &ms_v4w_create_reader,
612        NULL
613};
614
615static void ms_v4w_detect(MSWebCamManager *obj){
616        int i;
617        char dev[80];
618        char ver[80];
619        char name[160];
620        MSWebCam *cam;
621        for (i = 0; i < 9; i++){
622                if (capGetDriverDescription(i, dev, sizeof (dev),
623                        ver, sizeof (ver))){
624                        HWND hwnd=capCreateCaptureWindow("Capture Window",WS_CHILD /* WS_OVERLAPPED */
625                                ,0,0,352,288,HWND_MESSAGE, 0) ;
626                        if (hwnd==NULL) break;
627                        if(!capDriverConnect(hwnd,i )){
628                                ms_warning("v4w: could not connect to capture driver, no webcam connected.");
629                                DestroyWindow(hwnd);
630                                break;
631                        }else{
632                                capDriverDisconnect(hwnd);
633                                DestroyWindow(hwnd);
634                        }
635                        snprintf(name, sizeof(name), "%s/%s",dev,ver);
636                        cam=ms_web_cam_new(&ms_v4w_cam_desc);
637                        cam->data=(void*)i;/*store the device index */
638                        cam->name=ms_strdup(name);
639                        ms_web_cam_manager_add_cam(obj,cam);
640                }
641        }
642}
643
Note: See TracBrowser for help on using the repository browser.