GUID videoInputCamera::getMediaSubtype(int type) {

	int iCount = 0;
	int iSize = 0;
	HRESULT hr = pStreamConfig->GetNumberOfCapabilities(&iCount, &iSize);

	if (iSize == sizeof(VIDEO_STREAM_CONFIG_CAPS))
	{
		GUID lastFormat = MEDIASUBTYPE_None;
		for (int iFormat = 0; iFormat < iCount; iFormat+=2)
		{
			VIDEO_STREAM_CONFIG_CAPS scc;
			AM_MEDIA_TYPE *pmtConfig;
			hr =  pStreamConfig->GetStreamCaps(iFormat, &pmtConfig, (BYTE*)&scc);
			if (SUCCEEDED(hr)){

				if ( pmtConfig->subtype != lastFormat) {

					if (type == getMediaSubtype(pmtConfig->subtype)) {

						GUID mediaSubtype = pmtConfig->subtype;
						deleteMediaType(pmtConfig);
						return mediaSubtype;
					}
					lastFormat = pmtConfig->subtype;
				}
				deleteMediaType(pmtConfig);
			}
		}
	}


	return MEDIASUBTYPE_NULL;
}
bool VideoCaptureDirectShow2::setDeviceFilterMediaType(ICaptureGraphBuilder2* captureBuilder, IBaseFilter* deviceFilter, AVCapability cap) {

  if(!captureBuilder) {
    RX_ERROR("Cannot set device filter media type because the given ICaptureGraphBuilder* is invalid");
    return false;
  }

  if(!deviceFilter) {
    RX_ERROR("Cannot set the media type for the device filter because the device filter is invalid");
    return false;
  }
  
  if(cap.index < 0) {
    RX_ERROR("Cannot set the media type for the device filter because the given AVCapability has not index. Iterate over the stream caps to retrieve the caps index that we need");
    return false;
  }

  IAMStreamConfig* conf = NULL;
  HRESULT hr = captureBuilder->FindInterface(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video, deviceFilter, IID_IAMStreamConfig, (void**)&conf);
  if(FAILED(hr)) {
    RX_ERROR("Failed to retrieve a IAMStreamConfig to set the device filter media type");
    return false;
  }

  bool result = true;
  AM_MEDIA_TYPE* mt;
  VIDEO_STREAM_CONFIG_CAPS caps;
  hr = conf->GetStreamCaps(cap.index, &mt, (BYTE*)&caps);
  if(FAILED(hr)) {
    RX_ERROR("Failed to retrieve the AM_MEDIA_TYPE for the AVCapabiltiy with stream caps index: %d", cap.index);
    result = false;
    goto done;
  }
  
  if(mt->majortype != MEDIATYPE_Video) {
    RX_ERROR("The AM_MEDIA_TYPE we found is not an Video type so we cannot use it to set the media format of the device filter");
    result = false;
    goto done;
  }
  if(mt->formattype != FORMAT_VideoInfo) {
    RX_ERROR("The AM_MEDIA_TYPE we found is not a Format_VideoInfo, so cannot set media type of device filter");
    result = false;
    goto done;
  }
  if(mt->cbFormat < sizeof(VIDEOINFOHEADER)) {
    RX_ERROR("The AMD_MEDIA_TYPE has an invalid cbFormat size");
    result = false;
    goto done;
  }
  if(mt->pbFormat == NULL) {
    RX_ERROR("The AM_MEDIA_TYPE.pbFormat is NULL; cannot set type of device filter");
    result = false;
    goto done;
  }

  GUID guid_pixfmt = libavPixelFormatToMediaSubTypeGUID(cap.pixel_format);
  if(mt->subtype != guid_pixfmt) {
    RX_ERROR("The AM_MEDIA_TYPE.subtype is not the same as the one we want..");
    result = false;
    goto done;
  }

  hr = conf->SetFormat(mt);
  if(FAILED(hr)) {
    RX_ERROR("Failed to set the AM_MEDIA_TYPE for the device filter");
    result = false;
    goto done;
  }

 done:
  deleteMediaType(mt);
  safeReleaseDirectShow(&conf);
  return result;
}
// Capabilities
// --------------------------------------------------------------------------------------
std::vector<AVCapability> VideoCaptureDirectShow2::getCapabilities(int device) {
  std::vector<AVCapability> result;
  IAMStreamConfig* cap_stream_config = NULL;
  IGraphBuilder* cap_graph_builder = NULL;
  ICaptureGraphBuilder2* cap_capture_graph_builder = NULL;
  IBaseFilter* cap_device_filter = NULL;

  // Create a IGraphBuilder for the device
  if(!initCaptureGraphBuilderForDevice(device, &cap_device_filter, &cap_graph_builder, &cap_capture_graph_builder)) {
    RX_ERROR("Cannot setup the IGraphBuilder/ICaptureGraphBuilder/IBaseFilter (device)");
    goto done;
  }

  // Get the StreamConfig object for the capture device filter which contains info on this device
  if(!createStreamConfig(cap_capture_graph_builder, cap_device_filter, &cap_stream_config)) {
    RX_ERROR("Cannot create the IAMStreamConfig to query capabilities");
    goto done;
  }

  int count = 0;
  int size = 0;
  HRESULT hr = cap_stream_config->GetNumberOfCapabilities(&count, &size);
  if(SUCCEEDED(hr)) {
    for(int i = 0; i < count; ++i) {
 
      VIDEO_STREAM_CONFIG_CAPS caps;
      AM_MEDIA_TYPE* mt;
      hr = cap_stream_config->GetStreamCaps(i, &mt, (BYTE*)&caps);
      if(FAILED(hr)) {
        RX_ERROR("GetStreamCaps() failed");
        continue;
      }
 
      if(mt->majortype == MEDIATYPE_Video 
         && mt->formattype == FORMAT_VideoInfo
         && mt->cbFormat >= sizeof(VIDEOINFOHEADER) 
         && mt->pbFormat != NULL)
        {
          VIDEOINFOHEADER* info = (VIDEOINFOHEADER*)mt->pbFormat;
          AVPixelFormat av_pix_fmt = mediaSubTypeGUIDToLibavPixelFormat(mt->subtype);
          
          AVCapability cap;
          cap.size.width = info->bmiHeader.biWidth;
          cap.size.height = info->bmiHeader.biHeight;
          cap.pixel_format = av_pix_fmt;
          cap.framerate.num = info->AvgTimePerFrame;
          cap.framerate.den = 10000000; 
          cap.index = i;
          result.push_back(cap);
        }
      deleteMediaType(mt);
    }
  }

 done:
  safeReleaseDirectShow(&cap_graph_builder);
  safeReleaseDirectShow(&cap_capture_graph_builder);
  safeReleaseDirectShow(&cap_device_filter);
  safeReleaseDirectShow(&cap_stream_config);
  return result;
}
std::vector<CameraConfig> videoInputCamera::getCameraConfigs(int dev_id) {

	std::vector<CameraConfig> cfg_list;

	int count = getDeviceCount();
	if (count==0) return cfg_list;

	comInit();

	HRESULT hr;
	ICaptureGraphBuilder2 *lpCaptureGraphBuilder;
	IGraphBuilder *lpGraphBuilder;
	IBaseFilter *lpInputFilter;
	IAMStreamConfig *lpStreamConfig;

	char 	nDeviceName[255];
	WCHAR 	wDeviceName[255];

	for (int cam_id=0;cam_id<count;cam_id++) {
		if ((dev_id>=0) && (dev_id!=cam_id)) continue;
		hr = CoCreateInstance(CLSID_CaptureGraphBuilder2, NULL, CLSCTX_INPROC_SERVER, IID_ICaptureGraphBuilder2, (void **)&lpCaptureGraphBuilder);
		if (FAILED(hr))	// FAILED is a macro that tests the return value
		{
			printf("ERROR - Could not create the Filter Graph Manager\n");
			comUnInit();
			return cfg_list;
		}

		// Create the Filter Graph Manager.
		hr = CoCreateInstance(CLSID_FilterGraph, 0, CLSCTX_INPROC_SERVER,IID_IGraphBuilder, (void**)&lpGraphBuilder);
		if (FAILED(hr))
		{
			printf("ERROR - Could not add the graph builder!\n");
			lpCaptureGraphBuilder->Release();
			comUnInit();
			return cfg_list;
		}

		hr = lpCaptureGraphBuilder->SetFiltergraph(lpGraphBuilder);
		if (FAILED(hr))
		{
			printf("ERROR - Could not set filtergraph\n");
			lpGraphBuilder->Release();
			lpCaptureGraphBuilder->Release();
			comUnInit();
			return cfg_list;
		}

		memset(wDeviceName, 0, sizeof(WCHAR) * 255);
		memset(nDeviceName, 0, sizeof(char) * 255);
		hr = getDevice(&lpInputFilter, cam_id, wDeviceName, nDeviceName);

		if (SUCCEEDED(hr)){
			hr = lpGraphBuilder->AddFilter(lpInputFilter, wDeviceName);
		}else{
			printf("ERROR - Could not find specified video device\n");
			lpGraphBuilder->Release();
			lpCaptureGraphBuilder->Release();
			comUnInit();
			return cfg_list;
		}

		hr = lpCaptureGraphBuilder->FindInterface(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video, lpInputFilter, IID_IAMStreamConfig, (void **)&lpStreamConfig);
		if(FAILED(hr)){
			printf("ERROR: Couldn't config the stream!\n");
			lpInputFilter->Release();
			lpGraphBuilder->Release();
			lpCaptureGraphBuilder->Release();
			comUnInit();
			return cfg_list;
		}

		CameraConfig cam_cfg;
		CameraTool::initCameraConfig(&cam_cfg);

		cam_cfg.driver = DRIVER_DEFAULT;
		cam_cfg.device = cam_id;
		sprintf(cam_cfg.name, "%s", nDeviceName);

		int iCount = 0;
		int iSize = 0;
		hr = lpStreamConfig->GetNumberOfCapabilities(&iCount, &iSize);
		std::vector<CameraConfig> fmt_list;

		if (iSize == sizeof(VIDEO_STREAM_CONFIG_CAPS))
		{
			GUID lastFormat = MEDIASUBTYPE_None;
			for (int iFormat = 0; iFormat < iCount; iFormat+=2)
			{
				VIDEO_STREAM_CONFIG_CAPS scc;
				AM_MEDIA_TYPE *pmtConfig;
				hr =  lpStreamConfig->GetStreamCaps(iFormat, &pmtConfig, (BYTE*)&scc);
				if (SUCCEEDED(hr)){

					if ( pmtConfig->subtype != lastFormat) {

						if (fmt_list.size()>0) {
							std::sort(fmt_list.begin(), fmt_list.end());
							cfg_list.insert( cfg_list.end(), fmt_list.begin(), fmt_list.end() );
							fmt_list.clear();
						}
						cam_cfg.cam_format = getMediaSubtype(pmtConfig->subtype);
						lastFormat = pmtConfig->subtype;
					}

					int stepX = scc.OutputGranularityX;
					int stepY = scc.OutputGranularityY;
					if(stepX < 1 || stepY < 1) continue;

					else if ((stepX==1) && (stepY==1)) {

						cam_cfg.cam_width = scc.InputSize.cx;
						cam_cfg.cam_height = scc.InputSize.cy;

						int maxFrameInterval = scc.MaxFrameInterval;
						if (maxFrameInterval==0) maxFrameInterval = 10000000;
						float last_fps=-1;
						VIDEOINFOHEADER *pVih = (VIDEOINFOHEADER*)pmtConfig->pbFormat;
						for (int iv=scc.MinFrameInterval;iv<=maxFrameInterval;iv=iv*2) {
							pVih->AvgTimePerFrame = iv;
							hr = lpStreamConfig->SetFormat(pmtConfig);
							if (hr==S_OK) { hr = lpStreamConfig->GetFormat(&pmtConfig);
							float fps = ((int)floor(100000000.0f/(float)pVih->AvgTimePerFrame + 0.5f))/10.0f;
							if (fps!=last_fps) {
								cam_cfg.cam_fps = fps;
								fmt_list.push_back(cam_cfg);
								last_fps=fps;
							} }
						}

					} else {
						int x,y;
						for (x=scc.MinOutputSize.cx,y=scc.MinOutputSize.cy;x<=scc.MaxOutputSize.cx,y<=scc.MaxOutputSize.cy;x+=stepX,y+=stepY) {

							cam_cfg.cam_width = x;
							cam_cfg.cam_height = y;

							int maxFrameInterval = scc.MaxFrameInterval;
							if (maxFrameInterval==0) maxFrameInterval = 10000000;
							float last_fps=-1;
							VIDEOINFOHEADER *pVih = (VIDEOINFOHEADER*)pmtConfig->pbFormat;
							for (int iv=scc.MinFrameInterval;iv<=maxFrameInterval;iv=iv*2) {
								pVih->AvgTimePerFrame = iv;
								hr = lpStreamConfig->SetFormat(pmtConfig);
								if (hr==S_OK) { hr = lpStreamConfig->GetFormat(&pmtConfig);
								float fps = ((int)floor(100000000.0f/(float)pVih->AvgTimePerFrame + 0.5f))/10.0f;
								if (fps!=last_fps) {
									cam_cfg.cam_fps = fps;
									fmt_list.push_back(cam_cfg);
									last_fps=fps;
								} }
							}

						}
					}

					deleteMediaType(pmtConfig);
				}
			}
		}

		if (fmt_list.size()>0) {
			std::sort(fmt_list.begin(), fmt_list.end());
			cfg_list.insert( cfg_list.end(), fmt_list.begin(), fmt_list.end() );
			fmt_list.clear();
		}

		lpStreamConfig->Release();
		lpInputFilter->Release();
		lpGraphBuilder->Release();
		lpCaptureGraphBuilder->Release();
	}

	comUnInit();
	return cfg_list;
}
HRESULT videoInputCamera::stopDevice() {

	HRESULT HR = NULL;

	//Stop the callback and free it
	if( (sgCallback) && (pSampleGrabber) )
	{
		//printf("SETUP: freeing Grabber Callback\n");
		pSampleGrabber->SetCallback(NULL, 1);
		sgCallback->Release();
		delete sgCallback;
		sgCallback = NULL;
	}

	//Check to see if the graph is running, if so stop it.
	if( (pMediaControl) )
	{
		HR = pMediaControl->Pause();
		if (FAILED(HR)) printf("ERROR - Could not pause pControl\n");

		HR = pMediaControl->Stop();
		if (FAILED(HR)) printf("ERROR - Could not stop pControl\n");
	}

	//Disconnect filters from capture device
	if( (pInputFilter) ) NukeDownstream(pInputFilter);

	//Release and zero pointers to our filters etc
	if (pDestFilter) { 		//printf("SETUP: freeing Renderer \n");
		pDestFilter->Release();
		pDestFilter = NULL;
	}
	if (pInputFilter) { 		//printf("SETUP: freeing Capture Source \n");
		pInputFilter->Release();
		pInputFilter = NULL;
	}
	if (pGrabberFilter) {		//printf("SETUP: freeing Grabber Filter  \n");
		pGrabberFilter->Release();
		pGrabberFilter = NULL;
	}
	if (pSampleGrabber) {       //printf("SETUP: freeing Grabber  \n");
		pSampleGrabber->Release();
		pSampleGrabber = NULL;
	}
	if (pMediaControl) { 		//printf("SETUP: freeing Control   \n");
		pMediaControl->Release();
		pMediaControl = NULL;
	}
	if (pStreamConfig) { 		//printf("SETUP: freeing Stream  \n");
		pStreamConfig->Release();
		pStreamConfig = NULL;
	}

	if (pAmMediaType) { 		//printf("SETUP: freeing Media Type  \n");
		deleteMediaType(pAmMediaType);
	}

	//Destroy the graph
	if (pGraphBuilder) destroyGraph();

	//Release and zero our capture graph and our main graph
	if (pCaptureGraphBuilder) { 		//printf("SETUP: freeing Capture Graph \n");
		pCaptureGraphBuilder->Release();
		pCaptureGraphBuilder = NULL;
	}
	if (pGraphBuilder) { 			//printf("SETUP: freeing Main Graph \n");
		pGraphBuilder->Release();
		pGraphBuilder = NULL;
	}

	comUnInit();
	return S_OK;
}