diff --git a/linphone/mediastreamer2/src/Makefile.am b/linphone/mediastreamer2/src/Makefile.am index 1860d4c8c..907c181a6 100644 --- a/linphone/mediastreamer2/src/Makefile.am +++ b/linphone/mediastreamer2/src/Makefile.am @@ -1,5 +1,8 @@ -EXTRA_DIST= winsnd2.c winsnd.c winvideo.c winvideods.c wincevideods.c dxfilter.h dxfilter.cpp msfileplayer_win.c msfilerec_win.c winsndds.cpp nowebcamCIF.jpg winsnd3.c vfw-missing.h +EXTRA_DIST= winsnd2.c winsnd.c winvideo.c \ + winvideods.c wincevideods.c dxfilter.h dxfilter.cpp \ + msfileplayer_win.c msfilerec_win.c winsndds.cpp nowebcamCIF.jpg winsnd3.c vfw-missing.h \ + winvideo2.c BUILT_SOURCES=alldescs.h diff --git a/linphone/mediastreamer2/src/pixconv.c b/linphone/mediastreamer2/src/pixconv.c index 566834d57..a39b946dd 100644 --- a/linphone/mediastreamer2/src/pixconv.c +++ b/linphone/mediastreamer2/src/pixconv.c @@ -136,6 +136,10 @@ static void pixconv_process(MSFilter *f){ s->out_fmt,SWS_FAST_BILINEAR, NULL, NULL, NULL); } + if (s->in_fmt==PIX_FMT_BGR24){ + inbuf.data[0]+=inbuf.linesize[0]*(s->size.height-1); + inbuf.linesize[0]=-inbuf.linesize[0]; + } if (sws_scale(s->sws_ctx,inbuf.data,inbuf.linesize, 0, s->size.height, s->outbuf.planes, s->outbuf.strides)<0){ ms_error("MSPixConv: Error in sws_scale()."); diff --git a/linphone/mediastreamer2/src/winvideo2.c b/linphone/mediastreamer2/src/winvideo2.c new file mode 100755 index 000000000..f94a28c0f --- /dev/null +++ b/linphone/mediastreamer2/src/winvideo2.c @@ -0,0 +1,458 @@ +/* +mediastreamer2 library - modular sound and video processing and streaming +Copyright (C) 2006 Simon MORLAT (simon.morlat@linphone.org) + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "mediastreamer2/msvideo.h" +#include "mediastreamer2/msticker.h" +#include "mediastreamer2/msv4l.h" +#include "Vfw.h" +#include +#include + +#include "mediastreamer2/mswebcam.h" + +#ifndef _MSC_VER +#include "vfw-missing.h" +#endif + +typedef void (*queue_msg_t)(void *, mblk_t *); + +typedef struct VfwEngine{ + ms_thread_t thread; + char dev[512]; + int devidx; + HWND capvideo; + MSVideoSize vsize; + MSPixFmt pix_fmt; + queue_msg_t cb; + void *cb_data; + bool_t started; + bool_t thread_running; +}VfwEngine; + +#define VFW_ENGINE_MAX_INSTANCES 9 + +static VfwEngine *engines[VFW_ENGINE_MAX_INSTANCES]={0}; + +static bool_t try_format(VfwEngine *s, BITMAPINFO *videoformat, MSPixFmt pixfmt){ + bool_t ret; + capGetVideoFormat(s->capvideo, videoformat, sizeof(BITMAPINFO)); + videoformat->bmiHeader.biSizeImage = 0; + videoformat->bmiHeader.biWidth = s->vsize.width; + videoformat->bmiHeader.biHeight = s->vsize.height; + switch(pixfmt){ + case MS_YUV420P: + videoformat->bmiHeader.biBitCount = 12; + videoformat->bmiHeader.biCompression=MAKEFOURCC('I','4','2','0'); + break; + case MS_YUY2: + videoformat->bmiHeader.biBitCount = 16; + videoformat->bmiHeader.biCompression=MAKEFOURCC('Y','U','Y','2'); + break; + case MS_RGB24: + videoformat->bmiHeader.biBitCount = 24; + videoformat->bmiHeader.biCompression=BI_RGB; + break; + default: + return FALSE; + } + ret=capSetVideoFormat(s->capvideo, videoformat, sizeof(BITMAPINFO)); + if (ret) { + /*recheck video format */ + capGetVideoFormat(s->capvideo, videoformat, sizeof(BITMAPINFO)); + } + return ret; +} + +static int _vfw_engine_select_format(VfwEngine *obj){ + BITMAPINFO videoformat; + MSPixFmt driver_last; + char compname[5]; + + capGetVideoFormat(obj->capvideo, &videoformat, sizeof(BITMAPINFO)); + memcpy(compname,&videoformat.bmiHeader.biCompression,4); + ms_message("vfw: camera's current format is %s", compname); + driver_last=ms_fourcc_to_pix_fmt(videoformat.bmiHeader.biCompression); + if (driver_last!=MS_PIX_FMT_UNKNOWN && try_format(obj,&videoformat,driver_last)){ + ms_message("Using driver last setting"); + obj->pix_fmt=driver_last; + }else if (try_format(obj,&videoformat,MS_YUV420P)){ + obj->pix_fmt=MS_YUV420P; + ms_message("Using YUV420P"); + }else if (try_format(obj,&videoformat,MS_RGB24)){ + obj->pix_fmt=MS_RGB24; + ms_message("Using RGB24"); + }else if (try_format(obj,&videoformat,MS_YUY2)){ + obj->pix_fmt=MS_YUY2; + ms_message("Using YUY2"); + }else{ + ms_error("v4w: Failed to set any video format."); + return -1; + } + if (obj->pix_fmt==MS_RGB24){ + if (videoformat.bmiHeader.biHeight>0){ + obj->pix_fmt=MS_RGB24_REV; + } + } + return 0; +} + +static void dummy(void*p){ +} + +LRESULT CALLBACK vfw_engine_stream_callback(HWND hWnd, LPVIDEOHDR lpVHdr) +{ + VfwEngine *s; + mblk_t *buf; + int size; + + s = (VfwEngine *)capGetUserData(hWnd); + if (s==NULL) + return FALSE; + + size = lpVHdr->dwBufferLength; + if (size>0 && s->cb!=NULL && s->started){ + buf = esballoc(lpVHdr->lpData,size,0,dummy); + buf->b_wptr+=size; + s->cb(s->cb_data,buf); + } + return TRUE ; +} + +static void * +vfw_engine_thread(void *arg) +{ + VfwEngine *s=(VfwEngine*)arg; + MSG msg; + + while(s->thread_running) + { + BOOL fGotMessage; + if((fGotMessage = PeekMessage(&msg, (HWND) s->capvideo, 0, 0, PM_REMOVE)) != 0) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + else + Sleep(10); + } + ms_thread_exit(NULL); + return NULL; +} + +static void vfw_engine_destroy(VfwEngine *obj){ + if (!capCaptureStop(obj->capvideo)){ + ms_error("vfw: fail to stop capture !"); + } + obj->thread_running=FALSE; + ms_thread_join(obj->thread,NULL); + capDriverDisconnect(obj->capvideo); + DestroyWindow(obj->capvideo); +} + +static int _vfw_engine_setup(VfwEngine *obj){ + CAPTUREPARMS capparam ; + capCaptureGetSetup(obj->capvideo,&capparam,sizeof(capparam)) ; + capparam.dwRequestMicroSecPerFrame = 100000 ; + // detach capture from application + capparam.fYield = TRUE ; + capparam.fMakeUserHitOKToCapture = FALSE; + capparam.fAbortLeftMouse = FALSE; + capparam.fAbortRightMouse = FALSE; + capparam.wPercentDropForError = 90 ; + capparam.fCaptureAudio = FALSE ; + capparam.fAbortRightMouse = FALSE; + capparam.fAbortLeftMouse = FALSE; + capparam.AVStreamMaster = AVSTREAMMASTER_NONE ; + if (!capCaptureSetSetup(obj->capvideo,&capparam,sizeof(capparam))){ + ms_error("capCaptureSetSetup failed."); + return -1; + } + capSetUserData(obj->capvideo, obj); + return 0; +} + +static VfwEngine * vfw_engine_new(int i){ + char dev[512]; + char ver[512]; + VfwEngine *obj=ms_new0(VfwEngine,1); + if (capGetDriverDescription(i, dev, sizeof (dev), + ver, sizeof (ver))){ + MSVideoSize sz=MS_VIDEO_SIZE_CIF; + HWND hwnd=capCreateCaptureWindow("Capture Window",WS_CHILD /* WS_OVERLAPPED */ + ,0,0,sz.width,sz.height,HWND_MESSAGE, 0) ; + + if (hwnd==NULL) return NULL; + if(!capDriverConnect(hwnd,i)){ + ms_warning("vfw: could not connect to capture driver, no webcam connected."); + DestroyWindow(hwnd); + return NULL; + } + strcpy(obj->dev,dev); + obj->devidx=i; + obj->capvideo=hwnd; + obj->vsize=sz; + + if (_vfw_engine_setup(obj)==-1){ + vfw_engine_destroy(obj); + return NULL; + } + + if (_vfw_engine_select_format(obj)==-1){ + vfw_engine_destroy(obj); + return NULL; + } + capSetCallbackOnVideoStream(obj->capvideo, vfw_engine_stream_callback); + if (!capCaptureSequenceNoFile(obj->capvideo)){ + ms_error("vfw: fail to start capture !"); + } + ms_thread_create(&obj->thread,NULL,vfw_engine_thread,obj); + engines[i]=obj; + return obj; + } + return NULL; +} + +static MSPixFmt vfw_engine_get_pix_fmt(VfwEngine *obj){ + return obj->pix_fmt; +} + +static MSVideoSize vfw_engine_get_video_size(VfwEngine *obj){ + return obj->vsize; +} + +static void vfw_engine_set_callback(VfwEngine* obj, queue_msg_t cb, void *cb_data){ + obj->cb=cb; + obj->cb_data=cb_data; +} + +static void vfw_engine_start_capture(VfwEngine *obj){ + obj->started=TRUE; +} + +static void vfw_engine_stop_capture(VfwEngine *obj){ + obj->started=FALSE; +} + +static void vfw_engines_free(void){ + int i; + for(i=0;ivsize.width=MS_VIDEO_SIZE_CIF_W; + s->vsize.height=MS_VIDEO_SIZE_CIF_H; + qinit(&s->rq); + ms_mutex_init(&s->mutex,NULL); + s->start_time=0; + s->frame_count=-1; + s->fps=15; + f->data=s; +} + + + +static void vfw_uninit(MSFilter *f){ + VfwState *s=(VfwState*)f->data; + flushq(&s->rq,0); + ms_mutex_destroy(&s->mutex); + ms_free(s); +} + +static void vfw_callback(void *data, mblk_t *m){ + VfwState *s=(VfwState*)data; + ms_mutex_lock(&s->mutex); + putq(&s->rq,m); + ms_mutex_unlock(&s->mutex); +} + +static void vfw_preprocess(MSFilter * obj){ + VfwState *s=(VfwState*)obj->data; + if (s->eng==NULL) s->eng=engines[0]; + vfw_engine_set_callback(s->eng,vfw_callback,s); + vfw_engine_start_capture(s->eng); +} + +static void vfw_postprocess(MSFilter * obj){ + VfwState *s=(VfwState*)obj->data; + vfw_engine_stop_capture(s->eng); + flushq(&s->rq,0); +} + +static void vfw_process(MSFilter * obj){ + VfwState *s=(VfwState*)obj->data; + mblk_t *m; + uint32_t timestamp; + int cur_frame; + + if (s->frame_count==-1){ + s->start_time=obj->ticker->time; + s->frame_count=0; + } + + + cur_frame=((obj->ticker->time-s->start_time)*s->fps/1000.0); + if (cur_frame>s->frame_count){ + mblk_t *om=NULL; + /*keep the most recent frame if several frames have been captured */ + if (s->eng!=NULL){ + ms_mutex_lock(&s->mutex); + while((m=getq(&s->rq))!=NULL){ + ms_mutex_unlock(&s->mutex); + if (om!=NULL) freemsg(om); + om=m; + ms_mutex_lock(&s->mutex); + } + ms_mutex_unlock(&s->mutex); + } + if (om!=NULL){ + timestamp=obj->ticker->time*90;/* rtp uses a 90000 Hz clockrate for video*/ + mblk_set_timestamp_info(om,timestamp); + ms_queue_put(obj->outputs[0],om); + } + s->frame_count++; + } +} + +static int vfw_set_fps(MSFilter *f, void *arg){ + VfwState *s=(VfwState*)f->data; + s->fps=*((float*)arg); + return 0; +} + +static int vfw_get_pix_fmt(MSFilter *f,void *arg){ + VfwState *s=(VfwState*)f->data; + MSPixFmt fmt=vfw_engine_get_pix_fmt(s->eng); + *((MSPixFmt*)arg)=fmt; + return 0; +} + +static int vfw_set_vsize(MSFilter *f, void *arg){ + VfwState *s=(VfwState*)f->data; + s->vsize=*((MSVideoSize*)arg); + return 0; +} + +static int vfw_get_vsize(MSFilter *f, void *arg){ + VfwState *s=(VfwState*)f->data; + MSVideoSize *vs=(MSVideoSize*)arg; + *vs=vfw_engine_get_video_size(s->eng); + return 0; +} + +static MSFilterMethod methods[]={ + { MS_FILTER_SET_FPS , vfw_set_fps }, + { MS_FILTER_GET_PIX_FMT , vfw_get_pix_fmt }, + { MS_FILTER_SET_VIDEO_SIZE, vfw_set_vsize }, + { MS_FILTER_GET_VIDEO_SIZE, vfw_get_vsize }, + { 0 , NULL } +}; + +#ifdef _MSC_VER + +MSFilterDesc ms_vfw_desc={ + MS_VFW_ID, + "MSVfw", + "A video for windows (vfw.h) based source filter to grab pictures.", + MS_FILTER_OTHER, + NULL, + 0, + 1, + vfw_init, + vfw_preprocess, + vfw_process, + vfw_postprocess, + vfw_uninit, + methods +}; + +#else + +MSFilterDesc ms_vfw_desc={ + .id=MS_VFW_ID, + .name="MSVfw", + .text="A video for windows (vfw.h) based source filter to grab pictures.", + .ninputs=0, + .noutputs=1, + .category=MS_FILTER_OTHER, + .init=vfw_init, + .preprocess=vfw_preprocess, + .process=vfw_process, + .postprocess=vfw_postprocess, + .uninit=vfw_uninit, + .methods=methods +}; + +#endif + +MS_FILTER_DESC_EXPORT(ms_vfw_desc) + +static void ms_vfw_detect(MSWebCamManager *obj); + +static void ms_vfw_cam_init(MSWebCam *cam){ +} + + +static MSFilter *ms_vfw_create_reader(MSWebCam *obj){ + MSFilter *f= ms_filter_new_from_desc(&ms_vfw_desc); + VfwState *s=(VfwState*)f->data; + s->eng=(VfwEngine*)obj->data; + return f; +} + +MSWebCamDesc ms_vfw_cam_desc={ + "VideoForWindows grabber", + &ms_vfw_detect, + &ms_vfw_cam_init, + &ms_vfw_create_reader, + NULL +}; + +static void ms_vfw_detect(MSWebCamManager *obj){ + int i; + MSWebCam *cam; + for (i = 0; i < VFW_ENGINE_MAX_INSTANCES; i++){ + VfwEngine *eng; + if ((eng=vfw_engine_new(i))!=NULL){ + cam=ms_web_cam_new(&ms_vfw_cam_desc); + cam->data=(void*)eng;/*store the engine */ + cam->name=ms_strdup(eng->dev); + ms_web_cam_manager_add_cam(obj,cam); + } + } + atexit(vfw_engines_free); +} +