// ---------------------------------------------------------------------------
// File: CDVGraph.cpp
// 
// Desc: CDVGraph Class definition, it supports DV Graph Building
//       This is the class to build all AVC graphs using 
//       MSTape.sys
//      
// Copyright (c) 2000-2002 Microsoft Corporation. All rights reserved.
//----------------------------------------------------------------------------

#include "CDVGraph.h"

#include <mmreg.h>
#include "dbgsup.h"

BOOL IsDeviceOutputDV(IBaseFilter * pFilter);


/*-----------------------------------------------------------------------------
|   Function:   CDVGraph::CDVGraph
|   Purpose:    Constructor for digital tv graph class.  
|   Arguments:  None 
|   Returns:    None 
|   Notes:      initializes the digital tv graph components
\----------------------------------------------------------------------------*/
CDVGraph::CDVGraph(void)
    : m_VideoFormat( DVENCODERVIDEOFORMAT_NTSC )
    , m_DVResolution( DVRESOLUTION_HALF )
    , m_SubunitMode ( VcrMode )
    , m_pGraph( NULL )
    , m_pCaptureGraphBuilder( NULL )
    , m_pMediaControl( NULL )
    , m_pMediaEvent( NULL )
    , m_pInputFileFilter (NULL )
    , m_pVideoWindow( NULL )
    , m_pDeviceFilter( NULL )
    , m_pIAMExtDevice( NULL )
    , m_pIAMExtTransport( NULL )
    , m_pIAMTCReader( NULL )
    , m_pDroppedFrames( NULL )
{
    CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
}

/*-----------------------------------------------------------------------------
|   Function:   CDVGraph::~CDVGraph
|   Purpose:    Destructor for the graph info class.  
|   Arguments:  None
|   Returns:    None 
|   Notes:      Any clean up needed should be put here.
\----------------------------------------------------------------------------*/
CDVGraph::~CDVGraph(void)
{
    if(m_pVideoWindow != NULL)
    {
        //Otherwise, a video image remains on the screen and the user cannot get rid of it. 
        m_pVideoWindow->put_Visible(OAFALSE);

        //Otherwise, messages are sent to the wrong window, likely causing errors.
        m_pVideoWindow->put_Owner(NULL);  
    }

    FreeFilters();
    CoUninitialize();
}

/*-----------------------------------------------------------------------------
|   Function:   CDVGraph::FreeFilters
|   Purpose:    Destructor for the graph info class.  
|   Arguments:  None
|   Returns:    None 
|   Notes:      Any clean up needed should be put here.
\----------------------------------------------------------------------------*/
void CDVGraph::FreeFilters()
{
    // Release the DirectShow interfaces created 
    SAFE_RELEASE(m_pGraph) 
    SAFE_RELEASE(m_pCaptureGraphBuilder) 
    SAFE_RELEASE(m_pMediaEvent);
    SAFE_RELEASE(m_pMediaControl) 
    SAFE_RELEASE(m_pInputFileFilter);
    SAFE_RELEASE(m_pDeviceFilter);
    SAFE_RELEASE(m_pIAMExtDevice);
    SAFE_RELEASE(m_pIAMExtTransport);
    SAFE_RELEASE(m_pIAMTCReader);
    SAFE_RELEASE(m_pVideoWindow);
    SAFE_RELEASE(m_pDroppedFrames);
}

/*-----------------------------------------------------------------------------
|   Function:   CAVCGraph::GraphInitialize
|   Purpose:    Initializing the graph class members.  
|   Arguments:  None
|   Returns:    Boolean TRUE if successfull or throws exception detailing the error
|   Notes:      Initializes the required DirectShow interfaces
\----------------------------------------------------------------------------*/
HRESULT CDVGraph::InitializeGraph()
{
    HRESULT hr = S_OK;
    Dump1( TEXT(" CDVGraph::InitializeGraph().hr = %x\0"), hr );

    // All DirectShow FilterGraphs need this
    hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC, 
                          IID_IGraphBuilder, (LPVOID *)&m_pGraph );
    CHECK_ERROR( TEXT(" Failed to create FilterGraph."), hr);

    // Helps the building all Graphs
    hr = CoCreateInstance((REFCLSID)CLSID_CaptureGraphBuilder2, NULL, CLSCTX_INPROC, 
                          (REFIID)IID_ICaptureGraphBuilder2, 
                          (void **)&m_pCaptureGraphBuilder);
    CHECK_ERROR( TEXT(" Failed to create CaptureGraphBuilder2."), hr);

    hr = m_pCaptureGraphBuilder->SetFiltergraph(m_pGraph);
    CHECK_ERROR( TEXT(" Failed to SetFiltergraph."), hr);

    hr = m_pGraph->QueryInterface(IID_IMediaEventEx, 
                                  reinterpret_cast<PVOID *>(&m_pMediaEvent)); 
    CHECK_ERROR( TEXT(" Failed to QI IMediaEventEx."), hr);

    // DirectShow Interface for Run, Stop, Pause the flow of the streams through the filter graph
    hr = m_pGraph->QueryInterface(IID_IMediaControl, (void **) &m_pMediaControl);
    CHECK_ERROR( TEXT(" Failed to QI IMediaControl."), hr);

    hr = m_pGraph->QueryInterface(IID_IVideoWindow, (void **) &m_pVideoWindow);
    CHECK_ERROR( TEXT(" Failed to  QI IVideoWindow."), hr);

    return hr;
}

/*-----------------------------------------------------------------------------
|   Function:   CDVGraph::BuildBasicGraph
|   Purpose:    Add device filter to the graph 
|   Arguments:  None 
|   Returns:    HRESULT
|   Notes:      
\----------------------------------------------------------------------------*/
HRESULT CDVGraph::BuildBasicGraph()
{
    HRESULT hr;

    hr = InitializeGraph();
    CHECK_ERROR( TEXT(" Failed to  initialize graph."), hr);

    hr = AddDeviceFilter();
    CHECK_ERROR( TEXT(" Failed to  add device filter."), hr);

    ASSERT(m_pDeviceFilter);

    hr = m_pDeviceFilter->QueryInterface(IID_IAMExtTransport, 
                                        (void **) &m_pIAMExtTransport);
    CHECK_ERROR( TEXT(" Failed to  QI IAMExtTransport."), hr);
  
    hr = m_pDeviceFilter->QueryInterface(IID_IAMExtDevice, (void **) &m_pIAMExtDevice);
    CHECK_ERROR( TEXT(" Failed to  QI IAMExtDevice."), hr);

    hr = m_pDeviceFilter->QueryInterface(IID_IAMTimecodeReader, (void **) &m_pIAMTCReader);
    CHECK_ERROR( TEXT(" Failed to  QI IAMTimecodeReader."), hr);

    hr= GetDVMode( &m_SubunitMode );
    CHECK_ERROR( TEXT(" GetMode Failed."), hr);

    return S_OK;
}


