]> sjero.net Git - linphone/blob - linphone/mediastreamer2/src/winvideo2.c
remote ortp and add it as a submodule instead.
[linphone] / linphone / mediastreamer2 / src / winvideo2.c
1 /*\r
2 mediastreamer2 library - modular sound and video processing and streaming\r
3 Copyright (C) 2006  Simon MORLAT (simon.morlat@linphone.org)\r
4 \r
5 This program is free software; you can redistribute it and/or\r
6 modify it under the terms of the GNU General Public License\r
7 as published by the Free Software Foundation; either version 2\r
8 of the License, or (at your option) any later version.\r
9 \r
10 This program is distributed in the hope that it will be useful,\r
11 but WITHOUT ANY WARRANTY; without even the implied warranty of\r
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
13 GNU General Public License for more details.\r
14 \r
15 You should have received a copy of the GNU General Public License\r
16 along with this program; if not, write to the Free Software\r
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\r
18 */\r
19 \r
20 #include "mediastreamer2/msvideo.h"\r
21 #include "mediastreamer2/msticker.h"\r
22 #include "mediastreamer2/msv4l.h"\r
23 #include "Vfw.h"\r
24 #include <winuser.h>\r
25 #include <Windows.h>\r
26 \r
27 #include "mediastreamer2/mswebcam.h"\r
28 \r
29 #ifndef AVSTREAMMASTER_NONE\r
30 #define AVSTREAMMASTER_NONE 1\r
31 #endif\r
32 \r
33 typedef void (*queue_msg_t)(void *, mblk_t *);\r
34 \r
35 typedef struct VfwEngine{\r
36         ms_thread_t thread;\r
37         char dev[512];\r
38         int devidx;\r
39         HWND capvideo;\r
40         MSVideoSize vsize;\r
41         MSPixFmt pix_fmt;\r
42         queue_msg_t cb;\r
43         void *cb_data;\r
44         bool_t started;\r
45         bool_t configured;\r
46         bool_t thread_running;\r
47 }VfwEngine;\r
48 \r
49 #define VFW_ENGINE_MAX_INSTANCES 9\r
50 \r
51 static VfwEngine *engines[VFW_ENGINE_MAX_INSTANCES]={0};\r
52 \r
53 static bool_t try_format(VfwEngine *s, BITMAPINFO *videoformat, MSPixFmt pixfmt){\r
54         MSVideoSize tried_size=s->vsize;\r
55         bool_t ret;\r
56         do{\r
57                 capGetVideoFormat(s->capvideo, videoformat, sizeof(BITMAPINFO));\r
58                 videoformat->bmiHeader.biSizeImage = 0;\r
59                 videoformat->bmiHeader.biWidth  = tried_size.width;\r
60                 videoformat->bmiHeader.biHeight = tried_size.height;\r
61                 switch(pixfmt){\r
62                         case MS_YUV420P:\r
63                                 videoformat->bmiHeader.biBitCount = 12;\r
64                                 videoformat->bmiHeader.biCompression=MAKEFOURCC('I','4','2','0');\r
65                         break;\r
66                         case MS_YUY2:\r
67                                 videoformat->bmiHeader.biBitCount = 16;\r
68                                 videoformat->bmiHeader.biCompression=MAKEFOURCC('Y','U','Y','2');\r
69                         break;\r
70                         case MS_RGB24:\r
71                                 videoformat->bmiHeader.biBitCount = 24;\r
72                                 videoformat->bmiHeader.biCompression=BI_RGB;\r
73                         break;\r
74                         default:\r
75                                 return FALSE;\r
76                 }\r
77                 ms_message("Trying video size %ix%i",tried_size.width,tried_size.height);\r
78                 ret=capSetVideoFormat(s->capvideo, videoformat, sizeof(BITMAPINFO));\r
79                 tried_size=ms_video_size_get_just_lower_than(tried_size);\r
80         }while(ret==FALSE && tried_size.width!=0);\r
81         if (ret) {\r
82                 /*recheck video format */\r
83                 capGetVideoFormat(s->capvideo, videoformat, sizeof(BITMAPINFO));\r
84                 s->vsize.width=videoformat->bmiHeader.biWidth;\r
85                 s->vsize.height=videoformat->bmiHeader.biHeight;\r
86         }\r
87         return ret;\r
88 }\r
89 \r
90 static int _vfw_engine_select_format(VfwEngine *obj){\r
91         BITMAPINFO videoformat;\r
92         MSPixFmt driver_last;\r
93         char compname[5];\r
94         \r
95         capGetVideoFormat(obj->capvideo, &videoformat, sizeof(BITMAPINFO));\r
96         memcpy(compname,&videoformat.bmiHeader.biCompression,4);\r
97         compname[4]='\0';\r
98         ms_message("vfw: camera's current format is '%s' at %ix%i", compname,\r
99                         videoformat.bmiHeader.biWidth,videoformat.bmiHeader.biHeight);\r
100         driver_last=ms_fourcc_to_pix_fmt(videoformat.bmiHeader.biCompression);\r
101         if (driver_last!=MS_PIX_FMT_UNKNOWN && try_format(obj,&videoformat,driver_last)){\r
102                 ms_message("Using driver last setting");\r
103                 obj->pix_fmt=driver_last;\r
104         }else if (try_format(obj,&videoformat,MS_YUV420P)){\r
105                 obj->pix_fmt=MS_YUV420P;\r
106                 ms_message("Using YUV420P");\r
107         }else if (try_format(obj,&videoformat,MS_RGB24)){\r
108                 obj->pix_fmt=MS_RGB24;\r
109                 ms_message("Using RGB24");\r
110         }else if (try_format(obj,&videoformat,MS_YUY2)){\r
111                 obj->pix_fmt=MS_YUY2;\r
112                 ms_message("Using YUY2");\r
113         }else{\r
114                 ms_error("v4w: Failed to set any video format.");\r
115                 return -1;\r
116         }\r
117         if (obj->pix_fmt==MS_RGB24){\r
118                 if (videoformat.bmiHeader.biHeight>0){\r
119                         obj->pix_fmt=MS_RGB24_REV;\r
120                 }\r
121         }\r
122         return 0;\r
123 }\r
124 \r
125 static void dummy(void*p){\r
126 }\r
127 \r
128 LRESULT CALLBACK vfw_engine_stream_callback(HWND hWnd, LPVIDEOHDR lpVHdr)\r
129 {\r
130         VfwEngine *s;\r
131         mblk_t *buf;\r
132         int size;\r
133 \r
134         s = (VfwEngine *)capGetUserData(hWnd);\r
135         if (s==NULL)\r
136                 return FALSE;\r
137 \r
138         size = lpVHdr->dwBufferLength;\r
139         if (size>0 && s->cb!=NULL && s->started){\r
140                 buf = esballoc(lpVHdr->lpData,size,0,dummy);\r
141                 buf->b_wptr+=size;\r
142                 s->cb(s->cb_data,buf);\r
143         }\r
144         return TRUE ;\r
145 }\r
146 \r
147 static void *\r
148 vfw_engine_thread(void *arg)\r
149 {\r
150         VfwEngine *s=(VfwEngine*)arg;\r
151     MSG msg;\r
152 \r
153         while(s->thread_running)\r
154         {\r
155                 BOOL fGotMessage;\r
156                 while((fGotMessage = PeekMessage(&msg, (HWND) s->capvideo, 0, 0, PM_REMOVE)) != 0)\r
157                 {\r
158                   TranslateMessage(&msg);\r
159                   DispatchMessage(&msg);\r
160                 }\r
161                 Sleep(10);\r
162         }\r
163         ms_thread_exit(NULL);\r
164         return NULL;\r
165 }\r
166 \r
167 static void _vfw_engine_unconfigure(VfwEngine *obj){\r
168         if (!capCaptureStop(obj->capvideo)){\r
169                 ms_error("vfw: fail to stop capture !");\r
170         }\r
171         obj->thread_running=FALSE;\r
172         ms_thread_join(obj->thread,NULL);\r
173         obj->configured=FALSE;\r
174 }\r
175 \r
176 static void _vfw_engine_disconnect(VfwEngine *obj){\r
177         capDriverDisconnect(obj->capvideo);\r
178         DestroyWindow(obj->capvideo);\r
179         obj->capvideo=NULL;\r
180 }\r
181 \r
182 static void vfw_engine_destroy(VfwEngine *obj){\r
183         if (obj->configured){\r
184                 _vfw_engine_unconfigure(obj);\r
185         }\r
186         _vfw_engine_disconnect(obj);\r
187         ms_free(obj);\r
188 }\r
189 \r
190 static int _vfw_engine_setup(VfwEngine *obj){\r
191         CAPTUREPARMS capparam ;\r
192         capCaptureGetSetup(obj->capvideo,&capparam,sizeof(capparam)) ;\r
193         capparam.dwRequestMicroSecPerFrame = 33000 ; /*makes around 30fps*/\r
194         // detach capture from application\r
195         capparam.fYield                    = TRUE ;\r
196         capparam.fMakeUserHitOKToCapture   = FALSE;\r
197         capparam.fAbortLeftMouse           = FALSE;\r
198         capparam.fAbortRightMouse          = FALSE;\r
199         capparam.wPercentDropForError      = 90 ;\r
200         capparam.fCaptureAudio             = FALSE ;\r
201         capparam.fAbortRightMouse       = FALSE;\r
202         capparam.fAbortLeftMouse        = FALSE;\r
203         capparam.AVStreamMaster            = AVSTREAMMASTER_NONE ;\r
204         if (!capCaptureSetSetup(obj->capvideo,&capparam,sizeof(capparam))){\r
205                 ms_error("capCaptureSetSetup failed.");\r
206                 return -1;\r
207         }\r
208         capSetUserData(obj->capvideo, obj);\r
209         return 0;\r
210 }\r
211 \r
212 static int _vfw_engine_connect(VfwEngine *obj){\r
213         MSVideoSize sz;\r
214         sz.width=MS_VIDEO_SIZE_CIF_W;\r
215         sz.height=MS_VIDEO_SIZE_CIF_H;\r
216         HWND hwnd=capCreateCaptureWindow("Capture Window",WS_CHILD /* WS_OVERLAPPED */\r
217                         ,0,0,sz.width,sz.height,HWND_MESSAGE, 0) ;\r
218 \r
219         if (hwnd==NULL) return -1;\r
220         if(!capDriverConnect(hwnd,obj->devidx)){\r
221                 ms_warning("vfw: could not connect to capture driver, no webcam connected.");\r
222                 DestroyWindow(hwnd);\r
223                 return -1;\r
224         }\r
225         obj->capvideo=hwnd;\r
226         obj->vsize=sz;\r
227         return 0;\r
228 }\r
229 \r
230 static VfwEngine * vfw_engine_new(int i){\r
231         char dev[512];\r
232         char ver[512];\r
233         VfwEngine *obj=(VfwEngine*)ms_new0(VfwEngine,1);\r
234         if (capGetDriverDescription(i, dev, sizeof (dev),\r
235                 ver, sizeof (ver))){\r
236                 obj->devidx=i;\r
237                 if (_vfw_engine_connect(obj)==-1){\r
238                         ms_free(obj);\r
239                         return NULL;\r
240                 }\r
241                 strcpy(obj->dev,dev);\r
242                 engines[i]=obj;\r
243                 return obj;\r
244         }\r
245         return NULL;\r
246 }\r
247 \r
248 static void _vfw_engine_configure(VfwEngine *obj){\r
249         if (_vfw_engine_setup(obj)==-1){\r
250                 return;\r
251         }\r
252         if (_vfw_engine_select_format(obj)==-1){\r
253                 return ;\r
254         }\r
255         capSetCallbackOnVideoStream(obj->capvideo, vfw_engine_stream_callback);\r
256         if (!capCaptureSequenceNoFile(obj->capvideo)){\r
257                 ms_error("vfw: fail to start capture !");\r
258         }\r
259         ms_thread_create(&obj->thread,NULL,vfw_engine_thread,obj);\r
260         obj->configured=TRUE;\r
261 }\r
262 \r
263 static MSPixFmt vfw_engine_get_pix_fmt(VfwEngine *obj){\r
264         if (!obj->configured)\r
265                 _vfw_engine_configure(obj); \r
266         return obj->pix_fmt;\r
267 }\r
268 \r
269 static MSVideoSize vfw_engine_get_video_size(VfwEngine *obj){\r
270         return obj->vsize;\r
271 }\r
272 \r
273 static void vfw_engine_set_video_size(VfwEngine *obj, MSVideoSize vsize){\r
274         if (!obj->configured)\r
275                 obj->vsize=vsize;\r
276         else if (ms_video_size_greater_than(vsize,obj->vsize) && !ms_video_size_equal(vsize,obj->vsize) ){\r
277                 _vfw_engine_unconfigure(obj);\r
278                 _vfw_engine_disconnect(obj);\r
279                 _vfw_engine_connect(obj);\r
280                 obj->vsize=vsize;\r
281                 _vfw_engine_configure(obj);\r
282         }\r
283 }\r
284 \r
285 static void vfw_engine_set_callback(VfwEngine* obj, queue_msg_t cb, void *cb_data){\r
286         obj->cb=cb;\r
287         obj->cb_data=cb_data;\r
288 }\r
289 \r
290 static void vfw_engine_start_capture(VfwEngine *obj){\r
291         if (!obj->configured) _vfw_engine_configure(obj);\r
292         obj->started=TRUE;\r
293 }\r
294 \r
295 static void vfw_engine_stop_capture(VfwEngine *obj){\r
296         obj->started=FALSE;\r
297 }\r
298 \r
299 static void vfw_engines_free(void){\r
300         int i;\r
301         for(i=0;i<VFW_ENGINE_MAX_INSTANCES;++i){\r
302                 if (engines[i])\r
303                         vfw_engine_destroy(engines[i]);\r
304         }\r
305 }\r
306 \r
307 \r
308 typedef struct VfwState{\r
309         MSVideoSize vsize;\r
310         queue_t rq;\r
311         ms_mutex_t mutex;\r
312         int frame_ind;\r
313         int frame_max;\r
314         float fps;\r
315         float start_time;\r
316         int frame_count;\r
317         VfwEngine *eng;\r
318 } VfwState;\r
319 \r
320 \r
321 static void vfw_init(MSFilter *f){\r
322         VfwState *s=(VfwState *)ms_new0(VfwState,1);\r
323         s->vsize.width=MS_VIDEO_SIZE_CIF_W;\r
324         s->vsize.height=MS_VIDEO_SIZE_CIF_H;\r
325         qinit(&s->rq);\r
326         ms_mutex_init(&s->mutex,NULL);\r
327         s->start_time=0;\r
328         s->frame_count=-1;\r
329         s->fps=15;\r
330         f->data=s;\r
331 }\r
332 \r
333 \r
334 \r
335 static void vfw_uninit(MSFilter *f){\r
336         VfwState *s=(VfwState*)f->data;\r
337         flushq(&s->rq,0);\r
338         ms_mutex_destroy(&s->mutex);\r
339         ms_free(s);\r
340 }\r
341 \r
342 static void vfw_callback(void *data, mblk_t *m){\r
343         VfwState *s=(VfwState*)data;\r
344         ms_mutex_lock(&s->mutex);\r
345         putq(&s->rq,m);\r
346         ms_mutex_unlock(&s->mutex);\r
347 }\r
348 \r
349 static void vfw_preprocess(MSFilter * obj){\r
350         VfwState *s=(VfwState*)obj->data;\r
351         if (s->eng==NULL) s->eng=engines[0];\r
352         vfw_engine_set_callback(s->eng,vfw_callback,s);\r
353         vfw_engine_start_capture(s->eng);\r
354 }\r
355 \r
356 static void vfw_postprocess(MSFilter * obj){\r
357         VfwState *s=(VfwState*)obj->data;\r
358         vfw_engine_stop_capture(s->eng);\r
359         flushq(&s->rq,0);\r
360 }\r
361 \r
362 static void vfw_process(MSFilter * obj){\r
363         VfwState *s=(VfwState*)obj->data;\r
364         mblk_t *m;\r
365         uint32_t timestamp;\r
366         int cur_frame;\r
367 \r
368         if (s->frame_count==-1){\r
369                 s->start_time=(float)obj->ticker->time;\r
370                 s->frame_count=0;\r
371         }\r
372 \r
373         cur_frame=(int)((obj->ticker->time-s->start_time)*s->fps/1000.0);\r
374         if (cur_frame>s->frame_count){\r
375                 mblk_t *om=NULL;\r
376                 /*keep the most recent frame if several frames have been captured */\r
377                 if (s->eng!=NULL){\r
378                         ms_mutex_lock(&s->mutex);\r
379                         while((m=getq(&s->rq))!=NULL){\r
380                                 ms_mutex_unlock(&s->mutex);\r
381                                 if (om!=NULL) freemsg(om);\r
382                                 om=m;\r
383                                 ms_mutex_lock(&s->mutex);\r
384                         }\r
385                         ms_mutex_unlock(&s->mutex);\r
386                 }\r
387                 if (om!=NULL){\r
388                         timestamp=(uint32_t)(obj->ticker->time*90);/* rtp uses a 90000 Hz clockrate for video*/\r
389                         mblk_set_timestamp_info(om,timestamp);\r
390                         ms_queue_put(obj->outputs[0],om);\r
391                 }\r
392                 s->frame_count++;\r
393         }\r
394 }\r
395 \r
396 static int vfw_set_fps(MSFilter *f, void *arg){\r
397         VfwState *s=(VfwState*)f->data;\r
398         s->fps=*((float*)arg);\r
399         return 0;\r
400 }\r
401 \r
402 static int vfw_get_pix_fmt(MSFilter *f,void *arg){\r
403         VfwState *s=(VfwState*)f->data;\r
404         MSPixFmt fmt=vfw_engine_get_pix_fmt(s->eng);\r
405         *((MSPixFmt*)arg)=fmt;\r
406         return 0;\r
407 }\r
408 \r
409 static int vfw_set_vsize(MSFilter *f, void *arg){\r
410         VfwState *s=(VfwState*)f->data;\r
411         s->vsize=*((MSVideoSize*)arg);\r
412         vfw_engine_set_video_size(s->eng,s->vsize);\r
413         return 0;\r
414 }\r
415 \r
416 static int vfw_get_vsize(MSFilter *f, void *arg){\r
417         VfwState *s=(VfwState*)f->data;\r
418         MSVideoSize *vs=(MSVideoSize*)arg;\r
419         *vs=vfw_engine_get_video_size(s->eng);\r
420         return 0;\r
421 }\r
422 \r
423 static MSFilterMethod methods[]={\r
424         {       MS_FILTER_SET_FPS       ,       vfw_set_fps     },\r
425         {       MS_FILTER_GET_PIX_FMT   ,       vfw_get_pix_fmt },\r
426         {       MS_FILTER_SET_VIDEO_SIZE, vfw_set_vsize },\r
427         {       MS_FILTER_GET_VIDEO_SIZE, vfw_get_vsize },\r
428         {       0                                                               ,       NULL                    }\r
429 };\r
430 \r
431 #ifdef _MSC_VER\r
432 \r
433 MSFilterDesc ms_vfw_desc={\r
434         MS_VFW_ID,\r
435         "MSVfw",\r
436         N_("A video for windows (vfw.h) based source filter to grab pictures."),\r
437         MS_FILTER_OTHER,\r
438         NULL,\r
439         0,\r
440         1,\r
441         vfw_init,\r
442         vfw_preprocess,\r
443         vfw_process,\r
444         vfw_postprocess,\r
445         vfw_uninit,\r
446         methods\r
447 };\r
448 \r
449 #else\r
450 \r
451 MSFilterDesc ms_vfw_desc={\r
452         .id=MS_VFW_ID,\r
453         .name="MSVfw",\r
454         .text=N_("A video for windows (vfw.h) based source filter to grab pictures."),\r
455         .ninputs=0,\r
456         .noutputs=1,\r
457         .category=MS_FILTER_OTHER,\r
458         .init=vfw_init,\r
459         .preprocess=vfw_preprocess,\r
460         .process=vfw_process,\r
461         .postprocess=vfw_postprocess,\r
462         .uninit=vfw_uninit,\r
463         .methods=methods\r
464 };\r
465 \r
466 #endif\r
467 \r
468 MS_FILTER_DESC_EXPORT(ms_vfw_desc)\r
469 \r
470 static void ms_vfw_detect(MSWebCamManager *obj);\r
471 \r
472 static void ms_vfw_cam_init(MSWebCam *cam){\r
473 }\r
474 \r
475 \r
476 static MSFilter *ms_vfw_create_reader(MSWebCam *obj){\r
477         MSFilter *f= ms_filter_new_from_desc(&ms_vfw_desc);\r
478         VfwState *s=(VfwState*)f->data;\r
479         s->eng=(VfwEngine*)obj->data;\r
480         return f;\r
481 }\r
482 \r
483 MSWebCamDesc ms_vfw_cam_desc={\r
484         "VideoForWindows grabber",\r
485         &ms_vfw_detect,\r
486         &ms_vfw_cam_init,\r
487         &ms_vfw_create_reader,\r
488         NULL\r
489 };\r
490 \r
491 static void ms_vfw_detect(MSWebCamManager *obj){\r
492         int i;\r
493         MSWebCam *cam;\r
494         for (i = 0; i < VFW_ENGINE_MAX_INSTANCES; i++){\r
495                 VfwEngine *eng;\r
496                 if ((eng=vfw_engine_new(i))!=NULL){\r
497                         cam=ms_web_cam_new(&ms_vfw_cam_desc);\r
498                         cam->data=(void*)eng;/*store the engine */\r
499                         cam->name=ms_strdup(eng->dev);\r
500                         ms_web_cam_manager_add_cam(obj,cam);\r
501                 }\r
502         }\r
503         atexit(vfw_engines_free);\r
504 }\r
505 \r