/*
// Based on C++ code examples by: Rajeev K R Pala
// http://www.codeproject.com/Articles/80413/Streaming-Server-using-Direct-Show-and-Windows-Med
// http://www.codeproject.com/Articles/43024/Capture-image-from-a-streaming-URL-using-different

#include "windows.h"
#include "dxtrans.h"
#include "qedit.h"
#include "dshow.h"
#include "dshowasf.h"
#include "wmsdk.h"
#include "wmsysprf.h"

#define EXPORT __declspec(dllexport) __stdcall
#define msg(a) MessageBox(0, a, "Debug", 0)
#define msgw(a) MessageBoxW(0, a, L"Debug", 0)
#define SafeRelease(a) if(a){a->Release(); a = NULL;}
#define ReturnError return DSStopStream(m)

typedef struct tagDSParams
{
	IGraphBuilder		*pGraph;
	IBaseFilter			*pVidDeviceFilter;
	IBaseFilter			*pAudDeviceFilter;
	IBaseFilter			*pSmartTee;		// Smart Tee filter
	IBaseFilter			*pGrabFilter;	// Sample grabber filter
	ISampleGrabber		*pGrab;			// Sample grabber interface
	IBaseFilter			*pVideo;		// Video Renderer filter
	IVMRWindowlessControl *pVideoControl; // Video Renderer control interface
	IVideoWindow		*pWindow;		// Video window interface
	IMediaControl		*pMediaControl;
	IMediaEvent			*pEvent;		// Media Event interface for capture the media events
	IBaseFilter			*pWMASFWritter;	// IBase Filter for WMASFWritter 
	IFileSinkFilter		*pFileSinkFilter;
	IWMWriterAdvanced2	*pWriter2;		// Windows media writer advanced from WMF
	IWMWriterNetworkSink *pNetSink;
	IServiceProvider	*pProvider;
	LPSTR				Error;
}DSParams;

//-----------------------------------------------------------------------------

HRESULT AddFilterByCLSID(IGraphBuilder *pGraph, const GUID& clsid, LPCWSTR wszName, IBaseFilter **ppF)
{
	
	try{
		if (!pGraph || !ppF)
			return E_POINTER;
		*ppF = 0;
		IBaseFilter *pF = 0;
		HRESULT hr;
		if (SUCCEEDED(hr = CoCreateInstance(clsid, 0, CLSCTX_INPROC_SERVER,
			IID_IBaseFilter, reinterpret_cast<void**>(&pF))))
		{
			if (SUCCEEDED(hr = pGraph->AddFilter(pF, wszName)))
				*ppF = pF;
			else
				pF->Release();
		}
		return hr;
	}catch(...){
		return E_FAIL;
	}

}

HRESULT GetPin(IBaseFilter *pFilter, PIN_DIRECTION PinDir, IPin **ppPin, int Count = 1){
	
	try{
		IEnumPins  *pEnum;
		IPin       *pPin;
		
		HRESULT hr = pFilter->EnumPins(&pEnum);
		if(FAILED(hr))
			return NULL;
		
		while(pEnum->Next(1, &pPin, 0) == S_OK)
		{
			PIN_DIRECTION PinDirThis;
			pPin->QueryDirection(&PinDirThis);
			if(PinDirThis == PinDir)
			{
				Count--;
				if(Count <= 0)
				{
					pEnum->Release();
					*ppPin = pPin;
					return S_OK;
				}
			}
			pPin->Release();
		}
		pEnum->Release();
		return E_FAIL;  
	}catch(...){
		return E_FAIL;
	}

}

HRESULT GetPinByName(IBaseFilter *pFilter, LPCOLESTR pinName, IPin **ppPin){
	
	try{
		IEnumPins*	pEnum;
		IPin*		pPin;

		HRESULT hr = pFilter->EnumPins(&pEnum);
		if(FAILED(hr))
			return hr;

		while(pEnum->Next(1, &pPin, 0) == S_OK){
			PIN_INFO pInfo;
			pPin->QueryPinInfo(&pInfo);
			BOOL found = !_wcsicmp(pInfo.achName, pinName);
			if(pInfo.pFilter) 
				pInfo.pFilter->Release();
			if(found){
				*ppPin = pPin;
				return S_OK;
			}
			pPin->Release();
		}
		return E_FAIL;  
	}catch(...){
		return E_FAIL;  
	}

}

HRESULT ConnectFilters(IGraphBuilder *pGraph, IBaseFilter *pFirst, IBaseFilter *pSecond, LPCOLESTR strFilterPin = NULL){
	
	try{
		IPin *pOut = NULL, *pIn = NULL;
		HRESULT hr = 0;
		
		if(strFilterPin == NULL)
			hr = GetPin(pSecond, PINDIR_INPUT, &pIn); // Get input pin
		else
			hr = GetPinByName(pSecond, strFilterPin, &pIn); // Get pin by name
		if(FAILED(hr))
			return hr;
		
		// The previous filter may have multiple outputs, so try each one!
		IEnumPins *pEnum;
		pFirst->EnumPins(&pEnum);
		while(pEnum->Next(1, &pOut, 0) == S_OK){
			PIN_DIRECTION PinDirThis;
			pOut->QueryDirection(&PinDirThis);
			if(PinDirThis == PINDIR_OUTPUT){
				hr = pGraph->Connect(pOut, pIn);
				//switch(hr){
				//	case S_OK:					Error = "Success"; break;
				//	case VFW_S_PARTIAL_RENDER:	Error = "VFW_S_PARTIAL_RENDER"; break;
				//	case E_ABORT:				Error = "E_ABORT"; break;
				//	case E_POINTER:				Error = "E_POINTER"; break;
				//	case VFW_E_CANNOT_CONNECT:	Error = "VFW_E_CANNOT_CONNECT"; break;
				//	case VFW_E_NOT_IN_GRAPH:	Error = "VFW_E_NOT_IN_GRAPH"; break;
				//}		
				if(!FAILED(hr))
					break;
			}
			pOut->Release();
		}
		pEnum->Release();
		pIn->Release();
		pOut->Release();
		return hr;
	}catch(...){
		return E_FAIL;
	}

}

//-----------------------------------------------------------------------------

int DSGetDevices(REFCLSID clsidDeviceClass, LPSTR strDevName, IBaseFilter **pFilter, int Count = 1){
	
	try{
		//CoInitialize(NULL);
		int id = 0;
		ULONG cRetrieved;
		IMoniker *pM;
		BOOL GetList = *strDevName == 0;
		
		// Enumerate all video capture devices
		ICreateDevEnum *pCreateDevEnum;
		HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER,
			IID_ICreateDevEnum, (void**)&pCreateDevEnum);
		if(FAILED(hr) || (pCreateDevEnum == NULL))
			return FALSE;
		
		IEnumMoniker *pEm;
		hr = pCreateDevEnum->CreateClassEnumerator(clsidDeviceClass, &pEm, 0);
		if(FAILED(hr) || (pEm == NULL))
			return FALSE;
		pEm->Reset();
		
		while(hr = pEm->Next(1, &pM, &cRetrieved), hr==S_OK){
			IPropertyBag *pBag;
			hr = pM->BindToStorage(0, 0, IID_IPropertyBag, (void **)&pBag);
			if(SUCCEEDED(hr) && (pBag != NULL)){
				VARIANT var;
				var.vt = VT_BSTR;
				hr = pBag->Read(L"FriendlyName", &var, NULL);
				if(hr == NOERROR){
					TCHAR strDev[256];
					WideCharToMultiByte(CP_ACP, 0, var.bstrVal, -1, strDev, 256, NULL, NULL);
					if(GetList){
						StringCchPrintf(strDevName, 256, "%s\r\n", strDev);
						strDevName += strlen(strDevName);
					//}else if(!strcmp(strDevName, strDev))
					}else if(!strncmp(strDevName, strDev, strlen(strDevName))){
						Count--;
						if(Count <= 0){
							pM->BindToObject(0, 0, IID_IBaseFilter, (void**)pFilter);
							break;
						}
					}
					SysFreeString(var.bstrVal);
				}
				pBag->Release();
			}
			pM->Release();
		}
		pEm->Release();
		pCreateDevEnum->Release();
		return TRUE;
	}catch(...){
		return FALSE;
	}

}

int EXPORT DSGetVideoDevices(LPSTR strDevName, IBaseFilter **pFilter, int Count = 1){
	
	return DSGetDevices(CLSID_VideoInputDeviceCategory, strDevName, pFilter, Count);

}

int EXPORT DSGetAudioDevices(LPSTR strDevName, IBaseFilter **pFilter, int Count = 1){
	
	return DSGetDevices(CLSID_AudioInputDeviceCategory, strDevName, pFilter, Count);

}

int EXPORT DSResizeWindow(DSParams *m, int Left, int Top, int Width, int Height){
	
	try{
		HRESULT hr = S_FALSE;
		if(m->pWindow)
			hr = m->pWindow->SetWindowPosition((long)Left, (long)Top, (long)Width, (long)Height);
//		if(m->pVideoControl){
//		//if(m->pWindow){
//			
//			RECT Dest;
//			int nWidth, nHeight, nARWidth, nARHeight;
//			m->pVideoControl->GetNativeVideoSize((LONG*)&nWidth, (LONG*)&nHeight, (LONG*)&nARWidth, (LONG*)&nARHeight);
//			//int nWidth, nHeight;
//			//m->pWindow->GetMaxIdealImageSize((long*)&nWidth, (long*)&nHeight);
//			
//			if(!Width && !Height){
//				Height = nHeight; Width = nWidth;
//			}else if(!Height)
//				Height = Width * nHeight / nWidth;
//			else if(!Width)
//				Width = Height * nWidth / nHeight;
//			
//			Dest.left = Left; Dest.top = Top; Dest.right = Left + Width; Dest.bottom = Top + Height;
//			hr = m->pVideoControl->SetVideoPosition(NULL, &Dest);
//			//hr = m->pWindow->SetWindowPosition((long)Left, (long)Top, (long)Width, (long)Height);
//		}
		return SUCCEEDED(hr);
	}catch(...){
		return FALSE;
	}

}

int EXPORT DSStopStream(DSParams *m){
	
	try{
		HRESULT hr = S_FALSE;

		if(m->pMediaControl)
			hr = m->pMediaControl->StopWhenReady();	
		if(m->pNetSink)
			hr = m->pNetSink->Close();
		if(m->pWindow){
			hr = m->pWindow->put_Visible(OAFALSE);
			hr = m->pWindow->put_Owner(NULL);
			}
		SafeRelease(m->pGraph);
		SafeRelease(m->pVidDeviceFilter);
		SafeRelease(m->pAudDeviceFilter);
		SafeRelease(m->pSmartTee);
		SafeRelease(m->pGrabFilter);
		SafeRelease(m->pGrab);
		SafeRelease(m->pVideo);
		SafeRelease(m->pVideoControl);
		SafeRelease(m->pWindow);
		SafeRelease(m->pMediaControl);
		SafeRelease(m->pEvent);
		SafeRelease(m->pWMASFWritter);
		SafeRelease(m->pFileSinkFilter);
		SafeRelease(m->pWriter2);
		SafeRelease(m->pNetSink);
		SafeRelease(m->pProvider);
		
		return FALSE;
	}catch(...){
		return FALSE;
	}

}

int EXPORT DSStartStream(DSParams *m, 
		LPSTR strVidDevice, LPSTR strAudDevice, DWORD dwPort, int VidCount, int AudCount, 
		int Quality, LPSTR strUrl, HWND hWnd, int *Left, int *Top, int *Width, int *Height){
	
	try{
		// Create the graph
		HRESULT hr;
		m->Error = "Invalid parameters";
		if(!hWnd && !strUrl)
			ReturnError;
		//m->Error = "Initialize COM failed";
		//if(FAILED(hr = CoInitialize(NULL))) 
			//ReturnError;
		m->Error = "Create GraphBuilder failed";
		if(FAILED(hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, IID_IGraphBuilder, (void **)&m->pGraph)))
			ReturnError;
		
		// Get the input devices and add to graph
		m->Error = "Get video capture device failed";
		if(!strVidDevice)
			ReturnError;
		if(!DSGetVideoDevices(strVidDevice, &m->pVidDeviceFilter, VidCount))
			ReturnError;
		m->Error = "Add video capture filter failed";
		if(FAILED(hr = m->pGraph->AddFilter(m->pVidDeviceFilter, L"Vid Capture Filter"))) 
			ReturnError;
		if(strAudDevice){
			m->Error = "Get audio capture device failed";
			if(!DSGetAudioDevices(strAudDevice, &m->pAudDeviceFilter, AudCount))
				ReturnError;
			m->Error = "Add audio capture filter failed";
			if(FAILED(hr = m->pGraph->AddFilter(m->pAudDeviceFilter, L"Aud Capture Filter"))) 
				ReturnError;
		}
		
		if(strUrl && strAudDevice){
			// Create ASFWriter object and setup video streaming
			m->Error = "Create ASFWrite failed";
			if(FAILED(hr = CoCreateInstance(CLSID_WMAsfWriter, NULL, CLSCTX_INPROC_SERVER, 
					IID_IBaseFilter, (void **) &m->pWMASFWritter))) 
				ReturnError;
			m->Error = "Add ASFWrite filter failed";
			if(FAILED(hr = m->pGraph->AddFilter(m->pWMASFWritter, L"ASF Writter"))) 
				ReturnError;
			if(Quality){
				m->Error = "Configure ASFWrite filter failed";
				IConfigAsfWriter *pConfig;
				if(SUCCEEDED(hr = m->pWMASFWritter->QueryInterface(IID_IConfigAsfWriter, (void**)&pConfig))){
					switch(Quality){
						case 1: hr = pConfig->ConfigureFilterUsingProfileGuid(WMProfile_V80_56Video); break;
						case 2: hr = pConfig->ConfigureFilterUsingProfileGuid(WMProfile_V80_100Video); break;
						case 3: hr = pConfig->ConfigureFilterUsingProfileGuid(WMProfile_V80_256Video); break;
						case 4: hr = pConfig->ConfigureFilterUsingProfileGuid(WMProfile_V80_768Video); break;
					}
					pConfig->Release();
				}
			}
			m->Error = "Query FileSinkFilter failed";
			if(FAILED(hr = m->pWMASFWritter->QueryInterface(IID_IFileSinkFilter, (void**)&m->pFileSinkFilter))) 
				ReturnError;
			WCHAR tmp_path[MAX_PATH], tmp_file[MAX_PATH];
			GetTempPathW(MAX_PATH, tmp_path);
			StringCchPrintfW(tmp_file, MAX_PATH, L"%sStream.wmv", tmp_path);
			m->Error = "Set filename failed";
			if(FAILED(hr = m->pFileSinkFilter->SetFileName(tmp_file, NULL))) 
				ReturnError;
			m->Error = "Getting service provider failed";
			if(FAILED(hr = m->pWMASFWritter->QueryInterface(IID_IServiceProvider, (void**)&m->pProvider)))
				ReturnError;
			m->Error = "Query Service form IServiceProvider failed";
			if(FAILED(hr = m->pProvider->QueryService(IID_IWMWriterAdvanced2, IID_IWMWriterAdvanced2, (void**)&m->pWriter2)))
				ReturnError;
			m->Error = "Setting live source failed";
			if(FAILED(hr = m->pWriter2->SetLiveSource(TRUE)))
				ReturnError;
			m->Error = "Remove Sink failed";
			if(FAILED(hr = m->pWriter2->RemoveSink(0))) //For the time being we removing the default sink...
				ReturnError;
			SafeRelease(m->pProvider);
			m->Error = "WMCreateWriterNetworkSink failed";
			if(FAILED(hr = WMCreateWriterNetworkSink(&m->pNetSink)))
				ReturnError;
			m->Error = "Port opening failed";
			if(FAILED(hr = m->pNetSink->Open(&dwPort)))
				ReturnError;
			WCHAR url[256];
			DWORD len = 256;
			if(SUCCEEDED(hr = m->pNetSink->GetHostURL(url, &len)))
				if(strUrl)
					WideCharToMultiByte(CP_ACP, 0, url, -1, strUrl, 256, NULL, NULL);
			m->Error = "AddSink failed";
			if(FAILED(m->pWriter2->AddSink(m->pNetSink)))
				ReturnError;
		
			//Connect pins and run the graph
			if(hWnd){
				m->Error = "Create SmartTee filter failed";
				if(FAILED(hr = AddFilterByCLSID(m->pGraph, CLSID_SmartTee, L"SmartTee", &m->pSmartTee)))
					ReturnError;
				m->Error = "Connect Video output pin to SmartTee input pin failed";
				if(FAILED(hr = ConnectFilters(m->pGraph, m->pVidDeviceFilter, m->pSmartTee)))
					ReturnError;
				m->Error = "Connect SmartTee output pin to ASFWritter input pin failed";
				if(FAILED(hr = ConnectFilters(m->pGraph, m->pSmartTee, m->pWMASFWritter, L"Video Input 01")))
					ReturnError;
			}else{
				m->Error = "Connect Video output pin to ASFWritter input pin failed";
				if(FAILED(hr = ConnectFilters(m->pGraph, m->pVidDeviceFilter, m->pWMASFWritter, L"Video Input 01")))
					ReturnError;
			}
			m->Error = "Connect Audio output pin to ASFWritter input pin failed";
			if(FAILED(hr = ConnectFilters(m->pGraph, m->pAudDeviceFilter, m->pWMASFWritter, L"Audio Input 01")))
				ReturnError;
		}else{
			m->Error = "Create SmartTee filter failed";
			if(FAILED(hr = AddFilterByCLSID(m->pGraph, CLSID_SmartTee, L"SmartTee", &m->pSmartTee)))
				ReturnError;
			m->Error = "Connect Video output pin to SmartTee input pin failed";
			if(FAILED(hr = ConnectFilters(m->pGraph, m->pVidDeviceFilter, m->pSmartTee)))
				ReturnError;
		}
		
		if(hWnd){
			
//			m->Error = "Add Video render filter failed";
//			if(FAILED(hr = m->pGraph->AddFilter(m.pVideo, L"VMR7")))//Video Renderer
//				ReturnError;
//			m->Error = "Connect SmartTee output pin to Video render input pin failed";
//			if(FAILED(hr = ConnectFilters(m->pGraph, m->pSmartTee, m.pVideo)))
//				ReturnError;
//			
//			m->Error = "Create Video render filter failed";
//			if(FAILED(hr = CoCreateInstance(CLSID_VideoMixingRenderer, NULL, CLSCTX_INPROC, IID_IBaseFilter, (void**)&m->pVideo)))
//				ReturnError;
//			m->Error = "Add Video render filter failed";
//			if(FAILED(hr = m->pGraph->AddFilter(m->pVideo, L"Video Mixing Renderer")))
//				ReturnError;
//			IVMRFilterConfig* pConfig;
//			m->Error = "Get Video render config interface failed";
//			if(FAILED(hr = m->pVideo->QueryInterface(IID_IVMRFilterConfig, (void**)&pConfig)))
//				ReturnError;
//			pConfig->SetRenderingMode(VMRMode_Windowless);
//			pConfig->Release();
//			m->Error = "Get Video render control interface failed";
//			if(FAILED(hr = m->pVideo->QueryInterface(IID_IVMRWindowlessControl, (void**)&m->pVideoControl)))
//				ReturnError;
//			m->pVideoControl->SetVideoClippingWindow(hWnd);
//			m->Error = "Connect SmartTee output pin to Video render input pin failed";
//			if(FAILED(hr = ConnectFilters(m->pGraph, m->pSmartTee, m->pVideo)))
//				ReturnError;
//			if(Width && Height){
//				int nWidth, nHeight, nARWidth, nARHeight;
//				m->pVideoControl->GetNativeVideoSize((LONG*)&nWidth, (LONG*)&nHeight, (LONG*)&nARWidth, (LONG*)&nARHeight);
//				if(Left && Top)
//					if(*Width || *Height)
//						DSResizeWindow(m, *Left, *Top, *Width, *Height);
//					else
//						DSResizeWindow(m, *Left, *Top, nWidth, nHeight);
//				else
//					DSResizeWindow(m, 0, 0, nWidth, nHeight);
//				*Width = nWidth; *Height = nHeight;
//			}
			
			IPin *pIn, *pOut;
			
			m->Error = "Get SmartTee 2nd output pin failed";
			if(FAILED(hr = GetPin(m->pSmartTee, PINDIR_OUTPUT, &pOut, 2)))
				ReturnError;
			//m->Error = "Get Video output pin failed";
			//if(FAILED(hr = GetPin(m->pVidDeviceFilter, PINDIR_OUTPUT, &pOut)))
				//ReturnError;
			
			if(!strAudDevice){
				m->Error = "Create Sample Grabber filter failed";
				if(FAILED(hr = CoCreateInstance(CLSID_SampleGrabber, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void**)&m->pGrabFilter)))
					ReturnError;
				m->Error = "Add SampleGrabber filter failed";
				if(FAILED(hr = m->pGraph->AddFilter(m->pGrabFilter, L"Sample Grabber")))
					ReturnError;
				m->Error = "Get Sample Grabber interface failed";
				if(FAILED(hr = m->pGrabFilter->QueryInterface(IID_ISampleGrabber, (void**)&m->pGrab)))
					ReturnError;
				
				AM_MEDIA_TYPE mt;
				ZeroMemory(&mt, sizeof(AM_MEDIA_TYPE));
				mt.majortype = MEDIATYPE_Video;
				mt.subtype = MEDIASUBTYPE_RGB24;
				m->Error = "Set Sample Grabber media type failed";
				if(FAILED(hr = m->pGrab->SetMediaType(&mt)))
					ReturnError;
				m->Error = "Activate BufferSamples failed";
				if(FAILED(hr = m->pGrab->SetBufferSamples(OATRUE)))
					ReturnError;
				
				//IBaseFilter *pNull;
				//hr = CoCreateInstance (CLSID_NullRenderer, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void **)&m->pNull);
				//hr = m->pGraph->AddFilter(m->pNull, L"Null Renderer");			
				
				m->Error = "Get SampleGrabber input pin failed";
				if(FAILED(hr = GetPin(m->pGrabFilter, PINDIR_INPUT, &pIn)))
					ReturnError;
				m->Error = "Connect SmartTee output pin to SampleGrabber input pin failed";
				if(FAILED(hr = m->pGraph->Connect(pOut, pIn)))
					ReturnError;
				
				pOut->Release();
				pIn->Release();
				
				m->Error = "Get SampleGrabber output pin failed";
				if(FAILED(hr = GetPin(m->pGrabFilter, PINDIR_OUTPUT, &pOut)))
					ReturnError;
				
				//m->Error = "Connect SampleGrabber output pin to Video render input pin failed";
				//if(FAILED(hr = ConnectFilters(m->pGraph, m->pGrabFilter, m->pVideo)))
					//ReturnError;
			}
			
			m->Error = "Video render failed";
			if(FAILED(hr = m->pGraph->Render(pOut)))
				ReturnError;
			
			pOut->Release();

			if(!strUrl && strAudDevice){
				m->Error = "Get Audio output pin failed";
				if(FAILED(hr = GetPin(m->pAudDeviceFilter, PINDIR_OUTPUT, &pOut)))
					ReturnError;
				m->Error = "Audio render failed";
				if(FAILED(hr = m->pGraph->Render(pOut)))
					ReturnError;
				pOut->Release();
			}
			

			m->Error = "Query Video window interface failed";
			if(FAILED(hr = m->pGraph->QueryInterface(IID_IVideoWindow, (void **)&m->pWindow)))
				ReturnError;
			//m->Error = "Set Video window auto-show failed";
			//if(FAILED(hr = m->pWindow->put_AutoShow(OAFALSE)))
				//ReturnError;
			m->Error = "Set Video window owner failed";
			if(FAILED(hr = m->pWindow->put_Owner((OAHWND)hWnd)))
				ReturnError;
			m->Error = "Set Video window messages failed";
			if(FAILED(hr = m->pWindow->put_MessageDrain((OAHWND)hWnd)))
				ReturnError;
			//m->Error = "Set Video window visible failed";
			//if(FAILED(hr = m->pWindow->put_Visible(OAFALSE)))
				//ReturnError;
			//m->Error = "Set Video window on top failed";
			//if(FAILED(hr = m->pWindow->SetWindowForeground(OAFALSE)))
				//ReturnError;
			
			m->Error = "Get Video window size failed";
			HWND hVideo;
			RECT rc;
			if(hVideo = GetWindow(hWnd, GW_CHILD))
				GetClientRect(hVideo, &rc);
			
			m->Error = "Set Video window style failed";
			if(FAILED(hr = m->pWindow->put_WindowStyle(WS_CHILD | WS_CLIPCHILDREN)))
				ReturnError;
			
			m->Error = "Set Video window size failed";
			if(hVideo){
				long nLeft = 0, nTop = 0, nWidth = 0, nHeight = 0;
				if(Left && Top){
					nLeft = *Left; nTop = *Top;
				}
				if(Width && Height){
					nWidth = *Width; nHeight = *Height;
					*Width = rc.right; *Height = rc.bottom;
				}
				if(!nWidth && !nHeight){
					nWidth = rc.right; nHeight = rc.bottom;
				}else if(!nHeight){
					nHeight = nWidth * rc.bottom / rc.right;
				}else if(!nWidth){
					nWidth = nHeight * rc.right / rc.bottom;
				}
				m->pWindow->SetWindowPosition(nLeft, nTop, nWidth, nHeight);
			}
			
			m->Error = "Set Video window visible failed";
			if(FAILED(hr = m->pWindow->put_Visible(OATRUE)))
				ReturnError;
			
		}
		
		m->Error = "Query Media Control interface failed";
		if(FAILED(hr = m->pGraph->QueryInterface(IID_IMediaControl, (void **)&m->pMediaControl)))
			ReturnError;
		m->Error = "Query Event interface failed";
		if(FAILED(hr = m->pGraph->QueryInterface(IID_IMediaEvent, (void **)&m->pEvent)))
			ReturnError;
		m->Error = "Media Control Run failed";
		if(FAILED(hr = m->pMediaControl->Run()))
			ReturnError;
		
		//long evCode = 0; hr = pEvent->WaitForCompletion(INFINITE, &evCode); // Wait till its done.
		
		m->Error = "Success";
		return TRUE;
	}catch(...){
		ReturnError;
	}

}

int EXPORT DSGetSnapshot(DSParams *m, int *pBufferSize, int *pBuffer, BITMAPINFOHEADER *bmiHeader){
	try{
		HRESULT hr = S_FALSE;
		if(m->pGrab && pBufferSize){
			if(!pBuffer || !*pBufferSize){
				//hr = m->pGrab->GetCurrentBuffer((long*)pBufferSize, NULL);
				AM_MEDIA_TYPE mt;
				hr = m->pGrab->GetConnectedMediaType(&mt);
				if ((mt.formattype == FORMAT_VideoInfo) && 
					(mt.cbFormat >= sizeof(VIDEOINFOHEADER)) &&
					(mt.pbFormat != NULL)) 
				{
					*pBufferSize = mt.lSampleSize;
					VIDEOINFOHEADER *pVih = (VIDEOINFOHEADER*)mt.pbFormat;
					if(bmiHeader)
						*bmiHeader = pVih->bmiHeader;
					//hr = WriteBitmap(pszBitmapFile, &pVih->bmiHeader, 
						//mt.cbFormat - SIZE_PREHEADER, pBuffer, cbBuffer);
				}else
					*pBufferSize = 0;//hr = VFW_E_INVALIDMEDIATYPE;
			}else
				hr = m->pGrab->GetCurrentBuffer((long*)pBufferSize, (long*)pBuffer);
			
//			if(m->pMediaControl){
//				m->Error = "Media pause failed";
//				hr = m->pMediaControl->Pause();
//				//hr = m->pMediaControl->StopWhenReady();
//				//if(FAILED(hr)) goto ext;
//				
//				m->Error = "Wait for Media pause failed";
//				//OAFilterState pfs;
//				//do
//					//hr = m->pMediaControl->GetState(INFINITE, &pfs);
//				//while(SUCCEEDED(hr) && (pfs == State_Running));
//				long evCode;
//				hr = m->pEvent->WaitForCompletion(INFINITE, &evCode);
//				//if(FAILED(hr)) goto ext;
//				
//				m->Error = "Activate BufferSamples failed";
//				hr = m->pGrab->SetBufferSamples(OATRUE);
//				//if(FAILED(hr)) goto ext;
//				
//				m->Error = "Get Buffer failed";
//				hr = m->pGrab->GetCurrentBuffer((long*)pBufferSize, (long*)pBuffer);
//				//if(FAILED(hr)) goto ext;
//				
//				m->Error = "Deactivate BufferSamples failed";
//				hr = m->pGrab->SetBufferSamples(OAFALSE);
//				//if(FAILED(hr)) goto ext;
//				
//				m->Error = "Media run failed";
//				hr = m->pMediaControl->Run();
//				//if(FAILED(hr)) goto ext;
//				
//				m->Error = "Success";
//ext:
//				;
//			}
			
		}
		return SUCCEEDED(hr);
	}catch(...){
		return FALSE;
	}
}

int EXPORT DSShowSettings(IBaseFilter *pFilter, HWND hWnd, LPSTR strVidDevice){
	try{
		HRESULT hr = S_FALSE;
		if(pFilter){
			ISpecifyPropertyPages *pProp;
			hr = pFilter->QueryInterface(IID_ISpecifyPropertyPages, (void **)&pProp);
			if(SUCCEEDED(hr)){
				WCHAR strDev[256];
				MultiByteToWideChar(CP_ACP, 0, strVidDevice, -1, strDev, 256);
				FILTER_INFO FilterInfo;
				hr = pFilter->QueryFilterInfo(&FilterInfo);
				IUnknown *pFilterUnk;
				hr = pFilter->QueryInterface(IID_IUnknown, (void **)&pFilterUnk);
				CAUUID caGUID;
				pProp->GetPages(&caGUID);
				pProp->Release();
				OleCreatePropertyFrame(
					hWnd, 0, 0, strDev, //FilterInfo.achName,
					1, &pFilterUnk, caGUID.cElems, caGUID.pElems, 0, 0, NULL);
				pFilterUnk->Release();
				FilterInfo.pGraph->Release(); 
				CoTaskMemFree(caGUID.pElems);
			}
		}
		return SUCCEEDED(hr);
	}catch(...){
		return FALSE;
	}
}

int EXPORT DSGetVideoFormat(IBaseFilter *pFilter, int *Index, int *Width, int *Height){
	try{
		HRESULT hr = S_FALSE;
		IAMStreamConfig *pConfig;
		hr = pFilter->QueryInterface(IID_IAMStreamConfig, (void**)&pConfig);
		if(SUCCEEDED(hr) && (pConfig != NULL)){
			AM_MEDIA_TYPE *pmtConfig;
			VIDEO_STREAM_CONFIG_CAPS scc;
			int iCount = 0, iSize = 0;
			hr = pConfig->GetNumberOfCapabilities(&iCount, &iSize);
			if(SUCCEEDED(hr) && (Index != NULL))
				if(*Index < 0)
					*Index = iCount;
				else if (iSize == sizeof(VIDEO_STREAM_CONFIG_CAPS)){
					hr = pConfig->GetStreamCaps(*Index, &pmtConfig, (BYTE*)&scc);
					if (SUCCEEDED(hr) &&
						(pmtConfig->majortype == MEDIATYPE_Video) &&
						(pmtConfig->subtype == MEDIASUBTYPE_RGB24) &&
						(pmtConfig->formattype == FORMAT_VideoInfo) &&
						(pmtConfig->cbFormat >= sizeof (VIDEOINFOHEADER)) &&
						(pmtConfig->pbFormat != NULL))
					{
						VIDEOINFOHEADER *pVih = (VIDEOINFOHEADER*)pmtConfig->pbFormat;
						// pVih contains the detailed format information.
						*Width = pVih->bmiHeader.biWidth;
						*Height = pVih->bmiHeader.biHeight;
					}
				}
			pConfig->Release();
		}
		return SUCCEEDED(hr);
	}catch(...){
		return FALSE;
	}
}

int EXPORT DSSetVideoFormat(IBaseFilter *pFilter, int Index){
	try{
		HRESULT hr = S_FALSE;
		IAMStreamConfig *pConfig;
		hr = pFilter->QueryInterface(IID_IAMStreamConfig, (void**)&pConfig);
		if(SUCCEEDED(hr) && (pConfig != NULL)){
			AM_MEDIA_TYPE *pmtConfig;
			VIDEO_STREAM_CONFIG_CAPS scc;
			hr = pConfig->GetStreamCaps(Index, &pmtConfig, (BYTE*)&scc);
			hr = pConfig->SetFormat(pmtConfig);
			//pmtConfig->subtype = MEDIASUBTYPE_RGB24; 
			pConfig->Release();
		}
		return SUCCEEDED(hr);
	}catch(...){
		return FALSE;
	}
}

int EXPORT DSGetError(DSParams *m, char *Buffer, int Length)
{
	if(!Length || !Buffer)
		return strlen(m->Error);
	if(m->Error)
		strncpy(Buffer, m->Error, Length);
	return Length;
	}
*/
//-----------------------------------------------------------------------------
/*
#define WM_GRAPHNOTIFY (WM_APP + 1)

char strDevName[16384];
char strUrl[256];

DSParams m =
{
	NULL, NULL, NULL, NULL,
	//NULL,
	NULL, NULL, NULL, NULL,
	NULL, NULL, NULL, NULL,
	""
};

HRESULT HandleGraphEvent(void)
{
	LONG evCode, evParam1, evParam2;
	HRESULT hr = S_OK;
	if (m.pEvent)
		while(SUCCEEDED(m.pEvent->GetEvent(&evCode, &evParam1, &evParam2, 0)))
		{
			hr = m.pEvent->FreeEventParams(evCode, evParam1, evParam2);
			// Insert event processing code here
		}
	return hr;
}

LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam){
	int x = 0, y = 0, w, h;
	switch(uMsg){
        case WM_GRAPHNOTIFY: //HandleGraphEvent();
							break;
		case WM_CREATE:		strDevName[0] = 0;
							DSGetVideoDevices(strDevName, NULL, 0);
							msg(strDevName);
							strDevName[0] = 0;
							DSGetAudioDevices(strDevName, NULL, 0);
							msg(strDevName);
							DSStartStream(&m, "e2eSoft VCam", "Micrófono (Dispositivo de High ", 
								8080, 1, 1, 0, strUrl, hWnd, &x, &y, &w, &h);
							//DSStartStream(&m, "Logitech QuickCam Communicate STX", "Micrófono (Dispositivo de High ", 
								//8080, 1, 1, 0, strUrl, hWnd, &x, &y, &w, &h);
							msg(strUrl);
							//CaptureVideo("e2eSoft VCam", hWnd);
							msg(m.Error);
							break;
		case WM_DESTROY:	//CloseInterfaces();
							DSStopStream(&m);
							PostQuitMessage(0);
							break;
		default:			return DefWindowProc(hWnd, uMsg, wParam, lParam);
		}
	return 0;
	}

int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow){
	HWND hWnd;
	if(!hPrevInstance){ 
		WNDCLASS wc={CS_HREDRAW|CS_VREDRAW, WindowProc, 0, 0, hInstance,
			LoadIcon(0, IDI_APPLICATION), //LoadIcon(hInstance, MAKEINTRESOURCE(101))
			LoadCursor(0, IDC_ARROW), (HBRUSH)(COLOR_APPWORKSPACE+1), //0,
			0, "ClassName"};
		if(!RegisterClass(&wc))
			return 0;
		}
	hWnd = CreateWindowEx(0, "ClassName", "WinName", WS_OVERLAPPEDWINDOW,
		10, 10, 300, 300, 0, 0, hInstance, 0);
	if(!hWnd)
		return 0;
	ShowWindow(hWnd, nCmdShow);
	HDC hDC=GetDC(hWnd);
	MSG ms;
	while(GetMessage(&ms, 0, 0, 0)){
		TranslateMessage(&ms);
		DispatchMessage(&ms);
		}//message loop style
	ReleaseDC(hWnd, hDC);
	return ms.wParam;
	}
*/