/*-----------------------------------------------------------------------------
|   Function:   CDVGraph::AddDeviceFilter
|   Purpose:    Load (not add) a filter of the name "Microsoft DV Camera and VCR" 
|               which connects to the specified filter
|   Arguments:  None 
|   Returns:    HRESULT
|   Notes:      This method adds the device filter to the filtergraph
\----------------------------------------------------------------------------*/
HRESULT CDVGraph::AddDeviceFilter()
{
    HRESULT hr = S_OK;
    ICreateDevEnum * pCreateDevEnum = NULL;
    IEnumMoniker *   pEnumMoniker = NULL;
    IMoniker *       pMoniker = NULL;
    ULONG            nFetched = 0;

    // Create Device Enumerator
    hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, 
                          IID_ICreateDevEnum, reinterpret_cast<PVOID *>(&pCreateDevEnum));
    CHECK_ERROR( TEXT(" Failed to create SystemDeviceEnum."), hr);
    
    // Create the enumerator of the monikers for the specified Device Class & reset them 
    hr = pCreateDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &pEnumMoniker, 0);
    if(SUCCEEDED(hr) && pEnumMoniker)
        pEnumMoniker->Reset();
    else   {
        Dump( TEXT(" Failed to CreateClassEnumerator.") );
        return hr;
    }


    // Loop through to the last moniker
    while(SUCCEEDED(pEnumMoniker->Next( 1, &pMoniker, &nFetched )) && pMoniker)    
    {

        // get the device friendly name:
        IPropertyBag *pPropBag;
        hr = pMoniker->BindToStorage( 0, 0, IID_IPropertyBag, (void **)&pPropBag );
        CHECK_ERROR( TEXT(" Failed to BindToStorage."), hr);

        //Friendly name
        VARIANT varFriendlyName;
        varFriendlyName.vt = VT_BSTR;
        hr = pPropBag->Read( L"FriendlyName", &varFriendlyName, 0 );
        CHECK_ERROR( TEXT(" Failed to read friendlyname."), hr);

#ifdef UNICODE      
        _tcscpy( m_DeviceName, varFriendlyName.bstrVal);
#else
        WideCharToMultiByte( CP_ACP, 0, varFriendlyName.bstrVal, -1, m_DeviceName, sizeof(m_DeviceName), 0, 0 );
#endif      

        VariantClear( &varFriendlyName );


        // detect DV device by search media type of its output pins for DV type
        IBaseFilter *pDeviceFilter;

        hr = pMoniker->BindToObject(0, 0, IID_IBaseFilter, (void**)&pDeviceFilter );
        CHECK_ERROR(TEXT("CAVCGraph::AddDeviceFilter():: BindToObject failed"), hr);
        if(pDeviceFilter== NULL)
            return E_FAIL;

        if(IsDeviceOutputDV(pDeviceFilter))
        {
            hr = pMoniker->BindToObject(0, 0, IID_IBaseFilter, (void**)&m_pDeviceFilter );
            CHECK_ERROR( TEXT(" Failed to BindToObject."), hr);

            hr = m_pGraph->AddFilter(m_pDeviceFilter, L"Filter");
            CHECK_ERROR( TEXT(" Failed to AddFilter."), hr);

            SAFE_RELEASE(pMoniker);
            SAFE_RELEASE(pDeviceFilter);
            break;
        }
        else
            hr = E_FAIL;
            
        SAFE_RELEASE(pMoniker);
        SAFE_RELEASE(pDeviceFilter);
    }//end of while

    SAFE_RELEASE( pEnumMoniker );
    SAFE_RELEASE( pCreateDevEnum );

    return hr; 
}

/*-------------------------------------------------------------------------
Routine:        CDVGraph::GetTapeInfo
Purpose:        Get Frame rate and availability of dvcr tape
Arguments:      None
Returns:        HRESULT as appropriate
Notes:          
------------------------------------------------------------------------*/
HRESULT CDVGraph::GetTapeInfo(void)
{
    HRESULT hr;
    LONG    lMediaType = 0;
    LONG    lInSignalMode = 0;

    // Query Media Type of the transport
    hr = m_pIAMExtTransport->GetStatus(ED_MEDIA_TYPE, &lMediaType);
    CHECK_ERROR( TEXT(" GetStatus failed."), hr);

    if (ED_MEDIA_NOT_PRESENT == lMediaType)
    {
        // Return failure if there is no tape installed
        hr = S_FALSE;  
    } 
    else
    {
        // Tape type should always be DVC
        ASSERT(ED_MEDIA_DVC == lMediaType);

        // Now lets query for the signal mode of the tape.
        hr = m_pIAMExtTransport->GetTransportBasicParameters(ED_TRANSBASIC_INPUT_SIGNAL, 
                                                             &lInSignalMode, NULL);
        CHECK_ERROR( TEXT(" GetTransportBasicParameters failed."), hr);

        // determine whether the camcorder supports ntsc or pal
        switch (lInSignalMode)
        {
            case ED_TRANSBASIC_SIGNAL_525_60_SD :
                m_AvgTimePerFrame = 33;  // 33 milliseconds (29.97 FPS)
                m_VideoFormat = DVENCODERVIDEOFORMAT_NTSC;
                break;

            case ED_TRANSBASIC_SIGNAL_525_60_SDL :
                m_AvgTimePerFrame = 33;  // 33 milliseconds (29.97 FPS)
                m_VideoFormat = DVENCODERVIDEOFORMAT_NTSC;
                break;

            case ED_TRANSBASIC_SIGNAL_625_50_SD :
                m_AvgTimePerFrame = 40;  // 40 milliseconds (25 FPS)
                m_VideoFormat = DVENCODERVIDEOFORMAT_PAL;
                break;

            case ED_TRANSBASIC_SIGNAL_625_50_SDL :
                m_AvgTimePerFrame = 40;  // 40 milliseconds (25 FPS)
                m_VideoFormat = DVENCODERVIDEOFORMAT_PAL;
                break;

            default : 
                Dump(TEXT("Unsupported or unrecognized tape format type."));
                m_AvgTimePerFrame = 33;  // 33 milli-sec (29.97 FPS); default
                break;
        }
        Dump1(TEXT("Avg time per frame is %d FPS\0"), m_AvgTimePerFrame);
    }
 
    return hr;
}

/*-------------------------------------------------------------------------
Routine:        CDVGraph::DV_GetDVMode
Purpose:        Determines camera mode using IAMExtDevice::GetCapability()
Arguments:      None
Returns:        Subunit mode of camera device
Notes:          
------------------------------------------------------------------------*/
HRESULT CDVGraph::GetDVMode(DV_MODE *pSubunitMode)
{
    HRESULT hr = S_OK;
    LONG    lDeviceType = 0;

    ASSERT(m_pDeviceFilter);
    ASSERT(pSubunitMode);

    if (!pSubunitMode)
        return E_POINTER;
    if (!m_pIAMExtDevice)
        return E_NOINTERFACE;

    //  Query the Device Type Capability
    hr = m_pIAMExtDevice->GetCapability(ED_DEVCAP_DEVICE_TYPE, &lDeviceType, 0);
    CHECK_ERROR( TEXT(" m_pIAMExtDevice->GetCapability() failed."), hr);
    
    switch (lDeviceType)
    {
        case 0 :
            //device type is unknown
            *pSubunitMode = UnknownMode;
            break;

        case ED_DEVTYPE_VCR :
            *pSubunitMode = VcrMode;
            break;

        case ED_DEVTYPE_CAMERA :
            *pSubunitMode = CameraMode;
            break;

        default :
            Dump(TEXT("GetCapability returned an unknown device type!"));
            break;
    } 
    
    return hr;
} 

/*-------------------------------------------------------------------------
Routine:        DV_SaveGraph
Purpose:        Save the filter graph into a *.grf file
Arguments:      FileName
Returns:        HRESULT as appropriate
Notes:          
------------------------------------------------------------------------*/
HRESULT CDVGraph::SaveGraphToFile(TCHAR* sGraphFile)
{
    IStorage *          pStorage = NULL;
    IStream *           pStream = NULL;
    IPersistStream *    pPersistStream = NULL;
    HRESULT             hr = S_OK;

    if(m_pCaptureGraphBuilder == NULL || sGraphFile == NULL)
        return E_FAIL;

    USES_CONVERSION;

    // Either Open or Create the *.GRF file
    hr = StgOpenStorage( T2W (sGraphFile), NULL, STGM_TRANSACTED | STGM_READWRITE | STGM_SHARE_DENY_WRITE, 
                         NULL, NULL, &pStorage );
    if ( STG_E_FILENOTFOUND == hr )
        hr = StgCreateDocfile( T2W (sGraphFile), STGM_CREATE | STGM_TRANSACTED | STGM_READWRITE | STGM_SHARE_EXCLUSIVE , 
                         NULL , &pStorage);
    CHECK_ERROR( TEXT(" StgCreateDocfile failed."), hr);
    
    hr = pStorage->CreateStream( L"ActiveMovieGraph", STGM_WRITE | STGM_CREATE | STGM_SHARE_EXCLUSIVE, 
                                 NULL, NULL, &pStream );
    CHECK_ERROR( TEXT(" CreateStream failed."), hr);

    // Persist the stream, save & commit to disk
    hr = m_pGraph->QueryInterface( IID_IPersistStream, (void **) &pPersistStream );
    CHECK_ERROR( TEXT(" QI IPersistStream failed."), hr);

    hr = pPersistStream->Save(pStream, TRUE);
    CHECK_ERROR( TEXT(" pPersistStream->Save() failed."), hr);

    hr = pStorage->Commit( STGC_DEFAULT );
    CHECK_ERROR( TEXT(" Save GRF file failed."), hr);

    SAFE_RELEASE(pStorage);
    SAFE_RELEASE(pStream);
    SAFE_RELEASE(pPersistStream);

    return hr;
}

