2 mediastreamer2 library - modular sound and video processing and streaming
\r
3 Copyright (C) 2006 Simon MORLAT (simon.morlat@linphone.org)
\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
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
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
20 #include "mediastreamer2/msvideo.h"
\r
21 #include "mediastreamer2/msticker.h"
\r
22 #include "mediastreamer2/msv4l.h"
\r
24 #include <winuser.h>
\r
25 #include <Windows.h>
\r
27 #include "mediastreamer2/mswebcam.h"
\r
29 #ifndef AVSTREAMMASTER_NONE
\r
30 #define AVSTREAMMASTER_NONE 1
\r
33 typedef void (*queue_msg_t)(void *, mblk_t *);
\r
35 typedef struct VfwEngine{
\r
46 bool_t thread_running;
\r
49 #define VFW_ENGINE_MAX_INSTANCES 9
\r
51 static VfwEngine *engines[VFW_ENGINE_MAX_INSTANCES]={0};
\r
53 static bool_t try_format(VfwEngine *s, BITMAPINFO *videoformat, MSPixFmt pixfmt){
\r
54 MSVideoSize tried_size=s->vsize;
\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
63 videoformat->bmiHeader.biBitCount = 12;
\r
64 videoformat->bmiHeader.biCompression=MAKEFOURCC('I','4','2','0');
\r
67 videoformat->bmiHeader.biBitCount = 16;
\r
68 videoformat->bmiHeader.biCompression=MAKEFOURCC('Y','U','Y','2');
\r
71 videoformat->bmiHeader.biBitCount = 24;
\r
72 videoformat->bmiHeader.biCompression=BI_RGB;
\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
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
90 static int _vfw_engine_select_format(VfwEngine *obj){
\r
91 BITMAPINFO videoformat;
\r
92 MSPixFmt driver_last;
\r
95 capGetVideoFormat(obj->capvideo, &videoformat, sizeof(BITMAPINFO));
\r
96 memcpy(compname,&videoformat.bmiHeader.biCompression,4);
\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
114 ms_error("v4w: Failed to set any video format.");
\r
117 if (obj->pix_fmt==MS_RGB24){
\r
118 if (videoformat.bmiHeader.biHeight>0){
\r
119 obj->pix_fmt=MS_RGB24_REV;
\r
125 static void dummy(void*p){
\r
128 LRESULT CALLBACK vfw_engine_stream_callback(HWND hWnd, LPVIDEOHDR lpVHdr)
\r
134 s = (VfwEngine *)capGetUserData(hWnd);
\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
142 s->cb(s->cb_data,buf);
\r
148 vfw_engine_thread(void *arg)
\r
150 VfwEngine *s=(VfwEngine*)arg;
\r
153 while(s->thread_running)
\r
156 while((fGotMessage = PeekMessage(&msg, (HWND) s->capvideo, 0, 0, PM_REMOVE)) != 0)
\r
158 TranslateMessage(&msg);
\r
159 DispatchMessage(&msg);
\r
163 ms_thread_exit(NULL);
\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
171 obj->thread_running=FALSE;
\r
172 ms_thread_join(obj->thread,NULL);
\r
173 obj->configured=FALSE;
\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
182 static void vfw_engine_destroy(VfwEngine *obj){
\r
183 if (obj->configured){
\r
184 _vfw_engine_unconfigure(obj);
\r
186 _vfw_engine_disconnect(obj);
\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
208 capSetUserData(obj->capvideo, obj);
\r
212 static int _vfw_engine_connect(VfwEngine *obj){
\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
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
225 obj->capvideo=hwnd;
\r
230 static VfwEngine * vfw_engine_new(int i){
\r
233 VfwEngine *obj=(VfwEngine*)ms_new0(VfwEngine,1);
\r
234 if (capGetDriverDescription(i, dev, sizeof (dev),
\r
235 ver, sizeof (ver))){
\r
237 if (_vfw_engine_connect(obj)==-1){
\r
241 strcpy(obj->dev,dev);
\r
248 static void _vfw_engine_configure(VfwEngine *obj){
\r
249 if (_vfw_engine_setup(obj)==-1){
\r
252 if (_vfw_engine_select_format(obj)==-1){
\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
259 ms_thread_create(&obj->thread,NULL,vfw_engine_thread,obj);
\r
260 obj->configured=TRUE;
\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
269 static MSVideoSize vfw_engine_get_video_size(VfwEngine *obj){
\r
273 static void vfw_engine_set_video_size(VfwEngine *obj, MSVideoSize vsize){
\r
274 if (!obj->configured)
\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
281 _vfw_engine_configure(obj);
\r
285 static void vfw_engine_set_callback(VfwEngine* obj, queue_msg_t cb, void *cb_data){
\r
287 obj->cb_data=cb_data;
\r
290 static void vfw_engine_start_capture(VfwEngine *obj){
\r
291 if (!obj->configured) _vfw_engine_configure(obj);
\r
295 static void vfw_engine_stop_capture(VfwEngine *obj){
\r
296 obj->started=FALSE;
\r
299 static void vfw_engines_free(void){
\r
301 for(i=0;i<VFW_ENGINE_MAX_INSTANCES;++i){
\r
303 vfw_engine_destroy(engines[i]);
\r
308 typedef struct VfwState{
\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
326 ms_mutex_init(&s->mutex,NULL);
\r
335 static void vfw_uninit(MSFilter *f){
\r
336 VfwState *s=(VfwState*)f->data;
\r
338 ms_mutex_destroy(&s->mutex);
\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
346 ms_mutex_unlock(&s->mutex);
\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
356 static void vfw_postprocess(MSFilter * obj){
\r
357 VfwState *s=(VfwState*)obj->data;
\r
358 vfw_engine_stop_capture(s->eng);
\r
362 static void vfw_process(MSFilter * obj){
\r
363 VfwState *s=(VfwState*)obj->data;
\r
365 uint32_t timestamp;
\r
368 if (s->frame_count==-1){
\r
369 s->start_time=(float)obj->ticker->time;
\r
373 cur_frame=(int)((obj->ticker->time-s->start_time)*s->fps/1000.0);
\r
374 if (cur_frame>s->frame_count){
\r
376 /*keep the most recent frame if several frames have been captured */
\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
383 ms_mutex_lock(&s->mutex);
\r
385 ms_mutex_unlock(&s->mutex);
\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
396 static int vfw_set_fps(MSFilter *f, void *arg){
\r
397 VfwState *s=(VfwState*)f->data;
\r
398 s->fps=*((float*)arg);
\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
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
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
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
433 MSFilterDesc ms_vfw_desc={
\r
436 N_("A video for windows (vfw.h) based source filter to grab pictures."),
\r
451 MSFilterDesc ms_vfw_desc={
\r
454 .text=N_("A video for windows (vfw.h) based source filter to grab pictures."),
\r
457 .category=MS_FILTER_OTHER,
\r
459 .preprocess=vfw_preprocess,
\r
460 .process=vfw_process,
\r
461 .postprocess=vfw_postprocess,
\r
462 .uninit=vfw_uninit,
\r
468 MS_FILTER_DESC_EXPORT(ms_vfw_desc)
\r
470 static void ms_vfw_detect(MSWebCamManager *obj);
\r
472 static void ms_vfw_cam_init(MSWebCam *cam){
\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
483 MSWebCamDesc ms_vfw_cam_desc={
\r
484 "VideoForWindows grabber",
\r
487 &ms_vfw_create_reader,
\r
491 static void ms_vfw_detect(MSWebCamManager *obj){
\r
494 for (i = 0; i < VFW_ENGINE_MAX_INSTANCES; i++){
\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
503 atexit(vfw_engines_free);
\r