/***************************************************************************** * * (C) COPYRIGHT MICROSOFT CORPORATION, 1999-2000 * * TITLE: PrvGrph.cpp * * VERSION: 1.0 * * AUTHOR: OrenR * * DATE: 2000/10/25 * * DESCRIPTION: Implements preview graph for capture still images * *****************************************************************************/ #include #pragma hdrstop /////////////////////////////// // // Constants // /////////////////////////////// // // Amount of time we are willing to wait to take a picture // const UINT TIMEOUT_TAKE_PICTURE = 1000 * 5; // 5 seconds // // These 2 values define how many media sample packets the still // filter should cache // const UINT CAPTURE_NUM_SAMPLES_TO_CACHE = 6; const UINT STILL_NUM_SAMPLES_TO_CACHE = 1; // // Max amount of time we wait for us to transition into a running state // const UINT STATE_TRANSITION_TIMEOUT = 1000 * 2; // 2 seconds // // Video size preferrences. This will not affect DV devices, only // USB WebCams that support changing their format. // const GUID DEFAULT_MEDIASUBTYPE = MEDIASUBTYPE_IYUV; const LONG MIN_VIDEO_WIDTH = 176; const LONG MIN_VIDEO_HEIGHT = 144; const LONG MAX_VIDEO_WIDTH = 640; const LONG MAX_VIDEO_HEIGHT = 480; const LONG PREFERRED_VIDEO_WIDTH = 176; const LONG PREFERRED_VIDEO_HEIGHT = 144; const LONG PREFERRED_FRAME_RATE = 30; // ideal frame rate const LONG BACKUP_FRAME_RATE = 15; // less than ideal frame rate. /////////////////////////////// // CPreviewGraph Constructor // CPreviewGraph::CPreviewGraph() : m_hwndParent(NULL), m_lStillPinCaps(0), m_lStyle(0), m_bPreviewVisible(TRUE), m_CurrentState(WIAVIDEO_NO_VIDEO), m_pWiaVideo(NULL), m_bSizeVideoToWindow(FALSE), m_pVideoProperties(NULL) { DBG_FN("CPreviewGraph::CPreviewGraph"); } /////////////////////////////// // CPreviewGraph Destructor // CPreviewGraph::~CPreviewGraph() { DBG_FN("CPreviewGraph::~CPreviewGraph"); // // Term the object if it is not already terminated. // Term(); } /////////////////////////////// // Init // HRESULT CPreviewGraph::Init(CWiaVideo *pWiaVideo) { HRESULT hr = S_OK; if (pWiaVideo == NULL) { hr = E_POINTER; CHECK_S_OK2(hr, ("CPreviewGraph::Init, received NULL CWiaVideo " "param, this should never happen")); } if (pWiaVideo) { m_pWiaVideo = pWiaVideo; } m_StillProcessor.Init(this); CreateHiddenWindow(); return hr; } /////////////////////////////// // Term // HRESULT CPreviewGraph::Term() { HRESULT hr = S_OK; m_StillProcessor.Term(); if (GetState() != WIAVIDEO_NO_VIDEO) { DestroyVideo(); } DestroyHiddenWindow(); m_pWiaVideo = NULL; return hr; } /////////////////////////////// // GetImagesDirectory // HRESULT CPreviewGraph::GetImagesDirectory(CSimpleString *pImagesDirectory) { DBG_FN("CPreviewGraph::GetImagesDirectory"); ASSERT(pImagesDirectory != NULL); HRESULT hr = S_OK; if (pImagesDirectory == NULL) { hr = E_POINTER; CHECK_S_OK2(hr, ("CPreviewGraph::GetImagesDirectory received a " "NULL parameter")); } if (hr == S_OK) { *pImagesDirectory = m_strImagesDirectory; } return hr; } /////////////////////////////// // SetImagesDirectory // HRESULT CPreviewGraph::SetImagesDirectory( const CSimpleString *pImagesDirectory) { DBG_FN("CPreviewGraph::SetImagesDirectory"); ASSERT(pImagesDirectory != NULL); HRESULT hr = S_OK; if (pImagesDirectory == NULL) { hr = E_POINTER; CHECK_S_OK2(hr, ("CPreviewGraph::SetImagesDirectory received a " "NULL parameter")); } if (hr == S_OK) { m_strImagesDirectory = *pImagesDirectory; // // If the graph is created, then we should set the image directory // so that the next image captured will be saved to the new directory. // if (GetState() == WIAVIDEO_NO_VIDEO) { hr = m_StillProcessor.CreateImageDir(&m_strImagesDirectory); CHECK_S_OK2(hr, ("CPreviewGraph::SetImagesDirectory, failed to " "create images directory '%ls'", CSimpleStringConvert::WideString(m_strImagesDirectory))); } } return hr; } /////////////////////////////// // GetState // WIAVIDEO_STATE CPreviewGraph::GetState() { return m_CurrentState; } /////////////////////////////// // SetState // HRESULT CPreviewGraph::SetState(WIAVIDEO_STATE NewState) { m_CurrentState = NewState; return S_OK; } /////////////////////////////// // CreateVideo // HRESULT CPreviewGraph::CreateVideo(const TCHAR *pszOptionalWiaDeviceID, IMoniker *pCaptureDeviceMoniker, HWND hwndParent, BOOL bStretchToFitParent, BOOL bAutoPlay) { DBG_FN("CPreviewGraph::CreateVideo"); HRESULT hr = S_OK; WIAVIDEO_STATE State = GetState(); if ((m_strImagesDirectory.Length() == 0) || (pCaptureDeviceMoniker == NULL)) { hr = E_POINTER; CHECK_S_OK2(hr, ("CPreviewGraph::CreateVideo, received NULL param")); return hr; } else if ((State == WIAVIDEO_DESTROYING_VIDEO) || (State == WIAVIDEO_CREATING_VIDEO)) { hr = E_FAIL; CHECK_S_OK2(hr, ("CPreviewGraph::CreateVideo, cannot create video, " "still in the process of creating or destroying " "it, CurrentState = '%lu'", State)); return hr; } else if (State != WIAVIDEO_NO_VIDEO) { // // If we are not in the process of creating or destorying the // video, and our state is not NO_VIDEO, then assume everything // is okay, and return S_OK. // return S_OK; } ASSERT(m_strImagesDirectory.Length() != 0); ASSERT(pCaptureDeviceMoniker != NULL); // // Set our state to indicate we are creating the video // SetState(WIAVIDEO_CREATING_VIDEO); // // Create our image directory // if (SUCCEEDED(hr)) { // // Save the parent window handle. // m_hwndParent = hwndParent; m_bSizeVideoToWindow = bStretchToFitParent; } if (SUCCEEDED(hr)) { m_pVideoProperties = new CWiaVideoProperties(pszOptionalWiaDeviceID); if (m_pVideoProperties == NULL) { hr = E_OUTOFMEMORY; CHECK_S_OK2(hr, ("CPreviewGraph::CreateVideo, failed to allocate memory " "for CWiaVideoProperties. Cannot create video")); } } // // Build the DirectShow video preview graph // if (SUCCEEDED(hr)) { hr = BuildPreviewGraph(pCaptureDeviceMoniker, bStretchToFitParent); CHECK_S_OK2(hr, ("CPreviewGraph::CreateVideo failed to build the " "preview graph")); } if (SUCCEEDED(hr)) { // // Video graph exists, update our state // SetState(WIAVIDEO_VIDEO_CREATED); // // Begin playback automatically // if (bAutoPlay) { hr = Play(); CHECK_S_OK2(hr, ("CPreviewGraph::CreateVideo failed begin " "playback")); } } if (FAILED(hr)) { CHECK_S_OK2(hr, ("CreateVideo failed to build the graph, tearing it " "down")); DestroyVideo(); } return hr; } /////////////////////////////// // DestroyVideo // HRESULT CPreviewGraph::DestroyVideo() { DBG_FN("CPreviewGraph::DestroyVideo"); HRESULT hr = S_OK; SetState(WIAVIDEO_DESTROYING_VIDEO); // // Stop the graph first // Stop(); // // Delete the video properties object. // if (m_pVideoProperties) { delete m_pVideoProperties; m_pVideoProperties = NULL; } // // Destroys the preview graph and all DShow components associated with it. // Even if these are already gone, there is no harm in calling it. // TeardownPreviewGraph(); m_hwndParent = NULL; m_pVideoControl = NULL; m_pStillPin = NULL; m_pCapturePinSnapshot = NULL; m_pStillPinSnapshot = NULL; m_pPreviewVW = NULL; m_pCaptureGraphBuilder = NULL; m_pGraphBuilder = NULL; m_pCaptureFilter = NULL; m_lStillPinCaps = 0; m_lStyle = 0; SetState(WIAVIDEO_NO_VIDEO); // // We purposely put this here so that it does NOT get reset after // destroying the video. This should remain the lifetime of this // object instance, unless changed by the user via the // get/put_PreviewVisible properties in CWiaVideo. // m_bPreviewVisible = m_bPreviewVisible; return hr; } /////////////////////////////// // TakePicture // HRESULT CPreviewGraph::TakePicture(CSimpleString *pstrNewImageFileName) { DBG_FN("CPreviewGraph::TakePicture"); WIAVIDEO_STATE State = GetState(); HRESULT hr = S_OK; CSimpleString strNewImageFullPath; if ((State != WIAVIDEO_VIDEO_PLAYING) && (State != WIAVIDEO_VIDEO_PAUSED)) { hr = E_FAIL; CHECK_S_OK2(hr, ("CPreviewGraph::TakePicture, cannot take a picture " "because we are in an incorrect state, " "current state = '%lu'", State)); return hr; } ASSERT((State == WIAVIDEO_VIDEO_PLAYING) || (State == WIAVIDEO_VIDEO_PAUSED)); // // Set this to TRUE. We reset it when we return from our // WaitForNewImage fn. This allows us to distinguish between // a user initiated take picture event, and an async take picture // event generated by a hardware button push. // m_StillProcessor.SetTakePicturePending(TRUE); // // If the device is internal triggerable, trigger it // The triggered image will be delivered to the still pin and will // then travel down stream until it reaches the WIA StreamSnapshot filter, // which will process the image. // if (m_pVideoControl && (m_lStillPinCaps & VideoControlFlag_Trigger)) { // // ignore the time stamp here since we do not need it. // hr = m_pVideoControl->SetMode(m_pStillPin, VideoControlFlag_Trigger); CHECK_S_OK2(hr, ("CPreviewGraph::TakePicture, attempted to trigger " "the still pin on the capture filter to take a " "picture, but it failed")); } else { if (m_pCapturePinSnapshot) { hr = m_pCapturePinSnapshot->Snapshot(GetMessageTime()); CHECK_S_OK2(hr, ("CPreviewGraph::TakePicture, attempted to " "trigger the WIA Image filter to take a " "snapshot of the video stream, but it " "failed")); } else { hr = E_FAIL; CHECK_S_OK2(hr, ("CPreviewGraph::TakePicture, attempted to call " "snapshot on the WIA Image filter, but the " "filter pointer is NULL")); } } // // If we are taking the picture via the still pin, then taking a // picture is an asynchronous operation, and we have to wait for // the StillProcessor's callback function to complete its work. // It will signal us when it's done. If we are taking the picture // via the WIA image filter, then the operation is synchronous, // in which case this wait function will return immediately. // hr = m_StillProcessor.WaitForNewImage(TIMEOUT_TAKE_PICTURE, &strNewImageFullPath); CHECK_S_OK2(hr, ("CPreviewGraph::TakePicture failed waiting for new " "still image to arrive, our timeout was '%d'", TIMEOUT_TAKE_PICTURE)); // // Set this to TRUE. We reset it when we return from our // WaitForNewImage fn. // m_StillProcessor.SetTakePicturePending(FALSE); if ((pstrNewImageFileName) && (strNewImageFullPath.Length() > 0)) { *pstrNewImageFileName = strNewImageFullPath; } CHECK_S_OK(hr); return hr; } /////////////////////////////// // ResizeVideo // HRESULT CPreviewGraph::ResizeVideo(BOOL bSizeVideoToWindow) { DBG_FN("CPreviewGraph::ResizeVideo"); RECT rc = {0}; HRESULT hr = S_OK; // // Check for invalid args // if ((m_hwndParent) && (m_pPreviewVW)) { hr = CDShowUtil::SizeVideoToWindow(m_hwndParent, m_pPreviewVW, bSizeVideoToWindow); m_bSizeVideoToWindow = bSizeVideoToWindow; CHECK_S_OK2(hr, ("CPreviewGraph::ResizeVideo, failed to resize " "video window")); } else { hr = E_FAIL; CHECK_S_OK2(hr, ("CPreviewGraph::ResizeVideo, either the parent " "window is NULL (0x%08lx), or the Video Preview " "pointer is NULL (0x%08lx)", m_hwndParent, m_pPreviewVW)); } return hr; } /////////////////////////////// // ShowVideo // HRESULT CPreviewGraph::ShowVideo(BOOL bShow) { DBG_FN("CPreviewGraph::ShowVideo"); HRESULT hr = S_OK; if (m_pPreviewVW) { hr = CDShowUtil::ShowVideo(bShow, m_pPreviewVW); CHECK_S_OK2(hr, ("CPreviewGraph::ShowVideo failed")); } m_bPreviewVisible = bShow; return hr; } /////////////////////////////// // Play // HRESULT CPreviewGraph::Play() { DBG_FN("CPreviewGraph::Play"); HRESULT hr = S_OK; WIAVIDEO_STATE State = GetState(); if ((State != WIAVIDEO_VIDEO_CREATED) && (State != WIAVIDEO_VIDEO_PAUSED)) { hr = E_FAIL; CHECK_S_OK2(hr, ("CPreviewGraph::Play, cannot begin playback " "because we are in an incorrect state, " "current state = '%lu'", State)); return hr; } else if (State == WIAVIDEO_VIDEO_PLAYING) { DBG_WRN(("CPreviewGraph::Play, play was called, but we are already " "playing, doing nothing.")); return hr; } ASSERT(m_pGraphBuilder != NULL); if (m_pGraphBuilder == NULL) { hr = E_FAIL; CHECK_S_OK2(hr, ("CPreviewGraph::Play, m_pGraphBuilder is NULL")); } if (hr == S_OK) { CComQIPtr pMC(m_pGraphBuilder); if (pMC) { DBG_TRC(("CPreviewGraph::Play ***Beginning Playback ***")); // // Set the graph running... // hr = pMC->Run(); if (hr == S_OK) { DBG_TRC(("CPreviewGraph::Play, graph is running...")); SetState(WIAVIDEO_VIDEO_PLAYING); } else if (hr == S_FALSE) { OAFilterState FilterState; DBG_TRC(("CPreviewGraph::Play, Waiting '%lu' millisec for " "graph to transition to running state", STATE_TRANSITION_TIMEOUT)); // // Give the graph a chance to transition into the running // state. Note that this function will wait for // STATE_TRANSITION_TIMEOUT milliseconds, so make sure that // this is not a long wait, otherwise the caller could // appear unresponsive. // hr = pMC->GetState(STATE_TRANSITION_TIMEOUT, &FilterState); if ((hr == S_OK) && (FilterState == State_Running)) { SetState(WIAVIDEO_VIDEO_PLAYING); DBG_TRC(("CPreviewGraph::Play, graph is running...")); } else if (hr == VFW_S_STATE_INTERMEDIATE) { // // We fudge our state a little here on the assumption // that the transition to the run state by DShow is // taking a little longer, but eventually will transition // to running. // SetState(WIAVIDEO_VIDEO_PLAYING); DBG_TRC(("CPreviewGraph::Play, still transitioning to " "play state...")); } else { CHECK_S_OK2(hr, ("CPreviewGraph::Play, " "IMediaControl::GetState failed...")); } hr = S_OK; } else { CHECK_S_OK2(hr, ("CPreviewGraph::Play, " "IMediaControl::Run failed")); } } else { DBG_ERR(("CPreviewGraph::Play, Unable to get " "MediaControl interface")); } } // // If the user of this object specified in the past that the video window // should be visible, then show it, otherwise, make sure it is hidden. // ResizeVideo(m_bSizeVideoToWindow); ShowVideo(m_bPreviewVisible); return hr; } /////////////////////////////// // Stop // HRESULT CPreviewGraph::Stop() { DBG_FN("CPreviewGraph::Stop"); HRESULT hr = S_OK; if (m_pGraphBuilder) { CComQIPtr pMC(m_pGraphBuilder); if (pMC) { hr = pMC->Stop(); CHECK_S_OK2(hr, ("CPreviewGraph::Stop, IMediaControl::Stop " "failed")); } else { hr = E_FAIL; DBG_ERR(("CPreviewGraph::Stop unable to get MediaControl " "interface, returning hr = 0x%08lx", hr)); } } CHECK_S_OK(hr); return hr; } /////////////////////////////// // Pause // HRESULT CPreviewGraph::Pause() { DBG_FN("CPreviewGraph::Pause"); HRESULT hr = S_OK; WIAVIDEO_STATE State = GetState(); if ((State != WIAVIDEO_VIDEO_CREATED) && (State != WIAVIDEO_VIDEO_PLAYING)) { hr = E_FAIL; CHECK_S_OK2(hr, ("CPreviewGraph::Pause, cannot begin pause " "because we are in an incorrect state, " "current state = '%lu'", State)); return hr; } else if (State == WIAVIDEO_VIDEO_PAUSED) { DBG_WRN(("CPreviewGraph::Pause, pause was called, but we are already " "paused, doing nothing.")); return hr; } CComQIPtr pMC(m_pGraphBuilder); if (pMC) { hr = pMC->Pause(); if (SUCCEEDED(hr)) { SetState(WIAVIDEO_VIDEO_PAUSED); } CHECK_S_OK2(hr, ("CPreviewGraph::Pause, failed to pause video")); } else { DBG_ERR(("CPreviewGraph::Pause unable to get MediaControl interface")); } return hr; } /////////////////////////////// // ProcessAsyncImage // // Called by CPreviewGraph // when user presses hardware // button and it is delivered to // Still Pin. // HRESULT CPreviewGraph::ProcessAsyncImage(const CSimpleString *pNewImage) { DBG_FN("CPreviewGraph::ProcessAsyncImage"); HRESULT hr = S_OK; if (m_pWiaVideo) { hr = m_pWiaVideo->ProcessAsyncImage(pNewImage); } else { DBG_WRN(("CPreviewGraph::ProcessAsyncImage failed, m_pWiaVideo " "is NULL")); } return hr; } /////////////////////////////// // GetStillPinCaps // HRESULT CPreviewGraph::GetStillPinCaps(IBaseFilter *pCaptureFilter, IPin **ppStillPin, IAMVideoControl **ppVideoControl, LONG *plCaps) { DBG_FN("CPreviewGraph::GetStillPinCaps"); HRESULT hr = S_OK; ASSERT(pCaptureFilter != NULL); ASSERT(ppStillPin != NULL); ASSERT(ppVideoControl != NULL); ASSERT(plCaps != NULL); if ((pCaptureFilter == NULL) || (ppStillPin == NULL) || (ppVideoControl == NULL) || (plCaps == NULL)) { hr = E_POINTER; CHECK_S_OK2(hr, ("CPreviewGraph::GetStillPinCaps received a NULL " "param")); } if (hr == S_OK) { // // Attempt to find the still pin on the capture filter // This will decide the type of graph we are going to build // hr = m_pCaptureGraphBuilder->FindInterface(&PIN_CATEGORY_STILL, &MEDIATYPE_Video, pCaptureFilter, IID_IPin, (void **)ppStillPin); } if (hr == S_OK) { // determine if it is triggerable or not. hr = m_pCaptureGraphBuilder->FindInterface(&PIN_CATEGORY_STILL, &MEDIATYPE_Video, pCaptureFilter, IID_IAMVideoControl, (void **)ppVideoControl); if ((hr == S_OK) && (*ppVideoControl) && (*ppStillPin)) { hr = (*ppVideoControl)->GetCaps(*ppStillPin, plCaps); } if (hr == S_OK) { // // If the still pin cannot be triggered externally or internally // then it is useless to us, so just ignore it. // if (!(*plCaps & (VideoControlFlag_ExternalTriggerEnable | VideoControlFlag_Trigger))) { *plCaps = 0; *ppStillPin = NULL; *ppVideoControl = NULL; } } } else { DBG_PRT(("CPreviewGraph::GetStillPinCaps, Capture Filter does not " "have a still pin")); } return hr; } /////////////////////////////// // AddStillFilterToGraph // // Add the WIA Stream Snapshot // filter to our graph // HRESULT CPreviewGraph::AddStillFilterToGraph(LPCWSTR pwszFilterName, IBaseFilter **ppFilter, IStillSnapshot **ppSnapshot) { DBG_FN("CPreviewGraph::AddStillFilterToGraph"); HRESULT hr = S_OK; ASSERT(pwszFilterName != NULL); ASSERT(ppFilter != NULL); ASSERT(ppSnapshot != NULL); if ((pwszFilterName == NULL) || (ppFilter == NULL) || (ppSnapshot == NULL)) { hr = E_POINTER; CHECK_S_OK2(hr, ("CPreviewGraph::AddStillFilterToGraph received " "NULL params")); } if (hr == S_OK) { // // Create the still filter // hr = CoCreateInstance(CLSID_STILL_FILTER, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void**)ppFilter); CHECK_S_OK2(hr, ("CPreviewGraph::AddStillFilterToGraph failed to " "CoCreate Still Image Filter")); } if (hr == S_OK) { // // Add the still filter to the graph. // hr = m_pGraphBuilder->AddFilter(*ppFilter, pwszFilterName); CHECK_S_OK2(hr, ("CPreviewGraph::AddStillFilterToGraph failed to " "add '%ls' filter to the graph", pwszFilterName)); } if (hr == S_OK) { hr = (*ppFilter)->QueryInterface(IID_IStillSnapshot, (void **)ppSnapshot); CHECK_S_OK2(hr, ("CPreviewGraph::AddStillFilterToGraph, failed " "to get IStillSnapshot interface on still filter")); } return hr; } /////////////////////////////// // AddColorConverterToGraph // // Creates the capture filter // identified by the device ID // and returns it. // HRESULT CPreviewGraph::AddColorConverterToGraph(LPCWSTR pwszFilterName, IBaseFilter **ppColorSpaceConverter) { HRESULT hr = S_OK; ASSERT(pwszFilterName != NULL); ASSERT(ppColorSpaceConverter != NULL); if ((pwszFilterName == NULL) || (ppColorSpaceConverter == NULL)) { hr = E_POINTER; CHECK_S_OK2(hr, ("CPreviewGraph::AddColorConverterToGraph, " "received a NULL pointer")); return hr; } // // Create the Color Converter filter. // if (hr == S_OK) { hr = CoCreateInstance(CLSID_Colour, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void**) ppColorSpaceConverter); CHECK_S_OK2(hr, ("CPreviewGraph::AddColorConverterToGraph failed to " "create the DShow Color Converter Filter")); } if (hr == S_OK) { hr = m_pGraphBuilder->AddFilter(*ppColorSpaceConverter, pwszFilterName); CHECK_S_OK2(hr, ("CPreviewGraph::AddColorConverterToGraph failed to " "add '%ls' filter to the graph", pwszFilterName)); } return hr; } /////////////////////////////// // AddVideoRendererToGraph // // HRESULT CPreviewGraph::AddVideoRendererToGraph(LPCWSTR pwszFilterName, IBaseFilter **ppVideoRenderer) { HRESULT hr = S_OK; ASSERT(pwszFilterName != NULL); ASSERT(ppVideoRenderer != NULL); if ((pwszFilterName == NULL) || (ppVideoRenderer == NULL)) { hr = E_POINTER; CHECK_S_OK2(hr, ("CPreviewGraph::AddVideoRendererToGraph, " "received a NULL pointer")); return hr; } // // Create the Video Renderer filter. // if (hr == S_OK) { BOOL bUseVMR = FALSE; // // even if we fail, in the worst case we don't use the vmr filter. // Not the end of the world. // CWiaUtil::GetUseVMR(&bUseVMR); if (bUseVMR) { hr = CoCreateInstance(CLSID_VideoMixingRenderer, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void**) ppVideoRenderer); } else { hr = CoCreateInstance(CLSID_VideoRenderer, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void**) ppVideoRenderer); } CHECK_S_OK2(hr, ("CPreviewGraph::AddVideoRendererToGraph failed to " "create the DShow Video Renderer Filter")); } if (hr == S_OK) { hr = m_pGraphBuilder->AddFilter(*ppVideoRenderer, pwszFilterName); CHECK_S_OK2(hr, ("CPreviewGraph::AddVideoRenderer failed to " "add '%ls' filter to the graph", pwszFilterName)); } return hr; } /////////////////////////////// // InitVideoWindows // // Initialize the video windows // so that they don't have // an owner, and they are not // visible. // HRESULT CPreviewGraph::InitVideoWindows(HWND hwndParent, IBaseFilter *pCaptureFilter, IVideoWindow **ppPreviewVideoWindow, BOOL bStretchToFitParent) { DBG_FN("CPreviewGraph::InitVideoWindows"); HRESULT hr = S_OK; ASSERT(pCaptureFilter != NULL); ASSERT(ppPreviewVideoWindow != NULL); if ((pCaptureFilter == NULL) || (ppPreviewVideoWindow == NULL)) { hr = E_POINTER; CHECK_S_OK2(hr, ("CPreviewGraph::InitVideoWindows received NULL " "params")); } // // If the still pin exists, make sure that the video renderer hanging // off of this path in the graph is NOT visible. // if (hr == S_OK) { if (m_pStillPin) { CComPtr pStillVW; hr = m_pCaptureGraphBuilder->FindInterface(&PIN_CATEGORY_STILL, &MEDIATYPE_Video, pCaptureFilter, IID_IVideoWindow, (void**)&pStillVW); CHECK_S_OK2(hr, ("CPreviewGraph::InitVideoWindows failed to " "find video renderer off of the still " "filter pin")); // // We hide the video renderer attached to the still pin stream // since it will contain the still image, which we save to a // file, rather than show it on the desktop. // if (hr == S_OK) { CDShowUtil::ShowVideo(FALSE, pStillVW); CDShowUtil::SetVideoWindowParent(NULL, pStillVW, NULL); } } // // If this fails, it is not fatal // hr = S_OK; } if (hr == S_OK) { // // Find the video renderer hanging off of the capture pin path in // the graph and make sure that it is made the child of the // parent window. hr = m_pCaptureGraphBuilder->FindInterface( &PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video, pCaptureFilter, IID_IVideoWindow, (void **)ppPreviewVideoWindow); CHECK_S_OK2(hr, ("CPreviewGraph::InitVideoWindows, failed to " "find video renderer off of capture/preview pin")); } if (hr == S_OK) { // // Hide the video window before we set it's parent. // CDShowUtil::ShowVideo(FALSE, *ppPreviewVideoWindow); // // Set the video window's parent. // CDShowUtil::SetVideoWindowParent(hwndParent, *ppPreviewVideoWindow, &m_lStyle); } return hr; } /////////////////////////////// // CreateCaptureFilter // // Creates the capture filter // identified by the device ID // and returns it. // HRESULT CPreviewGraph::CreateCaptureFilter(IMoniker *pCaptureDeviceMoniker, IBaseFilter **ppCaptureFilter) { DBG_FN("CPreviewGraph::CreateCaptureFilter"); ASSERT(pCaptureDeviceMoniker != NULL); ASSERT(ppCaptureFilter != NULL); HRESULT hr = S_OK; if ((pCaptureDeviceMoniker == NULL) || (ppCaptureFilter == NULL)) { hr = E_POINTER; CHECK_S_OK2(hr, ("CPreviewGraph::CreateCaptureFilter received NULL " "params")); } #ifdef DEBUG CDShowUtil::DumpCaptureMoniker(pCaptureDeviceMoniker); #endif if (hr == S_OK) { hr = pCaptureDeviceMoniker->BindToObject(0, 0, IID_IBaseFilter, (void**)ppCaptureFilter); CHECK_S_OK2(hr, ("CPreviewGraph::CreateCaptureFilter failed to bind " "to device's moniker.")); } return hr; } /////////////////////////////// // AddCaptureFilterToGraph // HRESULT CPreviewGraph::AddCaptureFilterToGraph(IBaseFilter *pCaptureFilter, IPin **ppCapturePin) { HRESULT hr = S_OK; CComPtr pCapturePin; GUID *pMediaSubType = NULL; LONG lWidth = 0; LONG lHeight = 0; LONG lFrameRate = 0; ASSERT(pCaptureFilter != NULL); ASSERT(ppCapturePin != NULL); if ((pCaptureFilter == NULL) || (ppCapturePin == NULL)) { hr = E_POINTER; CHECK_S_OK2(hr, ("CPreviewGraph::AddCaptureFilterToGraph, received " "a NULL pointer")); return hr; } if (SUCCEEDED(hr)) { hr = m_pGraphBuilder->AddFilter(pCaptureFilter, L"Capture Filter"); CHECK_S_OK2(hr, ("CPreviewGraph::AddCaptureFilterToGraph, failed to " "add capture filter to graph")); } // // Find the capture pin so we can render it. // if (SUCCEEDED(hr)) { hr = m_pCaptureGraphBuilder->FindInterface(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video, pCaptureFilter, IID_IPin, (void **)&pCapturePin); CHECK_S_OK2(hr, ("CPreviewGraph::AddCaptureFilterToGraph failed to " "find Capture pin on capture filter")); } // // Get all the video properties we can about this filter. // if (SUCCEEDED(hr)) { hr = CDShowUtil::GetVideoProperties(pCaptureFilter, pCapturePin, m_pVideoProperties); } // // We set the video width and height to the preferred setting size if // the driver's inf specifies these settings. If it does not, // then if the registery tells us to override the capture filter's // default settings, then attempt to set it to 176x144 YUV. // // This is the order we attempt to set preferences: // // Preferred Settings --> If don't exist --> Set to Driver Default if within MIN/MAX range // --> otherwise attempt to set to MINIMUM width/height. // if (SUCCEEDED(hr)) { GUID *pDefaultMediaSubType = &m_pVideoProperties->pMediaType->subtype; LONG lDefaultWidth = m_pVideoProperties->pVideoInfoHeader->bmiHeader.biWidth; LONG lDefaultHeight = m_pVideoProperties->pVideoInfoHeader->bmiHeader.biHeight; // // Validate default values are valid. // if ((lDefaultWidth <= MIN_VIDEO_WIDTH) || (lDefaultHeight <= MIN_VIDEO_HEIGHT)) { lDefaultWidth = MIN_VIDEO_WIDTH; lDefaultHeight = MIN_VIDEO_HEIGHT; } else if ((lDefaultWidth > MAX_VIDEO_WIDTH) || (lDefaultHeight > MAX_VIDEO_HEIGHT)) { lDefaultWidth = MIN_VIDEO_WIDTH; lDefaultHeight = MIN_VIDEO_HEIGHT; } // // If a preferred media subtype exists, use it, otherwise, use the // default specified by the capture filter. // if (m_pVideoProperties->PreferredSettingsMask & PREFERRED_SETTING_MASK_MEDIASUBTYPE) { pMediaSubType = &m_pVideoProperties->PreferredMediaSubType; DBG_TRC(("Settings: Using preferred media subtype -> dump of actual type is below")); } else { pMediaSubType = pDefaultMediaSubType; DBG_TRC(("Settings: Using default media subtype, no preferred media subtype " "found -> dump of actual type is below")); } // // If the default width and height exist, use it, otherwise use the // default width and height, provided they are valid values // if (m_pVideoProperties->PreferredSettingsMask & PREFERRED_SETTING_MASK_VIDEO_WIDTH_HEIGHT) { lWidth = m_pVideoProperties->PreferredWidth; lHeight = m_pVideoProperties->PreferredHeight; // // Validate preferred settings are valid. If they are not, then // set to default value. // if ((lWidth < MIN_VIDEO_WIDTH) || (lHeight < MIN_VIDEO_HEIGHT) || (lWidth > MAX_VIDEO_WIDTH) || (lHeight > MAX_VIDEO_HEIGHT)) { DBG_TRC(("Settings: Using default video width and height, preferred settings were invalid " "-> Invalid Width = %lu, Invalid Height = %lu", lWidth, lHeight)); lWidth = lDefaultWidth; lHeight = lDefaultHeight; } else { DBG_TRC(("Settings: Using preferred settings of video width and height " "-> dump of actual size is below")); } } else { lWidth = lDefaultWidth; lHeight = lDefaultHeight; } hr = CDShowUtil::SetPreferredVideoFormat(pCapturePin, pMediaSubType, lWidth, lHeight, m_pVideoProperties); if (hr != S_OK) { DBG_TRC(("Failed to set width = '%lu' and height = '%lu', " "attempting to set it to its default settings of " "width = '%lu', height = '%lu'", lWidth, lHeight, lDefaultWidth, lDefaultHeight)); hr = CDShowUtil::SetPreferredVideoFormat(pCapturePin, pDefaultMediaSubType, lDefaultWidth, lDefaultHeight, m_pVideoProperties); } if (hr != S_OK) { hr = S_OK; DBG_WRN(("Failed on all attempts to set the preferrences of " "the video properties (MediaSubType, width, height). " "Attempting to continue anyway")); } } // // 6. Attempt to set the frame rate to 30 frames per second. If this // fails for some reason, try 15 frames per second. If that fails, // then just accept the default and keep going. // if (SUCCEEDED(hr)) { LONG lDefaultFrameRate = m_pVideoProperties->dwFrameRate; if (lDefaultFrameRate < BACKUP_FRAME_RATE) { lDefaultFrameRate = PREFERRED_FRAME_RATE; } if (m_pVideoProperties->PreferredSettingsMask & PREFERRED_SETTING_MASK_VIDEO_FRAMERATE) { lFrameRate = m_pVideoProperties->PreferredFrameRate; if (lFrameRate < BACKUP_FRAME_RATE) { lFrameRate = lDefaultFrameRate; } } else { lFrameRate = PREFERRED_FRAME_RATE; } hr = CDShowUtil::SetFrameRate(pCapturePin, lFrameRate, m_pVideoProperties); if (hr != S_OK) { DBG_WRN(("WARNING: Failed to set frame rate to %lu. " "This is not fatal, attempting to set it to %lu, " "hr = 0x%08lx", lFrameRate, BACKUP_FRAME_RATE, hr)); hr = CDShowUtil::SetFrameRate(pCapturePin, lDefaultFrameRate, m_pVideoProperties); if (hr != S_OK) { DBG_WRN(("WARNING: Failed to set frame rate to %lu. " "This is not fatal, continuing to build graph, " "hr = 0x%08lx", lDefaultFrameRate, hr)); } } // // This is a nice to have, but if we can't then continue // anyway, better low quality video than no video at all. // hr = S_OK; } // // Set the picture and camera attributes to our preferred settings, // if we can. // if (SUCCEEDED(hr)) { // // If this camera supports setting the picture attributes, then // lets make sure that we set the ones we are interested in. // // This uses the DShow IAMVideoProcAmp interface // if (m_pVideoProperties->bPictureAttributesUsed) { // // Make sure we are outputing color video (as opposed to black and white) // hr = CDShowUtil::SetPictureAttribute(pCaptureFilter, &m_pVideoProperties->ColorEnable, (LONG) TRUE, VideoProcAmp_Flags_Manual); // // Turn on backlight compensation, to get the best video we can. // hr = CDShowUtil::SetPictureAttribute(pCaptureFilter, &m_pVideoProperties->BacklightCompensation, (LONG) TRUE, VideoProcAmp_Flags_Manual); // // Make sure white balance is set to auto // hr = CDShowUtil::SetPictureAttribute(pCaptureFilter, &m_pVideoProperties->WhiteBalance, m_pVideoProperties->WhiteBalance.lCurrentValue, VideoProcAmp_Flags_Auto); } // // If the camera supports setting the camera attributes, then set the // camera attributes we are interested in. // if (m_pVideoProperties->bCameraAttributesUsed) { // // Turn on auto exposure. // hr = CDShowUtil::SetCameraAttribute(pCaptureFilter, &m_pVideoProperties->Exposure, m_pVideoProperties->Exposure.lCurrentValue, CameraControl_Flags_Auto); } hr = S_OK; } // // Dump the video properties // CDShowUtil::MyDumpVideoProperties(m_pVideoProperties); if (SUCCEEDED(hr)) { *ppCapturePin = pCapturePin; (*ppCapturePin)->AddRef(); } return hr; } /////////////////////////////// // ConnectFilters // // This function connects the // capture filter's still pin // or it's capture pin to the // color space converter. It // then connects the color space // converter to the WIA Stream // snapshot filter. Last, it // renders the WIA Stream Snapshot // filter to bring in any remaining // required filters (such as the // video renderer) // HRESULT CPreviewGraph::ConnectFilters(IGraphBuilder *pGraphBuilder, IPin *pMediaSourceOutputPin, IBaseFilter *pColorSpaceFilter, IBaseFilter *pWiaFilter, IBaseFilter *pVideoRenderer) { HRESULT hr = S_OK; CComPtr pOutputPinToConnect; ASSERT(pGraphBuilder != NULL); ASSERT(pMediaSourceOutputPin != NULL); ASSERT(pVideoRenderer != NULL); if ((pGraphBuilder == NULL) || (pMediaSourceOutputPin == NULL) || (pVideoRenderer == NULL)) { hr = E_POINTER; CHECK_S_OK2(hr, ("CPreviewGraph::ConnectFilters received a NULL pointer")); } pOutputPinToConnect = pMediaSourceOutputPin; DBG_TRC(("CPreviewGraph::ConnectFilters, PinToRender is the " "Capture Filter's Output Pin")); if (pColorSpaceFilter) { CComPtr pColorInputPin; CComPtr pColorOutputPin; // // Get the input pin on the color space filter. // if (hr == S_OK) { hr = CDShowUtil::GetPin(pColorSpaceFilter, PINDIR_INPUT, &pColorInputPin); CHECK_S_OK2(hr, ("CPreviewGraph::ConnectFilters, failed to get the " "color space converter's input pin")); } // // Connect the Capture Filter's output pin to the color space // converter's input pin. // if (hr == S_OK) { hr = pGraphBuilder->Connect(pMediaSourceOutputPin, pColorInputPin); CHECK_S_OK2(hr, ("CPreviewGraph::ConnectFilters, failed to connect the " "capture filter's pin to the color space converter pin")); } // // Get the output pin on the color space converter. // if (hr == S_OK) { hr = CDShowUtil::GetPin(pColorSpaceFilter, PINDIR_OUTPUT, &pColorOutputPin); CHECK_S_OK2(hr, ("CPreviewGraph::ConnectFilters, failed to get the " "color space converter's output pin")); } if (hr == S_OK) { pOutputPinToConnect = pColorOutputPin; DBG_TRC(("CPreviewGraph::ConnectFilters, PinToRender is the " "Color Space Converter's Output Pin")); } // // If this fails, so what. Try to connect WIA Stream Snapshot // filter anyway. // hr = S_OK; } if (pWiaFilter) { CComPtr pWiaInputPin; CComPtr pWiaOutputPin; // // Get the input pin on the WIA Stream Snapshot filter. // if (hr == S_OK) { hr = CDShowUtil::GetPin(pWiaFilter, PINDIR_INPUT, &pWiaInputPin); CHECK_S_OK2(hr, ("CPreviewGraph::ConnectFilters, failed to get the " "WIA Stream Snapshot filter's input pin")); } // // Connect the output pin of the color space converter to the input // pin on the WIA Stream Snapshot filter. // if (hr == S_OK) { hr = pGraphBuilder->Connect(pOutputPinToConnect, pWiaInputPin); CHECK_S_OK2(hr, ("CPreviewGraph::ConnectFilters, failed to connect the " "pin to render to the WIA Stream Snapshot " "input pin")); } // // Get the output pin on the WIA Stream Snapshot filter. // if (hr == S_OK) { hr = CDShowUtil::GetPin(pWiaFilter, PINDIR_OUTPUT, &pWiaOutputPin); CHECK_S_OK2(hr, ("CPreviewGraph::ConnectFilters, failed to get the " "WIA Stream Snapshot filter's output pin")); } if (hr == S_OK) { pOutputPinToConnect = pWiaOutputPin; DBG_TRC(("CPreviewGraph::ConnectFilters, PinToRender is the " "WIA Stream Snapshot Filter's Output Pin")); } } // // Render the output pin of the WIA Stream Snapshot filter. // This completes the graph building process. // if (hr == S_OK) { CComPtr pVideoRendererInputPin; // // Get the input pin on the WIA Stream Snapshot filter. // if (hr == S_OK) { hr = CDShowUtil::GetPin(pVideoRenderer, PINDIR_INPUT, &pVideoRendererInputPin); CHECK_S_OK2(hr, ("CPreviewGraph::ConnectFilters, failed to get the " "WIA Stream Snapshot filter's input pin")); } // // Connect the output pin of the color space converter to the input // pin on the WIA Stream Snapshot filter. // if (hr == S_OK) { hr = pGraphBuilder->Connect(pOutputPinToConnect, pVideoRendererInputPin); CHECK_S_OK2(hr, ("CPreviewGraph::ConnectFilters, failed to connect the " "pin to render to the Video Renderer " "input pin")); } } pOutputPinToConnect = NULL; return hr; } /////////////////////////////// // BuildPreviewGraph // // This builds the preview graph // based on the device ID we // pass it. // HRESULT CPreviewGraph::BuildPreviewGraph(IMoniker *pCaptureDeviceMoniker, BOOL bStretchToFitParent) { DBG_FN("CPreviewGraph::BuildPreviewGraph"); ASSERT(pCaptureDeviceMoniker != NULL); HRESULT hr = S_OK; CComPtr pCapturePin; if (pCaptureDeviceMoniker == NULL) { hr = E_POINTER; CHECK_S_OK2(hr, ("ERROR: CPreviewGraph::BuildPreviewGraph " "received a NULL param")); return hr; } // // This function will build one of three possible graphs. // // (1) The capture filter does NOT have a // still pin on it (or if it does it is useless because // it is not triggerable either via hardware, or programmatically) // // CaptureFilter(Capture Pin) -> Decoder -> Color Converter -> WIA StreamSnapshot -> Renderer // // (2) The capture filter has a still pin that is programmatically // triggerable. // // CaptureFilter(CapturePin) -> Decoder -> Video Renderer // (StillPin) -> Decoder -> Color Converter -> WIA StreamSnapshot -> Renderer // // (3) The capture filter has a still pin, but it is only // triggered via external hardware button. In this case, if we // trigger a snapshot programmatically, the image comes from // the WIA Snapshot filter off of the Capture/Preview Pin. // If the hardware button is pressed, the image comes from the // WIA snapshot filter off of the StillPin. // // CaptureFilter(CapturePin) -> Decoder -> Color Converter -> WIA StreamSnapshot -> Renderer // (StillPin) -> Decoder -> Color Converter -> WIA StreamSnapshot -> Renderer // // DBG_TRC(("CPreviewGraph::BuildPreviewGraph - Starting to build preview " "graph")); // // 1. Create the capture filter based on the device ID we were given. // if (SUCCEEDED(hr)) { hr = CreateCaptureFilter(pCaptureDeviceMoniker, &m_pCaptureFilter); // passed by ref CHECK_S_OK2(hr, ("CPreviewGraph::BuildPreviewGraph, failed to " "create capture filter")); } // // 2. Create the DShow graph builder objects allowing us to // manipulate the graph. // if (SUCCEEDED(hr)) { hr = CDShowUtil::CreateGraphBuilder(&m_pCaptureGraphBuilder, &m_pGraphBuilder); CHECK_S_OK2(hr, ("CPreviewGraph::BuildPreviewGraph, failed to " "create DShow graph builder object")); } // // 3. Add the capture filter to the graph. // if (SUCCEEDED(hr)) { hr = AddCaptureFilterToGraph(m_pCaptureFilter, &pCapturePin); } // // 4. Get the Still Pin capabilities of the capture filter (if it has a // still pin at all) // if (SUCCEEDED(hr)) { hr = GetStillPinCaps(m_pCaptureFilter, &m_pStillPin, &m_pVideoControl, &m_lStillPinCaps); if (hr != S_OK) { // // This is not an error condition, it simply means that the // capture filter does not have a still pin on it. That's // okay, we can deal with that below. // hr = S_OK; } } // // Render the preview/capture stream. // ================================== // // If we don't have a still pin, or the still pin cannot be internally // triggered, we build the following preview graph. // // CaptureFilter(Capture Pin) -> Decoder -> WIA StreamSnapshot -> Renderer // // If we do have a still pin, and it is internally triggerable, then // we build the following preview graph (and add the still filter to the // still pin) // // CaptureFilter(CapturePin) -> Decoder -> WIA StreamSnapshot -> Renderer // (StillPin) -> Decoder -> WIA StreamSnapshot -> Renderer // // // 5. If we don't have a still pin, or it is only externally triggerable // then add the WIA StreamSnapshot Filter to the preview/capture pin. // if ((m_pStillPin == NULL) || (m_lStillPinCaps & VideoControlFlag_Trigger) == 0) { CComPtr pWiaFilter; CComPtr pColorSpaceConverter; CComPtr pVideoRenderer; DBG_TRC(("CPreviewGraph::BuildPreviewGraph, capture filter does NOT have " "a still pin, image captures will be triggered " "through the WIA Snapshot filter")); if (hr == S_OK) { hr = AddColorConverterToGraph(L"Color Converter on Capture Pin Graph", &pColorSpaceConverter); CHECK_S_OK2(hr, ("CPreviewGraph::BuildPreviewGraph, failed to " "add Color Converter to graph")); // // Even if this fails, we still may be able to successfully build // a graph, so attempt to continue. // hr = S_OK; } if (hr == S_OK) { hr = AddStillFilterToGraph(L"Still Filter On Capture", &pWiaFilter, &m_pCapturePinSnapshot); CHECK_S_OK2(hr, ("CPreviewGraph::BuildPreviewGraph, failed to " "add 'Still Filter On Capture' to graph")); } if (hr == S_OK) { hr = AddVideoRendererToGraph(L"Video Renderer On Capture", &pVideoRenderer); CHECK_S_OK2(hr, ("CPreviewGraph::BuildPreviewGraph, failed to " "add 'Video Renderer On Capture' to graph")); } if (hr == S_OK) { // // Connect as follows: // // Capture Filter --> Color Space Converter --> WIA Stream Snapshot Filter // hr = ConnectFilters(m_pGraphBuilder, pCapturePin, pColorSpaceConverter, pWiaFilter, pVideoRenderer); } CHECK_S_OK2(hr, ("CPreviewGraph::BuildPreviewGraph, failed to " "add still filter to graph off of the capture pin")); } else { CComPtr pColorSpaceConverter; CComPtr pVideoRenderer; if (hr == S_OK) { hr = AddColorConverterToGraph(L"Color Converter on Capture Pin Graph", &pColorSpaceConverter); CHECK_S_OK2(hr, ("CPreviewGraph::BuildPreviewGraph, failed to " "add Color Converter to graph")); // // Even if this fails, we still should be able to build the graph, // so continue. // hr = S_OK; } if (hr == S_OK) { hr = AddVideoRendererToGraph(L"Video Renderer On Capture", &pVideoRenderer); CHECK_S_OK2(hr, ("CPreviewGraph::BuildPreviewGraph, failed to " "add 'Video Renderer On Capture' to graph")); } // // Still Pin exists, and it is triggerable, so simply render // the capture pin. We connect the Still Pin below // hr = ConnectFilters(m_pGraphBuilder, pCapturePin, pColorSpaceConverter, NULL, pVideoRenderer); CHECK_S_OK2(hr, ("CPreviewGraph::BuildPreviewGraph, failed to connect " "filters to render capture pin, therefore won't see video")); } CDShowUtil::MyDumpGraph(TEXT("Capture Graph before processing Still Pin (if exists)"), m_pGraphBuilder); // // Render the Still Pin stream // =========================== // // If we have a still pin, then add the still filter to the // graph, and render the still pin. This will produce the // following graph: // // CaptureFilter(StillPin) -> Decoder -> StillFilter -> Renderer (hidden) // // 6. Now add the WIA Stream Snapshot filter to the still pin if it // exists. // if (SUCCEEDED(hr) && (m_pStillPin)) { CComPtr pWiaFilter; CComPtr pColorSpaceConverter; CComPtr pVideoRenderer; if (hr == S_OK) { hr = AddColorConverterToGraph(L"Color Converter on Still Pin Graph", &pColorSpaceConverter); CHECK_S_OK2(hr, ("CPreviewGraph::BuildPreviewGraph, failed to " "add Color Converter to graph")); // // This is not fatail if we fail. Ideally we would like it in // here, but try going on anyway, in case we can succeed without // this filter. // hr = S_OK; } if (hr == S_OK) { hr = AddStillFilterToGraph(L"Still filter on Still", &pWiaFilter, &m_pStillPinSnapshot); CHECK_S_OK2(hr, ("CPreviewGraph::BuildPreviewGraph, failed to add " "'Still filter on Still' filter to the graph. " "Probably won't be able to capture still images")); } if (hr == S_OK) { hr = AddVideoRendererToGraph(L"Video Renderer On Still", &pVideoRenderer); CHECK_S_OK2(hr, ("CPreviewGraph::BuildPreviewGraph, failed to " "add 'Video Renderer On Still' to graph")); } if (hr == S_OK) { // // Connect as follows: // // Capture Filter --> Color Space Converter --> WIA Stream Snapshot Filter // hr = ConnectFilters(m_pGraphBuilder, m_pStillPin, pColorSpaceConverter, pWiaFilter, pVideoRenderer); CHECK_S_OK2(hr, ("CPreviewGraph::BuildPreviewGraph, failed to " "connect graph to Still Pin on Capture Filter. " "This will prevent us from capturing still images")); } } CDShowUtil::MyDumpGraph(TEXT("*** Complete Graph ***"), m_pGraphBuilder); // // 7. Turn off the graph's clock. We do this in case some frames are // delivered late, at least they will still be delivered, the graph // won't drop them. Since we don't have any sound, we can do this // without worrying about losing sync with our sound. // if (SUCCEEDED(hr)) { hr = CDShowUtil::TurnOffGraphClock(m_pGraphBuilder); if (hr != S_OK) { DBG_WRN(("CPreviewGraph::BuildPreviewGraph, failed to turn off the " "graph clock. This is not fatal, continuing..., hr = 0x%lx", hr)); hr = S_OK; } } // // 8. Register ourselves with the WIA StreamSnapshot Filter so that // a callback of ours will be called if a still image is available. // if (SUCCEEDED(hr)) { // // the graph is ready to run. Initialize still filter and // register callback to get notification for new snapshots. // if (m_pCapturePinSnapshot) { m_pCapturePinSnapshot->SetSamplingSize( CAPTURE_NUM_SAMPLES_TO_CACHE); } if (m_pStillPinSnapshot) { m_pStillPinSnapshot->SetSamplingSize(STILL_NUM_SAMPLES_TO_CACHE); } hr = m_StillProcessor.RegisterStillProcessor(m_pCapturePinSnapshot, m_pStillPinSnapshot); CHECK_S_OK2(hr, ("CPreviewGraph::BuildPreviewGraph, failed to " "register our still processor's callback fn")); } // // 9. Get the Video Renderer window off of the preview/capture pin. // This will allow us to control the renderer position, size, etc. // if (SUCCEEDED(hr)) { hr = InitVideoWindows(m_hwndParent, m_pCaptureFilter, &m_pPreviewVW, bStretchToFitParent); } return hr; } /////////////////////////////// // TeardownPreviewGraph // HRESULT CPreviewGraph::TeardownPreviewGraph() { DBG_FN("CPreviewGraph::TeardownPreviewGraph"); HRESULT hr = S_OK; m_pStillPin = NULL; m_pVideoControl = NULL; m_pStillPinSnapshot = NULL; m_pCapturePinSnapshot = NULL; if (m_pPreviewVW) { CDShowUtil::ShowVideo(FALSE, m_pPreviewVW); CDShowUtil::SetVideoWindowParent(NULL, m_pPreviewVW, &m_lStyle); m_pPreviewVW = NULL; } // // Remove all the filters from the graph. // if (m_pGraphBuilder) { RemoveAllFilters(); } CDShowUtil::MyDumpGraph(TEXT("Graph after removing all filters ") TEXT("(should be empty)"), m_pGraphBuilder); m_pCaptureGraphBuilder = NULL; m_pGraphBuilder = NULL; m_pCaptureFilter = NULL; return hr; } /////////////////////////////// // RemoveAllFilters // // Notice this function makes no // attempt to disconnect each filter // before removing it. According to // MSDN, you do not need to disconnect // a filter before removing it, you only // need to ensure that the graph is stopped. // HRESULT CPreviewGraph::RemoveAllFilters() { ASSERT(m_pGraphBuilder != NULL); HRESULT hr = S_OK; CComPtr pEnum = NULL; DWORD dwRefCount = 0; BOOL bDone = FALSE; if (m_pGraphBuilder == NULL) { hr = E_POINTER; CHECK_S_OK2(hr, ("CPreviewGraph::RemoveAllFilters, m_pGraphBuilder " "is NULL, cannot remove filters")); } if (hr == S_OK) { hr = m_pGraphBuilder->EnumFilters(&pEnum); } if (pEnum) { // enumerate each filter. while (!bDone) { CComPtr pFilter = NULL; DWORD dwNumFetched = 0; // // Notice we reset our enumeration on every iteration since // the act of removing a filter from the graph may do some // funny things, so we really want to always remove the first // filter we get from the list until the list is empty. // hr = pEnum->Reset(); if (hr == S_OK) { hr = pEnum->Next(1, &pFilter, &dwNumFetched); } if (hr == S_OK) { // // This will disconnect the filter's pins and remove // it from the graph. If this fails, we want to get out // of the loop because otherwise we'll be in an endless loop // since we failed to remove the filter, then we reset our // enum and get the next filter, which will be this // filter again. // hr = m_pGraphBuilder->RemoveFilter(pFilter); CHECK_S_OK2(hr, ("CPreviewGraph::RemoveAllFilters, " "RemoveFilter failed")); // // Release our ref count. // pFilter = NULL; } if (hr != S_OK) { bDone = TRUE; } } } hr = S_OK; return hr; } ////////////////////////////////////////////////////////////////////// // HandlePowerEvent // LRESULT CPreviewGraph::HandlePowerEvent(WPARAM wParam, LPARAM lParam) { LRESULT iReturn = TRUE; if (wParam == PBT_APMQUERYSUSPEND) { if (GetState() != WIAVIDEO_NO_VIDEO) { iReturn = BROADCAST_QUERY_DENY; } } return iReturn; } /////////////////////////////// // CreateHiddenWindow // // Used to handle power management // messages. // HRESULT CPreviewGraph::CreateHiddenWindow() { HRESULT hr = S_OK; WNDCLASSEX wc = {0}; wc.style = 0; wc.cbSize = sizeof(wc); wc.lpfnWndProc = HiddenWndProc; wc.cbClsExtra = 0; wc.cbWndExtra = sizeof(this); wc.hInstance = _Module.GetModuleInstance(); wc.hIcon = NULL; wc.hIconSm = NULL; wc.hCursor = 0; wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); wc.lpszMenuName = 0; wc.lpszClassName = TEXT("WIAVIDEO_POWERMGMT"); RegisterClassEx(&wc); m_hwndPowerMgmt = CreateWindowEx(0, TEXT("WIAVIDEO_POWERMGMT"), TEXT("WIAVIDEO_POWERMGMT"), 0, 0, 0, 0, 0, NULL, NULL, _Module.GetModuleInstance(), this); return hr; } /////////////////////////////// // DestroyHiddenWindow // HRESULT CPreviewGraph::DestroyHiddenWindow() { HRESULT hr = S_OK; if (m_hwndPowerMgmt) { SendMessage(m_hwndPowerMgmt, WM_CLOSE, 0, 0); } m_hwndPowerMgmt = NULL; return hr; } ////////////////////////////////////////////////////////////////////// // HiddenWndProc // // Static Function // LRESULT CALLBACK CPreviewGraph::HiddenWndProc(HWND hwnd, UINT uiMessage, WPARAM wParam, LPARAM lParam) { LRESULT iReturn = 0; switch (uiMessage) { case WM_CREATE: { CREATESTRUCT *pCreateInfo = reinterpret_cast(lParam); if (pCreateInfo) { SetWindowLongPtr(hwnd, 0, reinterpret_cast(pCreateInfo->lpCreateParams)); } } break; case WM_POWERBROADCAST: { CPreviewGraph *pPreviewGraph = NULL; pPreviewGraph = reinterpret_cast(GetWindowLongPtr(hwnd, 0)); if (pPreviewGraph) { iReturn = pPreviewGraph->HandlePowerEvent(wParam, lParam); } } break; case WM_CLOSE: DestroyWindow(hwnd); break; default: iReturn = DefWindowProc(hwnd, uiMessage, wParam, lParam); break; } return iReturn; }