/*---------------------------------------------------------------------------------------------------------
Routine:        CDVGraph::MakePreviewGraph()
Purpose:        Builds the DV preview graph
Arguments:      None
Returns:        HRESULT as apropriate
Notes:          This is a  preview graph for DV :           
                    DV_Cam(AV Out)->DVSplitter(vid)->DVCodec->VideoWindow
                                    DVSplitter(aud)->Default DirectSound device
---------------------------------------------------------------------------------------------------------*/
HRESULT CDVGraph::MakePreviewGraph()
{
    m_iGraphType = GRAPH_PREVIEW;

    HRESULT hr = m_pCaptureGraphBuilder->RenderStream(&PIN_CATEGORY_PREVIEW, 
                                                      &MEDIATYPE_Interleaved, 
                                                      m_pDeviceFilter,  
                                                      NULL, NULL);
    return hr;
}

/*---------------------------------------------------------------------------------------------------------
Routine:        CDVGraph::MakeDvToFileGraph_Type1
Purpose:        Builds and runs the DV to File graph
Arguments:      None
Returns:        HRESULT as apropriate
Notes:          This is a capture & preview graph for DV Type 1 AVI files           
                    This graph is a bit more complex.  It looks like this:
                    DV_Cam(AV Out)->SmartTee(capture)->AviMux->FileWriter
                                    SmartTee(preview)->DVSplitter(vid)->DVCodec->VideoWindow
                                                       DVSplitter(aud)->Default DirectSound device
---------------------------------------------------------------------------------------------------------*/
HRESULT CDVGraph::MakeDvToFileGraph_Type1(TCHAR* OutputFileName)
{
    m_iGraphType = GRAPH_DV_TO_FILE;
    HRESULT hr = S_OK;
    USES_CONVERSION;

    ASSERT(OutputFileName[0]);
    
    CComPtr<IBaseFilter>        ppf;
    CComPtr<IFileSinkFilter>    pSink;     
    hr = m_pCaptureGraphBuilder->SetOutputFileName(&MEDIASUBTYPE_Avi, T2W (OutputFileName), &ppf, &pSink);
    CHECK_ERROR( TEXT(" CDVGraph::MakeDvToFileGraph_Type1::m_pCaptureGraphBuilder->SetOutputFileName() failed."), hr);

    hr = SetAviOptions(ppf, INTERLEAVE_NONE);
    CHECK_ERROR( TEXT(" CDVGraph::MakeDvToFileGraph_Type1::m_pCaptureGraphBuilder->SetAviOptions() failed."), hr);
    
    // The graph we're making is:   MSDV --> Smart Tee --> AVI Mux --> File Writer
    //                                 --> DV Splitter --> DV Decoder --> Video Renderer
    //                                 --> Audio Renderer       
    // Connect interleaved stream of MSDV to the AVI Mux/FW
    hr = m_pCaptureGraphBuilder->RenderStream(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Interleaved, m_pDeviceFilter, NULL, ppf);
    CHECK_ERROR( TEXT(" CDVGraph::MakeDvToFileGraph_Type1::m_pCaptureGraphBuilder->RenderStream() failed."), hr);

    // Build a preview graph off of it too
    hr = m_pCaptureGraphBuilder->RenderStream(&PIN_CATEGORY_PREVIEW, &MEDIATYPE_Interleaved, m_pDeviceFilter, NULL, NULL);
    CHECK_ERROR( TEXT(" CDVGraph::MakeDvToFileGraph_Type1::m_pCaptureGraphBuilder->RenderStream() failed."), hr);
   
    return hr;
}

/*---------------------------------------------------------------------------------------------------------
Routine:        DV_MakeDvToFileGraph_NoPre
Purpose:        Builds and runs the DV to File graph with no preview
Arguments:      None
Returns:        HRESULT as apropriate
Notes:          This is a capture only graph for DV Type 1 AVI files            
                    This graph is not too complex.  It looks like this:
                    DV_Cam(AV Out)->AviMux->FileWriter
---------------------------------------------------------------------------------------------------------*/
HRESULT CDVGraph::MakeDvToFileGraph_NoPre_Type1(TCHAR* OutputFileName)
{
    m_iGraphType = GRAPH_DV_TO_FILE_NOPRE;
    HRESULT hr = S_OK;
    USES_CONVERSION;    
    ASSERT(OutputFileName[0]) ;
    
    //add the avimux, and file writer to the graph 
    CComPtr<IBaseFilter>        ppf = NULL;
    CComPtr<IFileSinkFilter>    pSink = NULL;

    hr = m_pCaptureGraphBuilder->SetOutputFileName(&MEDIASUBTYPE_Avi, T2W(OutputFileName), 
                                                   &ppf, &pSink);
    CHECK_ERROR( TEXT(" CDVGraph::MakeDvToFileGraph_NoPre_Type1::m_pCaptureGraphBuilder->SetOutputFileName() failed."), hr);

    hr = SetAviOptions(ppf, INTERLEAVE_NONE);
    CHECK_ERROR( TEXT(" CDVGraph::MakeDvToFileGraph_NoPre_Type1::m_pCaptureGraphBuilder->RenderStream() failed."), hr);

    // the graph we're making is:  MSDV --> AVI Mux --> File Writer
    // Set the AVI Options like interleaving mode etc...       
    // Build the graph
    hr = m_pCaptureGraphBuilder->RenderStream(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Interleaved, m_pDeviceFilter, 
                                              NULL, ppf);
    CHECK_ERROR( TEXT(" CDVGraph::MakeDvToFileGraph_NoPre_Type1::m_pCaptureGraphBuilder->RenderStream() failed."), hr);
         
    return hr;
}

/*---------------------------------------------------------------------------------------------------------
Routine:        DV_MakeFileToDvGraph
Purpose:        Builds and runs the File to DV graph
Arguments:      None
Returns:        HRESULT as apropriate
Notes:          This is a transmit & playback graph for DV Type 1 AVI files         
                This graph is a bit more complex.  It looks like this:
                    FileSource->AVI_Splitter->InfPinTee->DV_Camera
                                              InfPinTee->DVSplitter(vid)->DVDecoder->VideoWIndow
                                                         DVSplitter(aud)->Default DirectSound device
---------------------------------------------------------------------------------------------------------*/
HRESULT CDVGraph::MakeFileToDvGraph_Type1(TCHAR* InputFileName)
{
    m_iGraphType = GRAPH_FILE_TO_DV;
    HRESULT hr = S_OK;
    USES_CONVERSION;

    // Add the file as source filter to the graph
    hr = m_pGraph->AddSourceFilter(T2W (InputFileName ), T2W(InputFileName), &m_pInputFileFilter);
    CHECK_ERROR( TEXT(" CDVGraph::MakeFileToDvGraph_Type1::m_pGraph->AddSourceFilter failed."), hr);

    CComPtr<IBaseFilter> pAviSplitter = NULL;
    CComPtr<IBaseFilter> pInfTee      = NULL;

    hr = CoCreateInstance(CLSID_AviSplitter, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, reinterpret_cast<PVOID *>(&pAviSplitter));
    CHECK_ERROR( TEXT(" CDVGraph::MakeDvToFileGraph_NoPre_Type1::CoCreate AviSplitter  failed."), hr);

    hr = CoCreateInstance(CLSID_InfTee, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, reinterpret_cast<PVOID *>(&pInfTee));
    CHECK_ERROR( TEXT(" CDVGraph::MakeDvToFileGraph_NoPre_Type1::CoCreate InfTee failed."), hr);

    // the graph we're making is:   Async Reader --> AVI Splitter --> Tee --> MSDV
    //                                           --> DV Splitter --> DV Decoder --> VidRend
    //                                                                          --> AudRend
    // Add the AVI Splitter
    hr = m_pGraph->AddFilter(pAviSplitter, L"AVI Splitter");
    CHECK_ERROR( TEXT(" CDVGraph::MakeDvToFileGraph_NoPre_Type1::m_pGraph->AddFilter failed."), hr);

    // Connect file source to the splitter
    hr = m_pCaptureGraphBuilder->RenderStream(NULL, NULL, m_pInputFileFilter, NULL, pAviSplitter);
    CHECK_ERROR( TEXT(" CDVGraph::MakeDvToFileGraph_NoPre_Type1::m_pCaptureGraphBuilder->RenderStream failed."), hr);

    // Add an infinite Tee
    hr = m_pGraph->AddFilter(pInfTee, L"Infinite Tee"); 
    CHECK_ERROR( TEXT(" CDVGraph::MakeDvToFileGraph_NoPre_Type1::m_pGraph->AddFilter() failed."), hr);

    // Connect the Splitter to the Tee
    hr = m_pCaptureGraphBuilder->RenderStream(NULL, &MEDIATYPE_Interleaved, pAviSplitter, NULL, pInfTee);
    CHECK_ERROR( TEXT(" CDVGraph::MakeDvToFileGraph_NoPre_Type1::m_pCaptureGraphBuilder->RenderStream() failed."), hr);

    // Connect one branch of the tee to MSDV 
    hr = m_pCaptureGraphBuilder->RenderStream(NULL, NULL, pInfTee, NULL, m_pDeviceFilter);
    CHECK_ERROR( TEXT("CDVGraph::MakeDvToFileGraph_NoPre_Type1::m_pCaptureGraphBuilder->RenderStream() failed."), hr);

    // Build a preview graph off the other branch
    hr = m_pCaptureGraphBuilder->RenderStream(NULL, NULL, pInfTee, NULL, NULL);
    CHECK_ERROR( TEXT("CDVGraph::MakeDvToFileGraph_NoPre_Type1::m_pCaptureGraphBuilder->RenderStream() failed."), hr);

    return hr;
}

