2 mediastreamer2 library - modular sound and video processing and streaming
3 Copyright (C) 2006 Simon MORLAT (simon.morlat@linphone.org)
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either version 2
8 of the License, or (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22 /* need to link with "dmoguids.lib strmiids.lib strmbase.lib atls.lib" */
24 #define _CE_ALLOW_SINGLE_THREADED_OBJECTS_IN_MTA
26 #include "mediastreamer2/msvideo.h"
27 #include "mediastreamer2/msticker.h"
28 #include "mediastreamer2/msv4l.h"
33 #include <ffmpeg/avcodec.h>
41 //#include <initguid.h>
49 typedef struct V4wState{
54 CComPtr<IGraphBuilder> m_pGraph;
55 CComPtr<ICaptureGraphBuilder2> m_pBuilder;
56 CComPtr<IMediaControl> m_pControl;
57 CDXFilter *m_pDXFilter;
58 CComPtr<IBaseFilter> m_pIDXFilter;
59 CComPtr<IBaseFilter> m_pNullRenderer;
60 CComPtr<IBaseFilter> m_pDeviceFilter;
74 bool_t startwith_yuv_bug; /* avoid bug with USB vimicro cards. */
77 static V4wState *s_callback=NULL;
79 static void dummy(void*p){
82 HRESULT ( Callback)(IMediaSample* pSample, REFERENCE_TIME* sTime, REFERENCE_TIME* eTime, BOOL changed)
87 V4wState *s = s_callback;
91 HRESULT hr = pSample->GetPointer(&byte_buf);
97 int size = pSample->GetActualDataLength();
101 memcpy(buf->b_wptr, byte_buf, size);
102 if (s->pix_fmt==MS_RGB24)
104 /* Conversion from top down bottom up (BGR to RGB and flip) */
105 unsigned long Index,nPixels;
112 nPixels=s->vsize.width*s->vsize.height;
115 for(Index=0;Index!=nPixels;Index++) // For each pixel
123 unsigned char *pLine1, *pLine2;
126 iLineLen=s->vsize.width*iPixelSize;
128 pLine2=&(buf->b_wptr)[iLineLen * (s->vsize.height - 1)];
130 for( ;pLine1<pLine2;pLine2-=(iLineLen*2))
132 for(iIndex=0;iIndex!=iLineLen;pLine1++,pLine2++,iIndex++)
142 ms_mutex_lock(&s->mutex);
144 ms_mutex_unlock(&s->mutex);
150 HRESULT GetFirstCameraDriver( WCHAR *pwzName ) {
152 HANDLE handle = NULL;
153 DEVMGR_DEVICE_INFORMATION di;
154 GUID guidCamera = { 0xCB998A05, 0x122C, 0x4166, 0x84, 0x6A,
155 0x93, 0x3E, 0x4D, 0x7E, 0x3C, 0x86 };
157 if( pwzName == NULL ) {
161 di.dwSize = sizeof(di);
163 handle = FindFirstDevice( DeviceSearchByGuid, &guidCamera, &di );
164 if(( handle == NULL ) || ( di.hDevice == NULL )) {
168 StringCchCopy( pwzName, MAX_PATH, di.szLegacyName );
181 class CPropertyBag : public IPropertyBag
187 HRESULT STDMETHODCALLTYPE
189 LPCOLESTR pszPropName,
195 HRESULT STDMETHODCALLTYPE
197 LPCOLESTR pszPropName,
201 ULONG STDMETHODCALLTYPE AddRef();
202 ULONG STDMETHODCALLTYPE Release();
203 HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppv);
210 CPropertyBag::CPropertyBag() : _refCount(1), pVar(0)
214 CPropertyBag::~CPropertyBag()
216 VAR_LIST *pTemp = pVar;
221 VAR_LIST *pDel = pTemp;
222 VariantClear(&pTemp->var);
223 SysFreeString(pTemp->pBSTRName);
224 pTemp = pTemp->pNext;
230 HRESULT STDMETHODCALLTYPE
231 CPropertyBag::Read(LPCOLESTR pszPropName,
233 IErrorLog *pErrorLog)
235 VAR_LIST *pTemp = pVar;
240 if(0 == wcscmp(pszPropName, pTemp->pBSTRName))
242 hr = VariantCopy(_pVar, &pTemp->var);
245 pTemp = pTemp->pNext;
251 HRESULT STDMETHODCALLTYPE
252 CPropertyBag::Write(LPCOLESTR pszPropName,
256 VAR_LIST *pTemp = new VAR_LIST();
261 return E_OUTOFMEMORY;
264 VariantInit(&pTemp->var);
265 pTemp->pBSTRName = SysAllocString(pszPropName);
268 return VariantCopy(&pTemp->var, _pVar);
271 ULONG STDMETHODCALLTYPE
272 CPropertyBag::AddRef()
274 return InterlockedIncrement((LONG *)&_refCount);
277 ULONG STDMETHODCALLTYPE
278 CPropertyBag::Release()
280 ASSERT(_refCount != 0xFFFFFFFF);
281 ULONG ret = InterlockedDecrement((LONG *)&_refCount);
287 HRESULT STDMETHODCALLTYPE
288 CPropertyBag::QueryInterface(REFIID riid, void** ppv)
292 if(riid == IID_IPropertyBag)
293 *ppv = static_cast<IPropertyBag*>(this);
295 return *ppv = 0, E_NOINTERFACE;
297 return AddRef(), S_OK;
300 static int v4w_open_videodevice(V4wState *s, int format, MSVideoSize *vsize)
306 HRESULT hr=s->m_pGraph.CoCreateInstance(CLSID_FilterGraph);
312 // get a CaptureGraphBuilder2
313 #if !defined(_WIN32_WCE)
314 hr=s->m_pBuilder.CoCreateInstance(CLSID_CaptureGraphBuilder2);
316 hr=s->m_pBuilder.CoCreateInstance(CLSID_CaptureGraphBuilder);
323 // connect capture graph builder with the graph
324 s->m_pBuilder->SetFiltergraph(s->m_pGraph);
326 // get mediacontrol so we can start and stop the filter graph
327 hr=s->m_pGraph.QueryInterface(&(s->m_pControl));
334 s->m_pDXFilter = new CDXFilter(NULL, &hr, FALSE);
335 if(s->m_pDXFilter==NULL)
339 s->m_pDXFilter->AddRef();
346 mt.SetType(&MEDIATYPE_Video);
348 if (format==MS_YUV420P)
350 GUID m = (GUID)FOURCCMap(MAKEFOURCC('I','4','2','0'));
352 mt.SetSubtype(&MEDIASUBTYPE_YV12);
354 else //if (format==MS_RGB24)
356 mt.SetSubtype(&MEDIASUBTYPE_RGB24);
359 //mt.SetSubtype(&MEDIASUBTYPE_IYUV);
360 //mt.SetSubtype(&MEDIASUBTYPE_YUYV);
361 //mt.SetSubtype(&MEDIASUBTYPE_RGB24);
362 //mt.SetSampleSize();
363 mt.formattype = FORMAT_VideoInfo;
364 mt.SetTemporalCompression(FALSE);
366 VIDEOINFO *pvi = (VIDEOINFO *)
367 mt.AllocFormatBuffer(sizeof(VIDEOINFO));
369 return E_OUTOFMEMORY;
370 ZeroMemory(pvi, sizeof(VIDEOINFO));
371 if (format==MS_YUV420P)
373 pvi->bmiHeader.biCompression = MAKEFOURCC('I','4','2','0');
374 pvi->bmiHeader.biCompression = MAKEFOURCC('Y','V','1','2');
375 pvi->bmiHeader.biBitCount = 12;
379 pvi->bmiHeader.biCompression = BI_RGB;
380 pvi->bmiHeader.biBitCount = 24;
382 pvi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
383 pvi->bmiHeader.biWidth = vsize->width;
384 pvi->bmiHeader.biHeight = vsize->height;
385 pvi->bmiHeader.biPlanes = 1;
386 pvi->bmiHeader.biSizeImage = GetBitmapSize(&pvi->bmiHeader);
387 pvi->bmiHeader.biClrImportant = 0;
388 mt.SetSampleSize(pvi->bmiHeader.biSizeImage);
389 mt.SetFormat((BYTE*)pvi, sizeof(VIDEOINFO));
391 hr = s->m_pDXFilter->SetAcceptedMediaType(&mt);
397 hr = s->m_pDXFilter->SetCallback(Callback);
403 hr = s->m_pDXFilter->QueryInterface(IID_IBaseFilter,
404 (LPVOID *)&s->m_pIDXFilter);
410 hr = s->m_pGraph->AddFilter(s->m_pIDXFilter, L"DXFilter Filter");
417 ICreateDevEnum *pCreateDevEnum = NULL;
418 IEnumMoniker *pEnumMoniker = NULL;
419 IMoniker *pMoniker = NULL;
423 hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER,
424 IID_ICreateDevEnum, (PVOID *)&pCreateDevEnum);
430 hr = pCreateDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory,
432 if (FAILED(hr) || pEnumMoniker == NULL) {
433 //printf("no device\n");
437 pEnumMoniker->Reset();
439 hr = pEnumMoniker->Next(1, &pMoniker, &nFetched);
440 if(FAILED(hr) || pMoniker==NULL)
445 hr = pMoniker->BindToObject(0, 0, IID_IBaseFilter, (void**)&s->m_pDeviceFilter );
451 s->m_pGraph->AddFilter(s->m_pDeviceFilter, L"Device Filter");
454 pEnumMoniker->Release();
455 pCreateDevEnum->Release();
457 WCHAR wzDeviceName[ MAX_PATH + 1 ];
458 CComVariant varCamName;
459 CPropertyBag PropBag;
460 CComPtr<IPersistPropertyBag> pPropertyBag;
461 GetFirstCameraDriver(wzDeviceName);
463 hr = s->m_pDeviceFilter.CoCreateInstance( CLSID_VideoCapture );
469 s->m_pDeviceFilter.QueryInterface( &pPropertyBag );
470 varCamName = wzDeviceName;
471 if(( varCamName.vt == VT_BSTR ) == NULL ) {
472 return E_OUTOFMEMORY;
474 PropBag.Write( L"VCapName", &varCamName );
475 pPropertyBag->Load( &PropBag, NULL );
476 pPropertyBag.Release();
478 hr = s->m_pGraph->AddFilter( s->m_pDeviceFilter, L"Video capture source" );
487 s->m_pNullRenderer = NULL;
489 hr=s->m_pNullRenderer.CoCreateInstance(CLSID_NullRenderer);
495 if (s->m_pNullRenderer!=NULL)
497 s->m_pGraph->AddFilter(s->m_pNullRenderer, L"Null Renderer");
500 hr = s->m_pBuilder->RenderStream(&PIN_CATEGORY_PREVIEW,
501 &MEDIATYPE_Video, s->m_pDeviceFilter, s->m_pIDXFilter, s->m_pNullRenderer);
504 //hr = s->m_pBuilder->RenderStream(&PIN_CATEGORY_CAPTURE,
505 // &MEDIATYPE_Video, s->m_pDeviceFilter, s->m_pIDXFilter, s->m_pNullRenderer);
512 //m_pDXFilter->SetBufferSamples(TRUE);
515 // Create the System Device Enumerator.
516 IFilterMapper *pMapper = NULL;
517 //IEnumMoniker *pEnum = NULL;
518 IEnumRegFilters *pEnum = NULL;
520 hr = CoCreateInstance(CLSID_FilterMapper,
521 NULL, CLSCTX_INPROC, IID_IFilterMapper,
526 // Error handling omitted for clarity.
529 GUID arrayInTypes[2];
530 arrayInTypes[0] = MEDIATYPE_Video;
531 arrayInTypes[1] = MEDIASUBTYPE_dvsd;
533 hr = pMapper->EnumMatchingFilters(
535 MERIT_HW_COMPRESSOR, // Minimum merit.
536 FALSE, // At least one input pin?
539 FALSE, // Must be a renderer?
540 FALSE, // At least one output pin?
544 // Enumerate the monikers.
545 //IMoniker *pMoniker;
548 while (pEnum->Next(1, &pMoniker, &cFetched) == S_OK)
550 IPropertyBag *pPropBag = NULL;
552 hr = pMoniker->BindToStorage(0, 0, IID_IPropertyBag,
557 // To retrieve the friendly name of the filter, do the following:
559 VariantInit(&varName);
560 hr = pPropBag->Read(L"FriendlyName", &varName, 0);
563 // Display the name in your UI somehow.
565 VariantClear(&varName);
567 // To create an instance of the filter, do the following:
568 IBaseFilter *pFilter;
569 hr = pMoniker->BindToObject(NULL, NULL, IID_IBaseFilter, (void**)&pFilter);
570 // Now add the filter to the graph. Remember to release pFilter later.
588 hr = s->m_pControl->Run();
596 s->vsize.height = vsize->height;
597 s->vsize.width = vsize->width;
601 static void v4w_init(MSFilter *f){
602 V4wState *s=(V4wState *)ms_new0(V4wState,1);
604 s->vsize.width=MS_VIDEO_SIZE_CIF_W;
605 s->vsize.height=MS_VIDEO_SIZE_CIF_H;
606 //s->pix_fmt=MS_RGB24;
607 s->pix_fmt=MS_YUV420P;
614 s->m_pIDXFilter=NULL;
615 s->m_pDeviceFilter=NULL;
618 for (idx=0;idx<10;idx++)
622 ms_mutex_init(&s->mutex,NULL);
630 static int try_format(V4wState *s, int format, MSVideoSize *vsize)
632 int i = v4w_open_videodevice(s, format, vsize);
635 if (s->m_pNullRenderer!=NULL)
636 s->m_pGraph->RemoveFilter(s->m_pNullRenderer);
637 if (s->m_pIDXFilter!=NULL)
638 s->m_pGraph->RemoveFilter(s->m_pIDXFilter);
639 if (s->m_pDeviceFilter!=NULL)
640 s->m_pGraph->RemoveFilter(s->m_pDeviceFilter);
643 s->m_pIDXFilter=NULL;
644 if (s->m_pDXFilter!=NULL)
645 s->m_pDXFilter->Release();
648 s->m_pNullRenderer=NULL;
649 s->m_pDeviceFilter=NULL;
655 static int _v4w_start(V4wState *s, void *arg)
657 MSVideoSize try_vsize;
662 if (s->pix_fmt==MS_YUV420P)
663 tryformat = MS_RGB24;
664 else if (s->pix_fmt==MS_RGB24)
665 tryformat = MS_YUV420P;
667 try_vsize.height = s->vsize.height;
668 try_vsize.width = s->vsize.width;
669 i = try_format(s, s->pix_fmt, &try_vsize);
672 /* try second format with same size */
673 i = try_format(s, tryformat, &try_vsize);
676 /* try both format with CIF size */
677 if (i==-14 && s->vsize.height!=MS_VIDEO_SIZE_CIF_H)
679 try_vsize.height = MS_VIDEO_SIZE_CIF_H;
680 try_vsize.width = MS_VIDEO_SIZE_CIF_W;
681 i = try_format(s, s->pix_fmt, &try_vsize);
684 i = try_format(s, tryformat, &try_vsize);
687 if (i==-14 && s->vsize.height!=MS_VIDEO_SIZE_QCIF_H)
689 try_vsize.height = MS_VIDEO_SIZE_QCIF_H;
690 try_vsize.width = MS_VIDEO_SIZE_QCIF_W;
691 i = try_format(s, s->pix_fmt, &try_vsize);
694 i = try_format(s, tryformat, &try_vsize);
697 if (i==-14 && s->vsize.height!=MS_VIDEO_SIZE_VGA_H)
699 try_vsize.height = MS_VIDEO_SIZE_VGA_H;
700 try_vsize.width = MS_VIDEO_SIZE_VGA_W;
701 i = try_format(s, s->pix_fmt, &try_vsize);
704 i = try_format(s, tryformat, &try_vsize);
708 if (i==-14 && s->vsize.height!=MS_VIDEO_SIZE_QVGA_H)
710 try_vsize.height = MS_VIDEO_SIZE_QVGA_H;
711 try_vsize.width = MS_VIDEO_SIZE_QVGA_W;
712 i = try_format(s, s->pix_fmt, &try_vsize);
715 i = try_format(s, tryformat, &try_vsize);
721 if (s->pix_fmt==MS_YUV420P)
722 ms_message("Using YUV420P");
723 else if (s->pix_fmt==MS_RGB24)
724 ms_message("Using RGB24");
727 if (s->rotregvalue==0){
728 //RemoveGraphFromRot(s->rotregvalue);
729 if (s->m_pNullRenderer!=NULL)
730 s->m_pGraph->RemoveFilter(s->m_pNullRenderer);
731 if (s->m_pIDXFilter!=NULL)
732 s->m_pGraph->RemoveFilter(s->m_pIDXFilter);
733 if (s->m_pDeviceFilter!=NULL)
734 s->m_pGraph->RemoveFilter(s->m_pDeviceFilter);
737 s->m_pIDXFilter=NULL;
738 if (s->m_pDXFilter!=NULL)
739 s->m_pDXFilter->Release();
742 s->m_pNullRenderer=NULL;
743 s->m_pDeviceFilter=NULL;
747 ms_message("v4w: graph not started (err=%i)", i);
753 static int _v4w_stop(V4wState *s, void *arg){
755 if (s->rotregvalue>0){
756 HRESULT hr = s->m_pControl->Stop();
759 ms_message("v4w: could not stop graph");
761 if (s->m_pNullRenderer!=NULL)
762 s->m_pGraph->RemoveFilter(s->m_pNullRenderer);
763 if (s->m_pIDXFilter!=NULL)
764 s->m_pGraph->RemoveFilter(s->m_pIDXFilter);
765 if (s->m_pDeviceFilter!=NULL)
766 s->m_pGraph->RemoveFilter(s->m_pDeviceFilter);
767 //RemoveGraphFromRot(s->rotregvalue);
770 s->m_pIDXFilter=NULL;
771 if (s->m_pDXFilter!=NULL)
772 s->m_pDXFilter->Release();
775 s->m_pNullRenderer=NULL;
776 s->m_pDeviceFilter=NULL;
780 ms_message("v4w: graph destroyed");
787 static int v4w_start(MSFilter *f, void *arg){
788 V4wState *s=(V4wState*)f->data;
793 static int v4w_stop(MSFilter *f, void *arg){
794 V4wState *s=(V4wState*)f->data;
800 static void v4w_uninit(MSFilter *f){
801 V4wState *s=(V4wState*)f->data;
804 ms_mutex_destroy(&s->mutex);
805 for (idx=0;idx<10;idx++)
807 if (s->mire[idx]==NULL)
809 freemsg(s->mire[idx]);
811 if (s->rotregvalue>0){
812 HRESULT hr = s->m_pControl->Stop();
815 ms_message("v4w: could not stop graph");
817 if (s->m_pNullRenderer!=NULL)
818 s->m_pGraph->RemoveFilter(s->m_pNullRenderer);
819 if (s->m_pIDXFilter!=NULL)
820 s->m_pGraph->RemoveFilter(s->m_pIDXFilter);
821 if (s->m_pDeviceFilter!=NULL)
822 s->m_pGraph->RemoveFilter(s->m_pDeviceFilter);
823 //RemoveGraphFromRot(s->rotregvalue);
826 s->m_pIDXFilter=NULL;
827 if (s->m_pDXFilter!=NULL)
828 s->m_pDXFilter->Release();
831 s->m_pNullRenderer=NULL;
832 s->m_pDeviceFilter=NULL;
836 ms_message("v4w: graph destroyed");
842 static mblk_t * v4w_make_nowebcam(V4wState *s){
843 #if defined(_WIN32_WCE)
848 if (s->mire[0]==NULL && s->frame_ind==0){
849 /* load several images to fake a movie */
850 for (idx=0;idx<10;idx++)
852 s->mire[idx]=ms_load_nowebcam(&s->vsize, idx);
853 if (s->mire[idx]==NULL)
857 s->mire[0]=ms_load_nowebcam(&s->vsize, -1);
859 for (count=0;count<10;count++)
861 if (s->mire[count]==NULL)
869 idx = s->frame_ind%count;
870 if (s->mire[idx]!=NULL)
876 static void v4w_preprocess(MSFilter * obj){
877 V4wState *s=(V4wState*)obj->data;
879 if (s->rotregvalue==0)
883 static void v4w_postprocess(MSFilter * obj){
884 V4wState *s=(V4wState*)obj->data;
888 static void v4w_process(MSFilter * obj){
889 V4wState *s=(V4wState*)obj->data;
894 if (s->frame_count==-1){
895 s->start_time=obj->ticker->time;
900 cur_frame=((obj->ticker->time-s->start_time)*s->fps/1000.0);
901 if (cur_frame>s->frame_count){
903 ms_mutex_lock(&s->mutex);
904 /*keep the most recent frame if several frames have been captured */
905 if (s->rotregvalue!=0){
906 while((m=getq(&s->rq))!=NULL){
907 if (om!=NULL) freemsg(om);
911 mblk_t *nowebcam = v4w_make_nowebcam(s);
915 ms_mutex_unlock(&s->mutex);
917 timestamp=obj->ticker->time*90;/* rtp uses a 90000 Hz clockrate for video*/
918 mblk_set_timestamp_info(om,timestamp);
919 ms_queue_put(obj->outputs[0],om);
920 /*ms_message("picture sent");*/
928 static int v4w_set_fps(MSFilter *f, void *arg){
929 V4wState *s=(V4wState*)f->data;
930 s->fps=*((float*)arg);
934 static int v4w_get_pix_fmt(MSFilter *f,void *arg){
935 V4wState *s=(V4wState*)f->data;
936 *((MSPixFmt*)arg) = (MSPixFmt)s->pix_fmt;
940 static int v4w_set_vsize(MSFilter *f, void *arg){
941 V4wState *s=(V4wState*)f->data;
942 s->vsize=*((MSVideoSize*)arg);
946 static int v4w_get_vsize(MSFilter *f, void *arg){
947 V4wState *s=(V4wState*)f->data;
948 MSVideoSize *vs=(MSVideoSize*)arg;
949 vs->width=s->vsize.width;
950 vs->height=s->vsize.height;
954 static MSFilterMethod methods[]={
955 { MS_FILTER_SET_FPS , v4w_set_fps },
956 { MS_FILTER_GET_PIX_FMT , v4w_get_pix_fmt },
957 { MS_FILTER_SET_VIDEO_SIZE, v4w_set_vsize },
958 { MS_FILTER_GET_VIDEO_SIZE, v4w_get_vsize },
959 { MS_V4L_START , v4w_start },
960 { MS_V4L_STOP , v4w_stop },
966 MSFilterDesc ms_v4w_desc={
969 N_("A video4windows compatible source filter to stream pictures."),
984 MSFilterDesc ms_v4w_desc={
987 .text=N_("A video4windows compatible source filter to stream pictures."),
990 .category=MS_FILTER_OTHER,
992 .preprocess=v4w_preprocess,
993 .process=v4w_process,
994 .postprocess=v4w_postprocess,
1001 MS_FILTER_DESC_EXPORT(ms_v4w_desc)