DShowCameraDeviceInteral (CameraDevice* const owner_,
                              const ComSmartPtr <ICaptureGraphBuilder2>& captureGraphBuilder_,
                              const ComSmartPtr <IBaseFilter>& filter_,
                              int minWidth, int minHeight,
                              int maxWidth, int maxHeight)
      : owner (owner_),
        captureGraphBuilder (captureGraphBuilder_),
        filter (filter_),
        ok (false),
        imageNeedsFlipping (false),
        width (0),
        height (0),
        activeUsers (0),
        recordNextFrameTime (false),
        previewMaxFPS (60)
    {
        HRESULT hr = graphBuilder.CoCreateInstance (CLSID_FilterGraph);
        if (FAILED (hr))
            return;

        hr = captureGraphBuilder->SetFiltergraph (graphBuilder);
        if (FAILED (hr))
            return;

        hr = graphBuilder.QueryInterface (mediaControl);
        if (FAILED (hr))
            return;

        {
            ComSmartPtr <IAMStreamConfig> streamConfig;

            hr = captureGraphBuilder->FindInterface (&PIN_CATEGORY_CAPTURE, 0, filter,
                                                     IID_IAMStreamConfig, (void**) streamConfig.resetAndGetPointerAddress());

            if (streamConfig != nullptr)
            {
                getVideoSizes (streamConfig);

                if (! selectVideoSize (streamConfig, minWidth, minHeight, maxWidth, maxHeight))
                    return;
            }
        }

        hr = graphBuilder->AddFilter (filter, _T("Video Capture"));
        if (FAILED (hr))
            return;

        hr = smartTee.CoCreateInstance (CLSID_SmartTee);
        if (FAILED (hr))
            return;

        hr = graphBuilder->AddFilter (smartTee, _T("Smart Tee"));
        if (FAILED (hr))
            return;

        if (! connectFilters (filter, smartTee))
            return;

        ComSmartPtr <IBaseFilter> sampleGrabberBase;
        hr = sampleGrabberBase.CoCreateInstance (CLSID_SampleGrabber);
        if (FAILED (hr))
            return;

        hr = sampleGrabberBase.QueryInterface (IID_ISampleGrabber, sampleGrabber);
        if (FAILED (hr))
            return;

        {
            AM_MEDIA_TYPE mt = { 0 };
            mt.majortype = MEDIATYPE_Video;
            mt.subtype = MEDIASUBTYPE_RGB24;
            mt.formattype = FORMAT_VideoInfo;
            sampleGrabber->SetMediaType (&mt);
        }

        callback = new GrabberCallback (*this);
        hr = sampleGrabber->SetCallback (callback, 1);

        hr = graphBuilder->AddFilter (sampleGrabberBase, _T("Sample Grabber"));
        if (FAILED (hr))
            return;

        ComSmartPtr <IPin> grabberInputPin;
        if (! (getPin (smartTee, PINDIR_OUTPUT, smartTeeCaptureOutputPin, "capture")
                && getPin (smartTee, PINDIR_OUTPUT, smartTeePreviewOutputPin, "preview")
                && getPin (sampleGrabberBase, PINDIR_INPUT, grabberInputPin)))
            return;

        hr = graphBuilder->Connect (smartTeePreviewOutputPin, grabberInputPin);
        if (FAILED (hr))
            return;

        AM_MEDIA_TYPE mt = { 0 };
        hr = sampleGrabber->GetConnectedMediaType (&mt);
        VIDEOINFOHEADER* pVih = (VIDEOINFOHEADER*) (mt.pbFormat);
        width = pVih->bmiHeader.biWidth;
        height = pVih->bmiHeader.biHeight;

        ComSmartPtr <IBaseFilter> nullFilter;
        hr = nullFilter.CoCreateInstance (CLSID_NullRenderer);
        hr = graphBuilder->AddFilter (nullFilter, _T("Null Renderer"));

        if (connectFilters (sampleGrabberBase, nullFilter)
              && addGraphToRot())
        {
            activeImage = Image (Image::RGB, width, height, true);
            loadingImage = Image (Image::RGB, width, height, true);

            ok = true;
        }
    }
DSVideoDevice::DSVideoDevice(int id, const std::wstring & devName, IFilterGraph2* graph, ICaptureGraphBuilder2* capture, IMoniker* moniker)
: d_(new Impl(id, devName, graph))
{
	//add a filter for the device
	HRESULT hr = graph->AddSourceFilterForMoniker(moniker, 0, d_->filtername.c_str(), &d_->sourcefilter);
	if (hr != S_OK) throw hr;

	//create a samplegrabber filter for the device
	hr = CoCreateInstance(CLSID_SampleGrabber, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void**)&d_->samplegrabberfilter);
	if (hr < 0) throw hr;

	//set mediatype on the samplegrabber
	hr = d_->samplegrabberfilter->QueryInterface(IID_ISampleGrabber, (void**)&d_->samplegrabber);
	if (hr != S_OK) throw hr;

	const std::wstring sgFilterName = L"SampleGrabberFilter_" + d_->filtername;
	graph->AddFilter(d_->samplegrabberfilter, sgFilterName.c_str());

	//set the media type
	AM_MEDIA_TYPE mt;
	memset(&mt, 0, sizeof(AM_MEDIA_TYPE));

	mt.majortype = MEDIATYPE_Video;
	mt.subtype   = MEDIASUBTYPE_RGB24; 
	// setting the above to 32 bits fails consecutive Select for some reason
	// and only sends one single callback (flush from previous one ???)
	// must be deeper problem. 24 bpp seems to work fine for now.

	hr = d_->samplegrabber->SetMediaType(&mt);
	if (hr != S_OK) throw hr;

	//add the callback to the samplegrabber
	hr = d_->samplegrabber->SetCallback(d_->callbackhandler, 0);
	if (hr != S_OK) throw hr;

	//set the null renderer
	hr = CoCreateInstance(CLSID_NullRenderer, NULL,CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void**) &d_->nullrenderer);
	if (hr < 0) throw hr; 

	const std::wstring nrFilterName = L"NullRenderer_" + d_->filtername;
	graph->AddFilter(d_->nullrenderer, nrFilterName.c_str());
	
	//set the render path
#ifdef DEBUG_RENDERER
	hr = capture->RenderStream(&PIN_CATEGORY_PREVIEW, &MEDIATYPE_Video, d_->sourcefilter, d_->samplegrabberfilter, NULL);
#else
	hr = capture->RenderStream(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video, d_->sourcefilter, d_->samplegrabberfilter, d_->nullrenderer);
#endif
	if (hr < 0) throw hr; 

	//#undef max // to get std::limits to work
	//long long start = 0;
	long long stop = MAXLONGLONG; //std::numeric_limits<long long>::max();
	//if the stream is started, start capturing immediately
	//hr = capture->ControlStream(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video, d_->sourcefilter, &start, &stop, 1, 2);
	hr = capture->ControlStream(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video, d_->sourcefilter, NULL, &stop, 1, 2);
	if (hr < 0) throw hr;


#ifdef REGISTER_FILTERGRAPH
	// Add our graph to the running object table, which will allow
	// the GraphEdit application to "spy" on our graph
	hr = addGraphToRot(d_->graph, &d_->dwGraphRegister);
	if (FAILED(hr))
	{
		//Msg(TEXT("Failed to register filter graph with ROT!  hr=0x%x"), hr);
		//g_dwGraphRegister = 0;
	}
#endif

}