/*---------------------------------------------------------------------------------------------------------
Routine:        DV_MakeFileToDvGraph_NoPre
Purpose:        Builds and runs the File to DV graph without preview
Arguments:      None
Returns:        HRESULT as apropriate
Notes:          This is a transmit only graph for DV Type 1 AVI files           
                    This graph is a bit simplex.  It looks like this:
                    FileSource->AVI_Splitter->DV_Camera
---------------------------------------------------------------------------------------------------------*/
HRESULT CDVGraph::MakeFileToDvGraph_NoPre_Type1(TCHAR* InputFileName)
{
    m_iGraphType = GRAPH_FILE_TO_DV_NOPRE;
    HRESULT hr = S_OK;
    USES_CONVERSION;
    ASSERT(InputFileName[0]) ;

    // Add the file as source filter to the graph
    hr = m_pGraph->AddSourceFilter(T2W(InputFileName), T2W(InputFileName), &m_pInputFileFilter);
    CHECK_ERROR( TEXT("CDVGraph::MakeFileToDVGraph_NoPre_Type1::m_pGraph->AddSourceFilter() failed."), hr);

    // the graph we're making is:    Async Reader --> AVI Splitter --> MSDV 
    hr = m_pCaptureGraphBuilder->RenderStream(NULL, NULL, m_pInputFileFilter, NULL, m_pDeviceFilter);
    CHECK_ERROR( TEXT("CDVGraph::MakeFileToDVGraph_NoPre_Type1::m_pCaptureGraphBuilder->RenderStream() failed."), hr);
              
    return hr;
}

/*---------------------------------------------------------------------------------------------------------
Routine:        DV_MakeDvToFileGraph_Type2
Purpose:        Builds and runs the DV to File graph
Arguments:      None
Returns:        HRESULT as apropriate
Notes:          This is a capture & preview graph for DV Type 2 AVI files           
                    This graph is a bit more complex.  It looks like this:
                    DV_Cam(AV Out)->DVSplitter(vid)->SmartTee(capture)->AviMux->FileWriter
                                                     SmartTee(preview)->DVCodec->VideoWindow
                                    DVSplitter(aud)->InfinitePinTee->AviMux->FileWriter
                                                     InfinitePinTee->Default DirectSound device
---------------------------------------------------------------------------------------------------------*/
HRESULT CDVGraph::MakeDvToFileGraph_Type2(TCHAR* OutputFileName)
{
    m_iGraphType = GRAPH_DV_TO_FILE_TYPE2;
    HRESULT hr = S_OK;
    USES_CONVERSION;
    // making sure there is a output file selected
    ASSERT(OutputFileName[0]);

    CComPtr<IBaseFilter>    pDVSplitter          = NULL;
    hr = CoCreateInstance(CLSID_DVSplitter, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, reinterpret_cast<PVOID *>(&pDVSplitter));
    CHECK_ERROR( TEXT("CDVGraph::MakeDVToFileGraph_Type2::create DVSplitter failed."), hr);

    hr =  m_pGraph->AddFilter(pDVSplitter, L"DV Splitter");
    CHECK_ERROR( TEXT("CDVGraph::MakeDVToFileGraph_Type2::m_pGraph->AddFilter() failed."), hr);


    //add the avimux, and file writer to the graph 
    CComPtr<IBaseFilter> ppf = NULL;
    CComPtr<IFileSinkFilter> pSink = NULL;
    hr = m_pCaptureGraphBuilder->SetOutputFileName(&MEDIASUBTYPE_Avi, T2W (OutputFileName), &ppf, &pSink);
    CHECK_ERROR( TEXT("CDVGraph::MakeDVToFileGraph_Type2::m_pCaptureGraphBuilder->SetOutputFileName() failed."), hr);

    // Set the AVI Options like interleaving mode etc...
    hr = SetAviOptions(ppf, INTERLEAVE_NONE);
    CHECK_ERROR( TEXT("CDVGraph::MakeDVToFileGraph_Type2::SetAviOptions() failed."), hr);
             
    // the graph we're making is:   MSDV --> Smart Tee --> DV SPLITTER --> AVI MUX --> File Writer
    //                                                 --> DV SPLITTER --> DV DEC  --> Video Renderer
    //                                                                             --> Audio Renderer 

    // Connect MSDV Interleave stream through DV Splitter to AVI MUX/FW
    hr = m_pCaptureGraphBuilder->RenderStream(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Interleaved, m_pDeviceFilter, pDVSplitter, ppf);
    CHECK_ERROR( TEXT("CDVGraph::MakeDVToFileGraph_Type2::m_pCaptureGraphBuilder->RenderStream() failed."), hr);

    // Connect the other DV Splitter output to the AVI MUX/FW
    hr = m_pCaptureGraphBuilder->RenderStream(NULL, NULL, pDVSplitter, NULL, ppf);
    CHECK_ERROR( TEXT("CDVGraph::MakeDVToFileGraph_Type2::m_pCaptureGraphBuilder->RenderStream() failed."), hr);

    // Render the preview part of the graph
    hr = m_pCaptureGraphBuilder->RenderStream(&PIN_CATEGORY_PREVIEW, &MEDIATYPE_Interleaved, m_pDeviceFilter, NULL, NULL);
    CHECK_ERROR( TEXT("CDVGraph::MakeDVToFileGraph_Type2::m_pCaptureGraphBuilder->RenderStream() failed."), hr);

    return hr;
}

/*---------------------------------------------------------------------------------------------------------
Routine:        DV_MakeDvToFileGraph_NoPre_Type2
Purpose:        Builds and runs the DV to File graph
Arguments:      None
Returns:        HRESULT as apropriate
Notes:          This is a capture only graph for DV Type 2 AVI files            
                    This graph is a bit simplex .  It looks like this:
                    DV_Cam(AV Out)->DVSplitter(vid)->AviMux->FileWriter
                                                DVSplitter(aud)->AviMux->FileWriter
---------------------------------------------------------------------------------------------------------*/
HRESULT CDVGraph::MakeDvToFileGraph_NoPre_Type2(TCHAR* OutputFileName)
{
    m_iGraphType = GRAPH_DV_TO_FILE_NOPRE_TYPE2;
    HRESULT hr = S_OK;
    USES_CONVERSION;
    // making sure there is a output file selected
    ASSERT(OutputFileName[0]);

    CComPtr<IBaseFilter> pDVSplitter = NULL;

    hr = CoCreateInstance(CLSID_DVSplitter, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, reinterpret_cast<PVOID *>(&pDVSplitter));
    CHECK_ERROR( TEXT("CDVGraph::MakeDvToFileGraph_NoPre_Type2::create DVSplitter failed."), hr);
   
    hr = m_pGraph->AddFilter(pDVSplitter, L"DV Splitter");
    CHECK_ERROR( TEXT("CDVGraph::MakeDvToFileGraph_NoPre_Type2::m_pGraph->AddFilter failed."), hr);

    //add the avimux, and file writer to the graph 
    CComPtr<IBaseFilter>        ppf = NULL;
    CComPtr<IFileSinkFilter>    pSink = NULL;

    hr = m_pCaptureGraphBuilder->SetOutputFileName(&MEDIASUBTYPE_Avi, T2W (OutputFileName), &ppf, &pSink);
    CHECK_ERROR( TEXT("CDVGraph::MakeDvToFileGraph_NoPre_Type2::m_pCaptureGraphBuilder->SetOutputFileName failed."), hr);
              
    // the graph we're making is:   MSDV --> Smart Tee --> DV SPLITTER --> AVI MUX --> File Writer
    //
    // connect MSDV interleaved pin through DV Splitter to AVI MUX/FW
    hr = m_pCaptureGraphBuilder->RenderStream(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Interleaved, m_pDeviceFilter, pDVSplitter, ppf);
    CHECK_ERROR( TEXT("CDVGraph::MakeDvToFileGraph_NoPre_Type2::m_pCaptureGraphBuilder->RenderStream failed."), hr);

    // connect the other DV Splitter output to the AVI MUX/FW
    hr = m_pCaptureGraphBuilder->RenderStream(NULL, NULL, pDVSplitter, NULL, ppf);
    CHECK_ERROR( TEXT("CDVGraph::MakeDvToFileGraph_NoPre_Type2::m_pCaptureGraphBuilder->RenderStream failed."), hr);
                      
    // Set the AVI Options like interleaving mode etc...
    hr = SetAviOptions(ppf, INTERLEAVE_NONE);
    CHECK_ERROR( TEXT("CDVGraph::MakeDvToFileGraph_NoPre_Type2::SetAviOptions failed."), hr);

    return hr;
}

/*---------------------------------------------------------------------------------------------------------
Routine:        DV_MakeFileToDvGraph_Type2
Purpose:        Builds and runs the File to DV graph 
Arguments:      None
Returns:        HRESULT as apropriate
Notes:          This is a transmit & playback graph for DV Type 2 AVI files         
                    This graph is a bit complex.  It looks like this:
                     FileSource->AVI_Splitter(vid) ->DVMuxer(vid)---------->InfPinTee->DV_Camera
                                        AVI_Splitter(aud)->DVMuxer(aud)     InfPinTee->DVSplitter(vid)->DVDecoder->VideoWIndow
                                                                                       DVSplitter(aud)->DSoundDevice
---------------------------------------------------------------------------------------------------------*/
HRESULT CDVGraph::MakeFileToDvGraph_Type2(TCHAR* InputFileName)
{
    m_iGraphType = GRAPH_FILE_TO_DV_TYPE2;
    HRESULT hr = S_OK;
    USES_CONVERSION;
    ASSERT(InputFileName[0] );

    CComPtr<IBaseFilter>  pDVMux   = NULL;
    CComPtr<IBaseFilter>  pInfTee  = NULL;
    hr = CoCreateInstance(CLSID_DVMux, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, reinterpret_cast<PVOID *>(&pDVMux));
    CHECK_ERROR( TEXT("CDVGraph::MakeFileToDvGraph_Type2::create DVMux failed."), hr);

    hr = CoCreateInstance(CLSID_InfTee, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, reinterpret_cast<PVOID *>(&pInfTee));
    CHECK_ERROR( TEXT("CDVGraph::MakeFileToDvGraph_Type2::CoCreate InfTeefailed."), hr); 

    // Add the file as source filter to the graph
    hr =m_pGraph->AddSourceFilter(T2W (InputFileName), T2W (InputFileName), &m_pInputFileFilter);
    CHECK_ERROR( TEXT("CDVGraph::MakeFileToDvGraph_Type2::m_pGraph->AddSourceFilter failed."), hr);

    // Add the DVMuxer to the graph
    hr = m_pGraph->AddFilter(pDVMux, L"DV Muxer");
    CHECK_ERROR( TEXT("CDVGraph::MakeFileToDvGraph_Type2::m_pGraph->AddFilter failed."), hr);

    //Add the infinite pin tee filter to the graph and connect it downstream to the dv muxer
    hr = m_pGraph->AddFilter(pInfTee, L"Infinite Tee"); 
    CHECK_ERROR( TEXT("CDVGraph::MakeFileToDvGraph_Type2::m_pGraph->AddFilter failed."), hr);

    // the graph we need to build is:    ASYNC reader --> AVI SPLITTER --> DV MUX --> TEE --> MSDV
    //                                                --> DVSP --> DV DEC --> VR
    //                                                --> AR
    // connect file source video stream to DV MUX
    hr = m_pCaptureGraphBuilder->RenderStream(NULL, NULL, m_pInputFileFilter, NULL, pDVMux);
    CHECK_ERROR( TEXT("CDVGraph::MakeFileToDvGraph_Type2::m_pCaptureGraphBuilder->RenderStream failed."), hr);

    // connect file source audio stream to DV MUX
    hr = m_pCaptureGraphBuilder->RenderStream(NULL, NULL, m_pInputFileFilter, NULL, pDVMux);
    CHECK_ERROR( TEXT("CDVGraph::MakeFileToDvGraph_Type2::m_pCaptureGraphBuilder->RenderStream failed."), hr);

    // connect DV MUX to tee
    hr = m_pCaptureGraphBuilder->RenderStream(NULL, NULL, pDVMux, NULL, pInfTee);
    CHECK_ERROR( TEXT("CDVGraph::MakeFileToDvGraph_Type2::m_pCaptureGraphBuilder->RenderStream failed."), hr);

    // connect one branch of tee to MSDV transmit
    hr = m_pCaptureGraphBuilder->RenderStream(NULL, NULL, pInfTee, NULL, m_pDeviceFilter);
    CHECK_ERROR( TEXT("CDVGraph::MakeFileToDvGraph_Type2::m_pCaptureGraphBuilder->RenderStream failed."), hr);

    // render other branch of tee to audio & video preview
    CComPtr<IPin> pOut;
    hr = m_pCaptureGraphBuilder->FindPin(pInfTee, PINDIR_OUTPUT, NULL, NULL, TRUE, 0, &pOut);
    CHECK_ERROR( TEXT("CDVGraph::MakeFileToDvGraph_Type2::m_pCaptureGraphBuilder->FindPin failed."), hr);

    hr = m_pGraph->Render(pOut);
    CHECK_ERROR( TEXT("CDVGraph::MakeFileToDvGraph_Type2::m_pGraph->Render failed."), hr);

    return hr;
}

/*---------------------------------------------------------------------------------------------------------
Routine:        DV_MakeFileToDvGraph_NoPre_Type2
Purpose:        Builds and runs the File to DV graph 
Arguments:      None
Returns:        HRESULT as apropriate
Notes:          This is a transmit only graph for DV Type 2 AVI files           
                    This graph looks like this:
                     FileSource->AVI_Splitter(vid) ->DVMuxer(vid)----->DV_Camera
                                        AVI_Splitter(aud)->DVMuxer(aud)                 
---------------------------------------------------------------------------------------------------------*/
HRESULT CDVGraph::MakeFileToDvGraph_NoPre_Type2(TCHAR* InputFileName)
{
    m_iGraphType = GRAPH_FILE_TO_DV_NOPRE_TYPE2;
    HRESULT hr = S_OK;
    USES_CONVERSION;
    ASSERT(InputFileName[0]);

    CComPtr< IBaseFilter > pDVMux = NULL;
    hr = CoCreateInstance(CLSID_DVMux, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, reinterpret_cast<PVOID *>(&pDVMux));
    CHECK_ERROR( TEXT("CDVGraph::MakeFileToDvGraph_NoPre_Type2::CoCreate DVMux failed."), hr);
  
    // Add the file as source filter to the graph
    hr = m_pGraph->AddSourceFilter(T2W (InputFileName), T2W (InputFileName), &m_pInputFileFilter);
    CHECK_ERROR( TEXT("CDVGraph::MakeFileToDvGraph_NoPre_Type2::m_pGraph->AddSourceFilter failed."), hr);

    // Add the DVMuxer to the graph
    hr = m_pGraph->AddFilter(pDVMux, L"DV Muxer");
    CHECK_ERROR( TEXT("CDVGraph::MakeFileToDvGraph_NoPre_Type2::add DVMux failed."), hr);

    // the graph we need to build is:   ASYNC reader --> AVI SPLITTER --> DV MUX --> MSDV
    //
    // connect file video stream to DV MUX
    hr = m_pCaptureGraphBuilder->RenderStream(NULL, NULL, m_pInputFileFilter, NULL, pDVMux);
    CHECK_ERROR( TEXT("CDVGraph::MakeFileToDvGraph_NoPre_Type2::m_pCaptureGraphBuilder->RenderStream failed."), hr);

    // connect file audio stream to DV MUX
    hr = m_pCaptureGraphBuilder->RenderStream(NULL, NULL, m_pInputFileFilter, NULL, pDVMux);
    CHECK_ERROR( TEXT("CDVGraph::MakeFileToDvGraph_NoPre_Type2::m_pCaptureGraphBuilder->RenderStream failed."), hr);

    // connect DV MUX to DV Transmit
    hr = m_pCaptureGraphBuilder->RenderStream(NULL, NULL, pDVMux, NULL, m_pDeviceFilter);
    CHECK_ERROR( TEXT("CDVGraph::MakeFileToDvGraph_NoPre_Type2::m_pCaptureGraphBuilder->RenderStream failed."), hr);

    return hr;
}

/*-------------------------------------------------------------------------
Routine:        DV_SetAviOptions
Purpose:        Routine for changing AVI Mux properties.  In this sample, 
                we just set a few options.  
                These options could be set through the Avi Mux property sheet, 
                or through a separate dialog.
Arguments:      Pointer to the AVI renderer (from SetOutputFileName())
Returns:        HRESULT as appropriate
Notes:          
------------------------------------------------------------------------*/
HRESULT CDVGraph::SetAviOptions(IBaseFilter *ppf, InterleavingMode INTERLEAVE_MODE)
{
    HRESULT hr;
    CComPtr<IConfigAviMux>        pMux           = NULL;
    CComPtr<IConfigInterleaving>  pInterleaving  = NULL;

    ASSERT(ppf);
    if (!ppf)
        return E_POINTER;

    // QI for interface AVI Muxer
    hr = ppf->QueryInterface(IID_IConfigAviMux, reinterpret_cast<PVOID *>(&pMux));
    CHECK_ERROR( TEXT("CDVGraph::SetAviOptions::QI IConfigAviMux failed."), hr);

    hr = pMux->SetOutputCompatibilityIndex(TRUE);
    CHECK_ERROR( TEXT("CDVGraph::SetAviOptions::pMux->SetOutputCompatibilityIndex failed."), hr);

    // QI for interface Interleaving
    hr = ppf->QueryInterface(IID_IConfigInterleaving, reinterpret_cast<PVOID *>(&pInterleaving));
    CHECK_ERROR( TEXT("CDVGraph::SetAviOptions::QI IConfigInterleaving failed."), hr);

    // put the interleaving mode (full, none, half)
    hr = pInterleaving->put_Mode(INTERLEAVE_MODE);
    CHECK_ERROR( TEXT("CDVGraph::SetAviOptions::pInterleaving->put_Mode failed."), hr);
   
    return hr;
} 

/*-----------------------------------------------------------------------------
|   Function:   CAVCGraph::NukeFilters
|   Purpose:    Tears downs the graph from the specified filter onwards.  
|   Arguments:  Filter to be nuked downstream from
|   Returns:    None 
|   Notes:      The filter specified in the argument is not removed from the filtergraph; This is a recursively calling function
\----------------------------------------------------------------------------*/
HRESULT CDVGraph::NukeFilters(IBaseFilter *pFilter, BOOL bNukeDownStream)
{
    USES_CONVERSION;
    HRESULT hr = S_OK;
    IPin *pPin = NULL, *pToPin = NULL;
    IEnumPins *pEnumPins = NULL;

    ULONG uFetched = 0;
    PIN_INFO PinInfo;

    ASSERT(m_pGraph);
    
    // Validating the the pointer to the Filter is not null
    if(!pFilter)
    {
        Dump( TEXT("CAVCGraph::NukeFilters():: Invalid Argument:: Invalid filter to nuke from") );
        return E_FAIL;
    }

    // enumerate all the pins on this filter
    // reset the enumerator to the first pin
    hr = pFilter->EnumPins(&pEnumPins);
    CHECK_ERROR( TEXT("CAVCGraph::NukeFilters():: Could not enumerate pins on the filter to be nuked.hr=%#x"), hr);
    pEnumPins->Reset(); 
   
    // Loop through all the pins of the filter
    while( SUCCEEDED(pEnumPins->Next(1, &pPin, &uFetched)) && pPin )
    {
        // Get the pin & its pin_info struct that this filter's pin is connected to 
        hr = pPin->ConnectedTo(&pToPin);
        if(SUCCEEDED(hr) &&pToPin )
        {
            hr = pToPin->QueryPinInfo(&PinInfo);
            CHECK_ERROR( TEXT("pToPin->QueryPinInfo failed.hr=%#x"), hr);
                   
            // Check that this ConnectedTo Pin is a input pin thus validating that our filter's pin is an output pin
            if(PinInfo.dir == PINDIR_INPUT && bNukeDownStream)
            {
                // thus we have a pin on the downstream filter so nuke everything downstream of that filter recursively
                NukeFilters(PinInfo.pFilter, bNukeDownStream);
                // Disconnect the two pins and remove the downstream filter
                m_pGraph->Disconnect(pToPin);
                m_pGraph->Disconnect(pPin);

                //always leave the Camera filter in the graph
                if (PinInfo.pFilter != m_pDeviceFilter)
                {           
                    FILTER_INFO FilterInfo;
                    TCHAR szName[256];
                    hr = PinInfo.pFilter ->QueryFilterInfo(&FilterInfo);
                    if (SUCCEEDED(hr))
                    {
#ifdef UNICODE
                        lstrcpyn(szName, FilterInfo.achName, 256);
#else
                        WideCharToMultiByte(CP_ACP, 0, FilterInfo.achName, -1, szName, 256, 0, 0);
#endif
                        FilterInfo.pGraph->Release();
                    }
                    szName[255] = 0;        // Null-terminate

                    hr = m_pGraph->RemoveFilter(PinInfo.pFilter);
                }
            }

            SAFE_RELEASE(PinInfo.pFilter);                 
            SAFE_RELEASE(pToPin);
        }
        SAFE_RELEASE(pPin);       
    }

    SAFE_RELEASE(pEnumPins);
    return S_OK;
}

/*-------------------------------------------------------------------------
Routine:        DV_StartGraph
Purpose:        Starts the Filter Graph 
Arguments:      None
Returns:        HResult as appropriate
Notes:          
------------------------------------------------------------------------*/
HRESULT CDVGraph::StartGraph(void)
{
    HRESULT hr;

    // start the graph
    hr = m_pMediaControl->Run();
    if ( FAILED(hr))
    {
        Dump(TEXT("CDVGraph::StartGraph::m_pMediaControl->Run() Failed!"));
        // stop parts that ran
        m_pMediaControl->Stop();
    }

    return hr;
}

/*-------------------------------------------------------------------------
Routine:        DV_PauseGraph
Purpose:        Starts the Filter Graph 
Arguments:      None
Returns:        HResult as appropriate
Notes:          
------------------------------------------------------------------------*/
HRESULT CDVGraph::PauseGraph(void)
{
    HRESULT hr;

    // Pause the graph
    hr = m_pMediaControl->Pause();
    if ( FAILED(hr))
    {
        Dump(TEXT("CDVGraph::StartGraph::m_pMediaControl->Pause() Failed!"));
         // stop parts that ran
        m_pMediaControl->Stop();
    }

    return hr;
}


/*-------------------------------------------------------------------------
Routine:        DV_StopGraph
Purpose:        Starts the Filter Graph 
Arguments:      None
Returns:        HResult as appropriate
Notes:          
------------------------------------------------------------------------*/
HRESULT CDVGraph::StopGraph(void)
{
    HRESULT hr = m_pMediaControl->Stop();
    return hr;
}

/*-------------------------------------------------------------------------
Routine:        CDVGraph::getDroppedFrameNum
Purpose:        Callback proc to display dropped frame info
Arguments:      [in]DWORD is the graph in transmit mode
Arguments:      [out]long* number of dropped frames
Arguments:      [out]long* number of not dropped frames
Returns:        None
Notes:          For both Capture & Transmit graphs
------------------------------------------------------------------------*/
HRESULT CDVGraph::getDroppedFrameNum( BOOL *bIsModeTransmit, long* pDropped, long* pNotdropped)
{
    HRESULT hr;
    CComPtr<IPin> pAVIn = NULL;

    SAFE_RELEASE(m_pDroppedFrames);
    ASSERT(bIsModeTransmit != NULL);
    
    if (!bIsModeTransmit)
        return E_POINTER;

    // capture
    if (GRAPH_DV_TO_FILE == m_iGraphType || GRAPH_DV_TO_FILE_NOPRE == m_iGraphType ||
        GRAPH_DV_TO_FILE_TYPE2 == m_iGraphType || GRAPH_DV_TO_FILE_NOPRE_TYPE2 == m_iGraphType)
        *bIsModeTransmit = FALSE;

    // transmit
    else if (GRAPH_FILE_TO_DV == m_iGraphType || GRAPH_FILE_TO_DV_NOPRE == m_iGraphType ||
        GRAPH_FILE_TO_DV_TYPE2 == m_iGraphType || GRAPH_FILE_TO_DV_NOPRE_TYPE2 == m_iGraphType)
        *bIsModeTransmit = TRUE;

    if( *bIsModeTransmit)
    {
        hr = m_pCaptureGraphBuilder->FindPin(m_pDeviceFilter, PINDIR_INPUT, NULL, NULL, FALSE, 0, &pAVIn);
        if(FAILED(hr) || pAVIn == NULL)
        {
            Dump(TEXT("CDVGraph::getDroppedFrameNum::m_pCaptureGraphBuilder->FindPin Failed!"));
            return hr;
        }
        
        hr = pAVIn->QueryInterface(IID_IAMDroppedFrames, reinterpret_cast<PVOID *>(&m_pDroppedFrames));
        CHECK_ERROR( TEXT("CDVGraph::getDroppedFrameNum::QI IAMDroppedFrames failed."), hr);        
    }
    else
    {
        hr = m_pCaptureGraphBuilder->FindInterface(&PIN_CATEGORY_CAPTURE, 
                                                   &MEDIATYPE_Interleaved, 
                                                   m_pDeviceFilter, IID_IAMDroppedFrames, 
                                                   reinterpret_cast<PVOID *>(&m_pDroppedFrames));
        CHECK_ERROR( TEXT("CDVGraph::getDroppedFrameNum::m_pCaptureGraphBuilder->FindInterface failed."), hr);        
    }
     
    hr = m_pDroppedFrames->GetNumDropped(pDropped);
    CHECK_ERROR( TEXT("CDVGraph::getDroppedFrameNum::m_pDroppedFrames->GetNumDropped failed."), hr);        

    hr = m_pDroppedFrames->GetNumNotDropped(pNotdropped);
    CHECK_ERROR( TEXT("CDVGraph::getDroppedFrameNum::m_pDroppedFrames->GetNumDropped failed."), hr);        

    return hr;
}

/*-------------------------------------------------------------------------
Routine:        ChangeFrameRate
Purpose:        Controls discard or not half of the frames in the video stream
Arguments:      None
Returns:        HRESULT
Notes:          For NTSC, the frame rate is reduced from 30 frames per second (fps) to 15 fps. 
                For PAL, the frame rate is reduced from 25 fps to 12.5 fps.
------------------------------------------------------------------------*/
HRESULT CDVGraph::ChangeFrameRate(BOOL bHalfFrameRate)
{
    HRESULT hr = S_OK;
    CComPtr<IBaseFilter>    pDVSplitter;
    CComPtr<IDVSplitter>    pIDVSplitter = NULL;

    /* Obtain the dv docoder's IBaseFilter interface. */
    hr = m_pGraph->FindFilterByName(L"DV Splitter", &pDVSplitter) ;
    CHECK_ERROR( TEXT("CDVGraph::ChangeFrameRate()::m_pGraph->FindFilterByName failed."), hr);        

    hr = pDVSplitter->QueryInterface(IID_IDVSplitter, reinterpret_cast<PVOID *>(&pIDVSplitter));
    CHECK_ERROR( TEXT("CDVGraph::ChangeFrameRate()::QI IDVSplitter failed."), hr);        
    
    if(bHalfFrameRate)
        hr = pIDVSplitter->DiscardAlternateVideoFrames(1);  // if the value is non-zero, discards alternate frames. 
    else 
        hr = pIDVSplitter->DiscardAlternateVideoFrames(0);  // If the value is zero, the filter delivers every frame.
    CHECK_ERROR( TEXT("CDVGraph::ChangeFrameRate()::pIDVSplitter->DiscardAlternateVideoFrames failed."), hr);        
    
    return hr;
}

/*-------------------------------------------------------------------------------
Routine:        CDVGraph::GetResolutionFromDVDecoderPropertyPage
Purpose:        controls discard or not half of the frames in the video stream
Arguments:      None
Returns:        HRESULT
Notes:          For NTSC, the frame rate is reduced from 30 frames per second (fps) to 15 fps. 
                For PAL, the frame rate is reduced from 25 fps to 12.5 fps.
---------------------------------------------------------------------------------*/
HRESULT CDVGraph::GetResolutionFromDVDecoderPropertyPage( HWND hwndApp, BOOL bChangeResolution)
{
    HRESULT hr;
    IBaseFilter *pDVDecoder;
    IIPDVDec    *pIPDVDec;

    /* Obtain the dv docoder's IBaseFilter interface. */
    hr = m_pGraph->FindFilterByName(L"DV Video Decoder", &pDVDecoder) ;
    CHECK_ERROR( TEXT("CDVGraph::GetResolutionFromDVDecoderPropertyPage()::m_pGraph->FindFilterByName failed."), hr);        

    CComPtr<ISpecifyPropertyPages> pProp;
    hr = pDVDecoder->QueryInterface(IID_ISpecifyPropertyPages, (void **)&pProp);
    CHECK_ERROR( TEXT("CDVGraph::GetResolutionFromDVDecoderPropertyPage()::QI ISpecifyPropertyPages failed."), hr);        
    
    // Get the filter's name and IUnknown pointer.
    FILTER_INFO FilterInfo;
    hr = pDVDecoder->QueryFilterInfo(&FilterInfo); 
    CHECK_ERROR( TEXT("CDVGraph::GetResolutionFromDVDecoderPropertyPage()::pDVDecoder->QueryFilterInfo failed."), hr);        

    // Show the page. 
    if(bChangeResolution)
    {
        CAUUID caGUID;
        pProp->GetPages(&caGUID);

        OleCreatePropertyFrame(
            hwndApp,                // Parent window
            0, 0,                   // (Reserved)
            FilterInfo.achName,     // Caption for the dialog box
            1,                      // Number of objects (just the filter)
            (IUnknown **)&pDVDecoder,            // Array of object pointers. 
            caGUID.cElems,          // Number of property pages
            caGUID.pElems,          // Array of property page CLSIDs
            0,                      // Locale identifier
            0, NULL                 // Reserved
        );
        CoTaskMemFree(caGUID.pElems);
    }

    // Clean up.
    FilterInfo.pGraph->Release(); 
   
    hr = pDVDecoder->QueryInterface(IID_IIPDVDec, reinterpret_cast<PVOID *>(&pIPDVDec));
    CHECK_ERROR( TEXT("CDVGraph::GetResolutionFromDVDecoderPropertyPage()::QI IIPDVDec failed."), hr);        

    hr = pIPDVDec->get_IPDisplay(reinterpret_cast <int *>(&m_DVResolution));
    CHECK_ERROR( TEXT("CDVGraph::GetResolutionFromDVDecoderPropertyPage()::pIPDVDec->get_IPDisplay failed."), hr);        
  
    SAFE_RELEASE( pDVDecoder );
    SAFE_RELEASE( pIPDVDec );

    return hr;
}

/*-------------------------------------------------------------------------
Routine:        CDVGraph::GetResolutionFromDVDecoderPropertyPage
Purpose:        controls discard or not half of the frames in the video stream
Arguments:      None
Returns:        HRESULT
Notes:          For NTSC, the frame rate is reduced from 30 frames per second (fps) to 15 fps. 
                For PAL, the frame rate is reduced from 25 fps to 12.5 fps.
------------------------------------------------------------------------*/
HRESULT CDVGraph::GetVideoWindowDimensions (int* pWidth, int *pHeight, BOOL bChangeResolution,HWND hwndApp)
{
    HRESULT hr;

    ASSERT(pWidth != NULL);
    ASSERT(pHeight != NULL);
    if (!pWidth || !pHeight)
        return E_POINTER;

    hr = GetResolutionFromDVDecoderPropertyPage( hwndApp, bChangeResolution );
    CHECK_ERROR( TEXT("CDVGraph::GetVideoWindowDimensions()::GetResolutionFromDVDecoderPropertyPage() failed."), hr); 

    switch (m_DVResolution)
    {
        case DVRESOLUTION_FULL:
            *pWidth = DVENCODER_WIDTH;
            if (DVENCODERVIDEOFORMAT_PAL == m_VideoFormat)
                *pHeight = PAL_DVENCODER_HEIGHT;
            else if (DVENCODERVIDEOFORMAT_NTSC == m_VideoFormat)
                *pHeight = NTSC_DVENCODER_HEIGHT;
            break;

        case DVRESOLUTION_HALF:
            *pWidth = DVENCODER_WIDTH/2;
            if (DVENCODERVIDEOFORMAT_PAL == m_VideoFormat)
                *pHeight = PAL_DVENCODER_HEIGHT/2;
            else if (DVENCODERVIDEOFORMAT_NTSC == m_VideoFormat)
                *pHeight = NTSC_DVENCODER_HEIGHT/2;
            break;

        case DVRESOLUTION_QUARTER:
            *pWidth = DVENCODER_WIDTH/4;
            if (DVENCODERVIDEOFORMAT_PAL == m_VideoFormat)
                *pHeight = PAL_DVENCODER_HEIGHT/4;
            else if (DVENCODERVIDEOFORMAT_NTSC == m_VideoFormat)
                *pHeight = NTSC_DVENCODER_HEIGHT/4;
            break;

        case DVRESOLUTION_DC:
            *pWidth = 88;
            if (DVENCODERVIDEOFORMAT_PAL == m_VideoFormat)
               *pHeight = PAL_DVENCODER_HEIGHT/8;
            else if (DVENCODERVIDEOFORMAT_NTSC == m_VideoFormat)
               *pHeight = NTSC_DVENCODER_HEIGHT/8;
            break;
    }

    return hr;
}

/*-------------------------------------------------------------------------
Routine:      DV_SeekATN
Purpose:      ATN Seek function - uses GetTransportBasicParameters to send RAW AVC command 
Arguments:    None
Returns:      TRUE if successful
Notes:        This is Absolute Track Number Seek not TimeCode Seek but 
              uses the timecode display as input
------------------------------------------------------------------------*/
HRESULT CDVGraph::SeekATN(int iHr, int iMn, int iSc, int iFr)
{
    BOOL bStatus = FALSE;
    HRESULT hr = S_OK;
    ULONG ulTrackNumToSearch;
    long iCnt = 8;

    if (DVENCODERVIDEOFORMAT_PAL == m_VideoFormat && (iFr > 25) )
    {
        Dump(TEXT("Invalid Parameter - Frame should be less than 25 for PAL"));
        return E_FAIL;
    }
    
    if (DVENCODERVIDEOFORMAT_NTSC == m_VideoFormat && (iFr > 30) )
    {
        Dump(TEXT("Invalid Parameter - Frame should be less than 30 for NTSC"));
        return E_FAIL;
    }   
    

    // ATN Seek Raw AVC Command 
    BYTE RawAVCPkt[8] = {0x00, 0x20, 0x52, 0x20, 0xff, 0xff, 0xff, 0xff};

    if ((iHr < 24) && (iHr >= 0) && (iMn < 60) && (iMn >= 0) && (iSc < 60) && (iSc >= 0))
    {
        //Calculate the ATN
        if (m_AvgTimePerFrame == 40) 
        {
            ulTrackNumToSearch = ((iMn * 60 + iSc) * 25 + iFr) * 12 * 2;
        } 
        else 
        {
            // Drop two frame every minutes
            ulTrackNumToSearch = ((iMn * 60 + iSc) * 30 + iFr - ((iMn - (iMn / 10)) * 2)) * 10 * 2;
        }
        // Update the Raw AVC Command query
        RawAVCPkt[4] = (BYTE)  (ulTrackNumToSearch & 0x000000ff);
        RawAVCPkt[5] = (BYTE) ((ulTrackNumToSearch & 0x0000ff00) >> 8);
        RawAVCPkt[6] = (BYTE) ((ulTrackNumToSearch & 0x00ff0000) >> 16);
        
        // RAW AVC Call
        hr = m_pIAMExtTransport->GetTransportBasicParameters(ED_RAW_EXT_DEV_CMD, &iCnt, (LPOLESTR *)RawAVCPkt);     
        if ((HRESULT) ERROR_TIMEOUT == hr)
            OutputDebugString(TEXT(" ATN Seek returns ERROR_TIMEOUT"));
        else if ((HRESULT) ERROR_REQ_NOT_ACCEP == hr)
            OutputDebugString(TEXT(" ATN Seek returns ERROR_REQ_NOT_ACCEP"));
        else if ((HRESULT) ERROR_NOT_SUPPORTED == hr)
            OutputDebugString(TEXT(" ATN Seek returns ERROR_NOT_SUPPORTED"));
        else if ((HRESULT) ERROR_REQUEST_ABORTED == hr)
            OutputDebugString(TEXT(" ATN Seek returns ERROR_REQUEST_ABORTED "));       
    }
    else
    {
        Dump(TEXT("Invalid Parameter - Time entered should be:\nHour:Minute:Second:Frame"));
        hr = E_FAIL;
    } 

    return hr;
} 

// helper function
BOOL IsDeviceOutputDV(IBaseFilter * pFilter) 
{
    if (!pFilter) return FALSE;

    IEnumPins *pEnum = 0;
    IPin *pPin = 0;
    ULONG            ul ;
    BOOL bFound = FALSE;

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

    while (S_OK == pEnum->Next(1, &pPin, 0))
    {
        // See if this pin matches the specified direction.
        PIN_DIRECTION ThisPinDir;
        hr = pPin->QueryDirection(&ThisPinDir);
        if (FAILED(hr))        {
            SAFE_RELEASE(pPin);
            break;
        }

        if (ThisPinDir == PINDIR_OUTPUT)
        {
            IEnumMediaTypes* pTypeEnum;
            hr = pPin->EnumMediaTypes (&pTypeEnum);

            AM_MEDIA_TYPE* pMediaType;
            // Loop thru' media type list for a match
            do {
                hr = pTypeEnum->Next(1, &pMediaType, &ul) ;
                if (FAILED(hr) || 0 == ul) {
                    SAFE_RELEASE(pPin);
                    break ;
                }
        
                if (pMediaType->subtype == MEDIASUBTYPE_dvsd  ||
                    pMediaType->subtype == MEDIASUBTYPE_DVSD) {  
                    bFound = TRUE;
                    SAFE_RELEASE(pPin);
                    DeleteMediaType( pMediaType );
                
                    pTypeEnum->Release();
                    SAFE_RELEASE(pEnum);

                    return TRUE;
                }
                
                DeleteMediaType( pMediaType );

            } while (!bFound) ;  // until the reqd one is found

            pTypeEnum->Release();                      
        } 
    }

    SAFE_RELEASE(pPin);
    SAFE_RELEASE(pEnum);

    // Did not find a matching filter.
    return FALSE;
}