//==========================================================================;
//
// Copyright (c) Microsoft Corporation 1999-2000.
//
//--------------------------------------------------------------------------;
//
// MSVidStreamBufferSource.cpp : Implementation of CMSVidStreamBufferSource
//

#include "stdafx.h"

#ifndef TUNING_MODEL_ONLY

#include "atltmp.h"
#include <encdec.h>
#include "MSVidCtl.h"
#include "MSVidsbeSource.h"
#include "encdec.h"

#if 0 // code for testing wm content
#include <wmsdkidl.h>
#endif

#include "msvidsbesink.h"   // to get pabCert2


#define FILE_BEGINNING 0
#define LOCAL_OATRUE -1
DEFINE_EXTERN_OBJECT_ENTRY(CLSID_MSVidStreamBufferSource, CMSVidStreamBufferSource)

enum{
    CLOSE_TO_LIVE = 50,
};

/////////////////////////////////////////////////////////////////////////////
// CMSVidStreamBufferSource
STDMETHODIMP CMSVidStreamBufferSource::get_SBESource(/*[out, retval]*/ IUnknown **sbeFilter){
    if(!sbeFilter){
         return E_POINTER;
    }

    if(!m_spFileSource){
        USES_CONVERSION;
        CString csName(_T("SBE Playback"));
        QIFileSource qiFSource;
        HRESULT hr = qiFSource.CoCreateInstance(CLSID_StreamBufferSource, NULL, CLSCTX_INPROC_SERVER);
        if (FAILED(hr)){
            _ASSERT(false);
            return E_UNEXPECTED;
        }
        if(!qiFSource){
            _ASSERT(false);
            return E_UNEXPECTED;
        }
        m_spFileSource = qiFSource;
    }

    CComPtr<IUnknown> pUnk(m_spFileSource);
    if(!pUnk){
        return E_UNEXPECTED;
    }

    *sbeFilter = pUnk.Detach();
    return NOERROR;
}

STDMETHODIMP CMSVidStreamBufferSource::CurrentRatings(/*[out, retval]*/ EnTvRat_System *pEnSystem, /*[out, retval]*/ EnTvRat_GenericLevel *pEnRating, 
                                               /*[out, retval]*/ LONG *plbfEnAttr){
    if(!pEnSystem || !pEnRating || !plbfEnAttr){
        return E_POINTER;
    }
    DSFilterList::iterator i;
    EnTvRat_System system = static_cast<EnTvRat_System>(-1);
    EnTvRat_GenericLevel level = static_cast<EnTvRat_GenericLevel>(-1);
    LONG attr = static_cast<LONG>(-1);
    
    if(m_decFilters.empty()){
        return Error(IDS_INVALID_STATE, __uuidof(IMSVidStreamBufferSource), HRESULT_FROM_WIN32(ERROR_INVALID_STATE));
    }

    for(i = m_decFilters.begin(); i != m_decFilters.end(); ++i){
        EnTvRat_System temp_system = static_cast<EnTvRat_System>(-1);
        EnTvRat_GenericLevel temp_level = static_cast<EnTvRat_GenericLevel>(-1);
        LONG temp_attri = static_cast<LONG>(-1);
        CComQIPtr<IDTFilter> qiDT(*i);
        if(!qiDT){
            continue;
        }
        HRESULT hr = qiDT->GetCurrRating(&temp_system, &temp_level, &temp_attri);
        if(FAILED(hr)){
            continue;
        }
        if(temp_system != system || 
            temp_level != level || 
            temp_attri != attr){
            system = temp_system;
            level = temp_level;
            attr = temp_attri;
        }
    }
    
    if(static_cast<long>(system) < 0 || static_cast<long>(level) < 0 || static_cast<long>(attr) < 0){
        return E_FAIL;
    }

    *pEnSystem = system;
    *pEnRating = level;
    *plbfEnAttr = attr;
    return S_OK;
}
												   // ------------------

STDMETHODIMP CMSVidStreamBufferSource::MaxRatingsLevel(/*[in]*/ EnTvRat_System enSystem, /*[in]*/ EnTvRat_GenericLevel enRating, 
                                                /*[in]*/ LONG						 plbfEnAttr){
    DSFilterList::iterator i;
    
    if(m_decFilters.empty()){
        return Error(IDS_INVALID_STATE, __uuidof(IMSVidStreamBufferSource), HRESULT_FROM_WIN32(ERROR_INVALID_STATE));
    }

    for(i = m_decFilters.begin(); i != m_decFilters.end(); ++i){
        CComQIPtr<IDTFilter> qiDT(*i);
        if(!qiDT){
            continue;
        }

		HRESULT hr = qiDT->put_BlockedRatingAttributes(enSystem, enRating, plbfEnAttr);

        if(FAILED(hr)){
            return hr;
        }
    }
    
    return S_OK;
}

STDMETHODIMP CMSVidStreamBufferSource::put_BlockUnrated(/*[in]*/ VARIANT_BOOL bBlock){
    DSFilterList::iterator i;
    bool block = (bBlock == VARIANT_TRUE) ? true : false;
    
    if(m_decFilters.empty()){
        return Error(IDS_INVALID_STATE, __uuidof(IMSVidStreamBufferSource), HRESULT_FROM_WIN32(ERROR_INVALID_STATE));
    }
    
    for(i = m_decFilters.begin(); i != m_decFilters.end(); ++i){
        CComQIPtr<IDTFilter> qiDT(*i);
        if(!qiDT){
            continue;
        }
        HRESULT hr = qiDT->put_BlockUnRated(block);
        if(FAILED(hr)){
            return hr;
        }
    }
    return S_OK;
}

STDMETHODIMP CMSVidStreamBufferSource::put_UnratedDelay(/*[in]*/ long dwDelay){
    DSFilterList::iterator i;
    
    if(m_decFilters.empty()){
        return Error(IDS_INVALID_STATE, __uuidof(IMSVidStreamBufferSource), HRESULT_FROM_WIN32(ERROR_INVALID_STATE));
    }

    for(i = m_decFilters.begin(); i != m_decFilters.end(); ++i){
        CComQIPtr<IDTFilter> qiDT(*i);
        if(!qiDT){
            continue;
        }
        HRESULT hr = qiDT->put_BlockUnRatedDelay(dwDelay);
        if(FAILED(hr)){
            return hr;
        }
    }
    
    return S_OK;
}

STDMETHODIMP CMSVidStreamBufferSource::Unload(void) {
    BroadcastUnadvise();
    m_decFilters.clear();
    HRESULT hr = IMSVidGraphSegmentImpl<CMSVidStreamBufferSource, MSVidSEG_SOURCE, &GUID_NULL>::Unload();
    m_iReader = -1;
    m_spFileSource = reinterpret_cast<IFileSourceFilter*>(NULL);
    return hr;
}
STDMETHODIMP CMSVidStreamBufferSource::put_Init(IUnknown *pInit){
    HRESULT hr = IMSVidGraphSegmentImpl<CMSVidStreamBufferSource, MSVidSEG_SOURCE, &GUID_NULL>::put_Init(pInit);
    if (FAILED(hr)) {
        return hr;
    }
    if (pInit) {
        m_fInit = false;
        return E_NOTIMPL;
    }
    return NOERROR;
}
STDMETHODIMP CMSVidStreamBufferSource::get_Name(BSTR * Name){
    if (!m_fInit) {
 	    return Error(IDS_OBJ_NO_INIT, __uuidof(IMSVidStreamBufferSource), CO_E_NOTINITIALIZED);
    }
	if (Name == NULL)
		return E_POINTER;
    try {
	    *Name = m_Name.Copy();	
    } catch(...) {
        return E_POINTER;
    }
	return NOERROR;
}
// IMSVidInputDevice
STDMETHODIMP CMSVidStreamBufferSource::IsViewable(VARIANT* pv, VARIANT_BOOL *pfViewable)
{
    if (!m_fInit) {
        return Error(IDS_OBJ_NO_INIT, __uuidof(IMSVidStreamBufferSource), CO_E_NOTINITIALIZED);
    }
    if (!pv) {
        return E_POINTER;
    }
    return E_NOTIMPL;
}
STDMETHODIMP CMSVidStreamBufferSource::View(VARIANT* pv) {
    if (!m_fInit) {
        return Error(IDS_OBJ_NO_INIT, __uuidof(IMSVidStreamBufferSource), CO_E_NOTINITIALIZED);
    }
    if (!pv) {
        return E_POINTER;
    }
    if (!_wcsnicmp(pv->bstrVal, L"DVD:", 4)) {
        return E_FAIL;
    }
	return put_FileName(pv->bstrVal);
}
STDMETHODIMP CMSVidStreamBufferSource::InterfaceSupportsErrorInfo(REFIID riid)
{
	static const IID* arr[] = 
	{
		&IID_IMSVidStreamBufferSource
	};
	for (int i=0; i < sizeof(arr) / sizeof(arr[0]); i++)
	{
		if (InlineIsEqualGUID(*arr[i],riid))
			return S_OK;
	}
	return S_FALSE;
}
STDMETHODIMP CMSVidStreamBufferSource::put_Container(IMSVidGraphSegmentContainer *pCtl){
    try {
        HRESULT hr = S_OK;
        if (!m_fInit) {
            return Error(IDS_OBJ_NO_INIT, __uuidof(IMSVidStreamBufferSource), CO_E_NOTINITIALIZED);
        }
        if (!pCtl) {
#ifdef BUILD_WITH_DRM
            CComQIPtr<IServiceProvider> spServiceProvider(m_pGraph);
            if (spServiceProvider != NULL) {
                CComPtr<IDRMSecureChannel>  spSecureService;  
                hr = spServiceProvider->QueryService(SID_DRMSecureServiceChannel, 
                    IID_IDRMSecureChannel,
                    reinterpret_cast<LPVOID*>(&spSecureService));
                if(S_OK == hr){
                    // Found existing Secure Server
                    CComQIPtr<IRegisterServiceProvider> spRegServiceProvider(m_pGraph);
                    if(spRegServiceProvider == NULL){
                        // no service provider interface on the graph - fatal!
                        hr = E_NOINTERFACE;                 
                    } 

                    if(SUCCEEDED(hr)){ 
                        hr = spRegServiceProvider->RegisterService(SID_DRMSecureServiceChannel, NULL);
                    }
                } 
                _ASSERT(SUCCEEDED(hr));
            }
#endif
            return Unload();
        }
        if (m_pContainer) {
            if (!m_pContainer.IsEqualObject(VWSegmentContainer(pCtl))) {
                return Error(IDS_OBJ_ALREADY_INIT, __uuidof(IMSVidStreamBufferSource), CO_E_ALREADYINITIALIZED);
            } else {
                return NO_ERROR;
            }
        }

        // DON'T addref the container.  we're guaranteed nested lifetimes
        // and an addref creates circular refcounts so we never unload.
        m_pContainer.p = pCtl;
        m_pGraph = m_pContainer.GetGraph();
        hr = BroadcastAdvise();
        if (FAILED(hr)) {
            TRACELM(TRACE_ERROR, "CMSVidStreamBufferSource::put_Container() can't advise for broadcast events");
            return E_UNEXPECTED;
        }
#if 0 // code for testing wm content
        CComPtr<IUnknown> pUnkCert;
        hr = WMCreateCertificate(&pUnkCert);
        if (FAILED(hr)){
            _ASSERT(false);
        }

        CComQIPtr<IMSVidCtl>tempCtl(pCtl);
        if(tempCtl){
            hr = tempCtl->put_ServiceProvider(pUnkCert);
            if (FAILED(hr)){
                _ASSERT(false);
            }
        }
#endif

#ifdef BUILD_WITH_DRM
#ifdef USE_TEST_DRM_CERT
        {
            DWORD dwDisableDRMCheck = 0;
            CRegKey c;
            CString keyname(_T("SOFTWARE\\Debug\\MSVidCtl"));
            DWORD rc = c.Open(HKEY_LOCAL_MACHINE, keyname, KEY_READ);
            if (rc == ERROR_SUCCESS) {
                rc = c.QueryValue(dwDisableDRMCheck, _T("DisableDRMCheck"));
                if (rc != ERROR_SUCCESS) {
                    dwDisableDRMCheck = 0;
                }
            }

            if(dwDisableDRMCheck == 1){
                return S_OK;
            }
        }
#endif
        CComQIPtr<IServiceProvider> spServiceProvider(m_pGraph);
        if (spServiceProvider == NULL) {
            return E_NOINTERFACE;
        }
        CComPtr<IDRMSecureChannel>  spSecureService;  
        hr = spServiceProvider->QueryService(SID_DRMSecureServiceChannel, 
            IID_IDRMSecureChannel,
            reinterpret_cast<LPVOID*>(&spSecureService));
        if(S_OK == hr){
            // Found existing Secure Server
            return S_OK;
        } 
        else{
            // if it's not there or failed for ANY reason 
            //   lets create it and register it
            CComQIPtr<IRegisterServiceProvider> spRegServiceProvider(m_pGraph);
            if(spRegServiceProvider == NULL){
                // no service provider interface on the graph - fatal!
                hr = E_NOINTERFACE;                 
            } 
            else{
                // Create the Client 
                CComPtr<IDRMSecureChannel>  spSecureServiceServer; 
                hr = DRMCreateSecureChannel( &spSecureServiceServer);
                if(spSecureServiceServer == NULL){
                    hr = E_OUTOFMEMORY;
                }
                if(FAILED(hr)){ 
                    return hr;
                }

                // Init keys
                hr = spSecureServiceServer->DRMSC_SetCertificate((BYTE *)pabCert2, cBytesCert2);
                if(FAILED(hr)){                
                    return hr;
                }

                hr = spSecureServiceServer->DRMSC_SetPrivateKeyBlob((BYTE *)pabPVK2, cBytesPVK2);
                if(FAILED(hr)){ 
                    return hr;
                }

                hr = spSecureServiceServer->DRMSC_AddVerificationPubKey((BYTE *)abEncDecCertRoot, sizeof(abEncDecCertRoot) );
                if(FAILED(hr)){ 
                    return hr;
                }

                // Register It
                //   note RegisterService does not addref pUnkSeekProvider             
                hr = spRegServiceProvider->RegisterService(SID_DRMSecureServiceChannel, spSecureServiceServer);
            }
        }
#endif      // BUILD_WITH_DRM

        return NOERROR;
    } catch (ComException &e) {
        return e;
    } catch (...) {
        return E_UNEXPECTED;
    }
}

//-----------------------------------------------------------------------------------------
// Name: get_CanStep(VARIANT_BOOL, VARIANT_BOOL*)
//-----------------------------------------------------------------------------------------
STDMETHODIMP CMSVidStreamBufferSource::get_CanStep(VARIANT_BOOL fBackwards, VARIANT_BOOL *pfCan){
        // NOTE: NO ONE supports backwords stepping (why not? who knows)
        // so just like everyone else we dont either
        try{
            // Checking args and interfaces 
       
            if(!pfCan){
                // Passed a NULL Pointer
                return E_POINTER;
            }

            if (!m_pGraph) {
                // graph not valid
                return Error(IDS_INVALID_STATE, __uuidof(IMSVidPlayback), HRESULT_FROM_WIN32(ERROR_INVALID_STATE));
			}

            //Get a VideoFrameStep Interface
			PQVideoFrameStep pVFS(m_pGraph);
            if(!pVFS){
                // Could Not QI
				return Error(IDS_E_CANTQI , __uuidof(IMSVidPlayback), E_NOINTERFACE);          
			}
            
            if(fBackwards == VARIANT_TRUE){
                *pfCan = VARIANT_TRUE;
                return S_OK;
            }
            else{
                if(pVFS->CanStep(FALSE, NULL)==S_OK){
                    // It is all Good, Can Step Forward
                    *pfCan = VARIANT_TRUE;
                    return S_OK;
                }
                
                else{
                    // Can't Step
                    *pfCan = VARIANT_FALSE;
                    return S_OK;
                }
            }
        }
        catch(HRESULT hrTmp){
            // Something went bad, threw a HRESULT
            return Error(IDS_INVALID_STATE , __uuidof(IMSVidPlayback), hrTmp);
        }
        catch(...){
            // Something went bad, dont know what it threw
            return E_UNEXPECTED;   
        }
    }

//-----------------------------------------------------------------------------------------
// Name: Step(long)
//-----------------------------------------------------------------------------------------
STDMETHODIMP CMSVidStreamBufferSource::Step(long lStep){
        try{
            // Checking args and interfaces
            long tempStep = lStep;
            if (!m_pGraph || !m_pContainer) {
                // graph not valid
                return Error(IDS_INVALID_STATE, __uuidof(IMSVidPlayback), HRESULT_FROM_WIN32(ERROR_INVALID_STATE));
            }
            
            PQVideoFrameStep pVFS(m_pGraph);
            
            if(!pVFS){
                // Could Not QI
                return Error(IDS_E_CANTQI , __uuidof(IMSVidPlayback), E_NOINTERFACE);
                
            }
            
            if(lStep < 0){
                PQMediaControl pmc(m_pGraph);
                if (!pmc) {
                    return Error(IDS_NO_MEDIA_CONTROL, __uuidof(IMSVidPlayback), E_UNEXPECTED);
                }
                HRESULT hr = pmc->Pause();
                if (FAILED(hr)) {
                    TRACELSM(TRACE_ERROR, (dbgDump << "CVidCtl::Pause() hr = " << std::hex << hr), "");
                    return Error(IDS_CANT_PAUSE_GRAPH, __uuidof(IMSVidPlayback), hr);
                }
                
                long cur = 0;
                long stepVal = (/*half a second in 100ths of a second*/ 50 * lStep);
                PositionModeList curMode;
                hr = get_PositionMode(&curMode);
                if(FAILED(hr)){
                    return hr;
                }
                
                if(curMode == FrameMode){
                    stepVal = (stepVal/100) * 30; // hard coded to 30 fps for now
                }
                
                hr = get_CurrentPosition(&cur);
                if(FAILED(hr)){
                    return hr;
                }

                if(cur == 0){
                    return Error(IDS_INVALID_STATE, __uuidof(IMSVidPlayback), HRESULT_FROM_WIN32(ERROR_INVALID_STATE));
                }
                
                cur = cur + stepVal; // stepVal is negative, duh
                hr = put_CurrentPosition(cur);
                if(FAILED(hr)){
                    return hr;
                }
                // Set tempStep and then step to refresh the current frame
                tempStep = 1;
            }
            // Make it step
            return pVFS->Step(tempStep, NULL);
        }
        
        catch(HRESULT hrTmp){
            // Something went bad, threw a HRESULT				
            return Error(IDS_INVALID_STATE , __uuidof(IMSVidPlayback), hrTmp);
        }
        catch(...){
            // Something went bad, dont know what it threw
            return E_UNEXPECTED;
        }
    }
    

//-----------------------------------------------------------------------------------------
// Name: get_Start(long)
//-----------------------------------------------------------------------------------------
STDMETHODIMP CMSVidStreamBufferSource::get_Start(/*[out, retval]*/long *lStart){
    HRESULT hr = S_OK;
    LONGLONG tempfirst, templatest;
    PositionModeList curMode;
    try{
        // Checking args and init'ing interfaces
        if (!lStart){
            return E_POINTER;
        }
        if (!m_pGraph) {
            // graph not valid
            return Error(IDS_INVALID_STATE, __uuidof(IMSVidStreamBufferSource), HRESULT_FROM_WIN32(ERROR_INVALID_STATE));
        }

        // See if object supports ISBEMediaSeeking
        PQISBEMSeeking PQIMSeeking(m_spFileSource);
        if(!( !PQIMSeeking)){ // not not'ing smart pointer, they assert if p == 0
            // Find out what postion mode is being used
            hr = get_PositionMode(&curMode);
            if(FAILED(hr)){
                return hr;
            }
            hr = PQIMSeeking->GetAvailable(&tempfirst, &templatest);
            if(FAILED(hr)){
                return hr;
            }
            // If it is FrameMode no conversion needed
            if(tempfirst == 0){
                *lStart = 0;
                hr = S_OK;
                return hr;
            }
            if(curMode == FrameMode){
                *lStart = static_cast<long>(tempfirst);
                TRACELSM(TRACE_ERROR, (dbgDump << "StreamBufferSource::get_Start() return=" << (unsigned long)(*lStart) << " longlong=" << (double)(tempfirst)), "");

                hr = S_OK;
                return hr;
            }
            // If it is TenthsSecondsMode need to be converted from 100 nanosecond units
            if(curMode == TenthsSecondsMode){
                *lStart = static_cast<long>(tempfirst / nano_to_hundredths);    
                TRACELSM(TRACE_ERROR, (dbgDump << "StreamBufferSource::get_Start() return=" << (unsigned long)(*lStart) << " longlong=" << (double)(tempfirst)), "");
                hr = S_OK;
                return hr;
            }
            // If it is some other mode not supported by the vidctl
            else{
                return E_UNEXPECTED;
            }
        }
        // Could Not QI IMedia Seeking or Position
        return Error(IDS_E_CANTQI , __uuidof(IMSVidPlayback), E_NOINTERFACE);
        
    }
    
    catch(HRESULT hrTmp){
        // Something went bad, threw a HRESULT				
        return Error(IDS_INVALID_STATE , __uuidof(IMSVidPlayback), hrTmp);
    }
    catch(...){
        // Something went bad, dont know what it threw
        return E_UNEXPECTED;
    }
}

STDMETHODIMP CMSVidStreamBufferSource::get_RecordingAttribute(/*[out, retval]*/ IUnknown **pRecordingAttribute){
    if(!pRecordingAttribute){
        return E_POINTER;
    }
    CComPtr<IUnknown> pRecUnk(m_spFileSource);
    if(!pRecUnk){
        return Error(IDS_INVALID_STATE, __uuidof(IMSVidStreamBufferSource), HRESULT_FROM_WIN32(ERROR_INVALID_STATE));
    }
    *pRecordingAttribute = pRecUnk.Detach();
    return S_OK;
}

//-----------------------------------------------------------------------------------------
// Name: get_Length(long)
//-----------------------------------------------------------------------------------------
STDMETHODIMP CMSVidStreamBufferSource::get_Length(/*[out, retval]*/long *lLength){
    HRESULT hr = S_OK;
    LONGLONG tempfirst, templatest;
    PositionModeList curMode;
    try{
        // Checking args and init'ing interfaces
        if (!lLength){
            return E_POINTER;
        }
        if (!m_pGraph) {
            // graph not valid
            return Error(IDS_INVALID_STATE, __uuidof(IMSVidStreamBufferSource), HRESULT_FROM_WIN32(ERROR_INVALID_STATE));
        }

        // See if object supports ISBEMediaSeeking
        PQISBEMSeeking PQIMSeeking(m_spFileSource);
        if(!( !PQIMSeeking)){ // not not'ing smart pointer, they assert if p == 0
            // Find out what postion mode is being used
            hr = get_PositionMode(&curMode);
            if(FAILED(hr)){
                return hr;
            }
            hr = PQIMSeeking->GetAvailable(&tempfirst, &templatest);
            if(FAILED(hr)){
                return hr;
            }
            // If it is FrameMode no conversion needed
            if(curMode == FrameMode){
                *lLength = static_cast<long>(templatest);
                TRACELSM(TRACE_ERROR, (dbgDump << "StreamBufferSource::get_Length() return=" << (unsigned long)(*lLength) << " longlong=" << (double)(templatest)), "");
                hr = S_OK;
                return hr;
            }
            // If it is TenthsSecondsMode need to be converted from 100 nanosecond units
            else if(curMode == TenthsSecondsMode){
                *lLength = static_cast<long>(templatest / nano_to_hundredths);
                TRACELSM(TRACE_ERROR, (dbgDump << "StreamBufferSource::get_Length() return=" << (unsigned long)(*lLength) << " longlong=" << (double)(templatest)), "");
                hr = S_OK;
                return hr;
            }
            // If it is some other mode not supported by the vidctl
            else{
                return E_UNEXPECTED;
            }
        }

         // Could Not QI IMedia Seeking
        return Error(IDS_E_CANTQI , __uuidof(IMSVidPlayback), E_NOINTERFACE);
        
    }
    
    catch(HRESULT hrTmp){
        // Something went bad, threw a HRESULT				
        return Error(IDS_INVALID_STATE , __uuidof(IMSVidPlayback), hrTmp);
    }
    catch(...){
        // Something went bad, dont know what it threw
        return E_UNEXPECTED;
    }
}

//-----------------------------------------------------------------------------------------
// Name: get_CurrentPosition(LONGLONG*)
//-----------------------------------------------------------------------------------------
STDMETHODIMP CMSVidStreamBufferSource::get_CurrentPosition(/*[out,retval]*/long *lPosition) {
    HRESULT hr = S_OK;
    LONGLONG tempval;
    PositionModeList curMode;
    try{
        // Checking args and init'ing interfaces
        if (!lPosition){
            return E_POINTER;
        }
        if (!m_spFileSource) {
            // graph not valid
            return Error(IDS_INVALID_STATE, __uuidof(IMSVidPlayback), HRESULT_FROM_WIN32(ERROR_INVALID_STATE));
        }

        // See if object supports ISBEMediaSeeking
        PQISBEMSeeking PQIMSeeking(m_spFileSource);
        if(!( !PQIMSeeking)){// not not'ing smart pointer, they assert if p == 0
            // Find out what postion mode is being used
            hr = get_PositionMode(&curMode);
            if(FAILED(hr)){
                return hr;
            }
            hr = PQIMSeeking->GetCurrentPosition(&tempval);
            if(FAILED(hr)){
                return hr;
            }
            // If it is FrameMode no conversion needed
            if(curMode == FrameMode){
                *lPosition = static_cast<long>(tempval);
                TRACELSM(TRACE_ERROR, (dbgDump << "StreamBufferSource::get_CurrentPosition() return=" << (unsigned long)(*lPosition) << " longlong=" << (double)(tempval)), "");
                hr = S_OK;
                return hr;
            }
            // If it is TenthsSecondsMode need to be converted from 100 nanosecond units
            else if(curMode == TenthsSecondsMode){
                *lPosition = static_cast<long>(tempval / nano_to_hundredths);
                TRACELSM(TRACE_ERROR, (dbgDump << "StreamBufferSource::get_CurrentPosition() return=" << (unsigned long)(*lPosition) << " longlong=" << (double)(tempval)), "");
                hr = S_OK;
                return hr;
            }
            // If it is some other mode not supported by the vidctl
            else{
                return E_UNEXPECTED;
            }
        }
        // Could Not QI IMedia Seeking
        return Error(IDS_E_CANTQI , __uuidof(IMSVidPlayback), E_NOINTERFACE);

    }

    catch(HRESULT hrTmp){
        // Something went bad, threw a HRESULT				
        return Error(IDS_INVALID_STATE , __uuidof(IMSVidPlayback), hrTmp);
    }
    catch(...){
        // Something went bad, dont know what it threw
        return E_UNEXPECTED;
    }
}
//-----------------------------------------------------------------------------------------
// Name: put_CurrentPosition(LONGLONG)
//-----------------------------------------------------------------------------------------
STDMETHODIMP CMSVidStreamBufferSource::put_CurrentPosition(/*[in]*/long lPosition) {
    HRESULT hr = S_OK;
    LONGLONG tempval;
    PositionModeList curMode;
    try{
        // Checking args and interfaces
        if (!m_spFileSource) {
            return Error(IDS_INVALID_STATE, __uuidof(IMSVidPlayback), HRESULT_FROM_WIN32(ERROR_INVALID_STATE));
        }

        // Check for a ISBEMediaSeeking Interface
        PQISBEMSeeking PQIMSeeking(m_spFileSource);
        if(!( !PQIMSeeking)){ // not not'ing smart pointer, they assert if p == 0
            // Get the position Mode
            hr = get_PositionMode(&curMode);
            if(FAILED(hr)){
                return hr;
            }
            tempval = lPosition;
            // If it is in TenthsSecondsMode convert input into 100 nanosecond units
            if(curMode == TenthsSecondsMode){
                tempval = (tempval) * nano_to_hundredths;
            }
            // If it is in some other mode
            else if(curMode != FrameMode){
                return E_UNEXPECTED;
            }
            // Set the new Position
            TRACELSM(TRACE_ERROR, (dbgDump << "StreamBufferSource::put_CurrentPosition() set to: input=" << (unsigned long)(lPosition) << " longlong=" << (double)(tempval)), "");
            hr = PQIMSeeking->SetPositions(&tempval, AM_SEEKING_AbsolutePositioning, NULL, 0);
            TRACELSM(TRACE_ERROR, (dbgDump << "StreamBufferSource::put_CurrentPosition() actually set to:" << (double)(tempval)), "");

            return hr; 
        }
        // Could Not QI Media Position
        return Error(IDS_E_CANTQI , __uuidof(IMSVidPlayback), E_NOINTERFACE);

    }

    catch(HRESULT hrTmp){
        // Something went bad, threw a HRESULT				
        return Error(IDS_INVALID_STATE , __uuidof(IMSVidPlayback), hrTmp);
    }
    catch(...){
        // Something went bad, dont know what it threw
        return E_UNEXPECTED;
    }
}
//-----------------------------------------------------------------------------------------
// Name: put_PositionMode(LONGLONG)
//-----------------------------------------------------------------------------------------

STDMETHODIMP CMSVidStreamBufferSource::put_PositionMode(/*[in]*/PositionModeList lPositionMode) {
    HRESULT hr = S_OK;
    double testval;
    get_Rate(&testval);
    try{
        // Checking args and interfaces
        if (!m_spFileSource) {
            // graph not valid
            return Error(IDS_INVALID_STATE, __uuidof(IMSVidPlayback), HRESULT_FROM_WIN32(ERROR_INVALID_STATE));
        }
        // only valid values
        if(lPositionMode != FrameMode && lPositionMode != TenthsSecondsMode){
            return E_INVALIDARG;
        }
        // Try for a ISBEMediaSeeking
        PQISBEMSeeking PQIMSeeking(m_spFileSource);
        if(!( !PQIMSeeking)){// not not'ing smart pointer, they assert if p == 0
            // Set the new mode
            if(lPositionMode == FrameMode){
                hr = PQIMSeeking->SetTimeFormat( &( static_cast<GUID>(TIME_FORMAT_FRAME) ) );
                return hr; 
            }
            if(lPositionMode == TenthsSecondsMode){
                hr = PQIMSeeking->SetTimeFormat(&(static_cast<GUID>(TIME_FORMAT_MEDIA_TIME)));
                return hr; 
            }
        }
        // Could Not QI
        return Error(IDS_E_CANTQI , __uuidof(IMSVidPlayback), E_NOINTERFACE);

    }

    catch(HRESULT hrTmp){
        // Something went bad, threw a HRESULT				
        return Error(IDS_INVALID_STATE , __uuidof(IMSVidPlayback), hrTmp);
    }
    catch(...){
        // Something went bad, dont know what it threw
        return E_UNEXPECTED;
    }
}


//-----------------------------------------------------------------------------------------
// Name: get_PositionMode(LONGLONG*)
//-----------------------------------------------------------------------------------------
STDMETHODIMP CMSVidStreamBufferSource::get_PositionMode(/*[out,retval]*/PositionModeList* lPositionMode) {
    HRESULT hr = S_OK;
    double testval;
    get_Rate(&testval);
    try{
        // Checking args and interfaces
        if(!lPositionMode){
            return E_POINTER;
        }
        if (!m_spFileSource) {
            // graph not valid
            return Error(IDS_INVALID_STATE, __uuidof(IMSVidPlayback), HRESULT_FROM_WIN32(ERROR_INVALID_STATE));
        }
        // Get an ISBEMediaSeeking Interface
        PQISBEMSeeking PQIMSeeking(m_spFileSource);
        if(!( !PQIMSeeking)){// not not'ing smart pointer, they assert if p == 0
            // Get the mode
            GUID cur_mode;
            hr = PQIMSeeking->GetTimeFormat(&cur_mode);
            if(FAILED(hr)){
                return hr;
            }
            // Check to see which mode it is in
            if(cur_mode == static_cast<GUID>(TIME_FORMAT_FRAME)){
                *lPositionMode = FrameMode;
                return S_OK;
            }
            if(cur_mode == static_cast<GUID>(TIME_FORMAT_MEDIA_TIME)){
                *lPositionMode = TenthsSecondsMode;
                return S_OK;
            }
            // Not in a vidctl supported mode
            else{
                return E_FAIL;
            }
        }
        // Could Not QI
        return Error(IDS_E_CANTQI , __uuidof(IMSVidPlayback), E_NOINTERFACE);

    }

    catch(HRESULT hrTmp){
        // Something went bad, threw a HRESULT				
        return Error(IDS_INVALID_STATE , __uuidof(IMSVidPlayback), hrTmp);
    }
    catch(...){
        // Something went bad, dont know what it threw
        return E_UNEXPECTED;
    }
}

//-----------------------------------------------------------------------------------------
// Name: put_Rate(double)
//-----------------------------------------------------------------------------------------
STDMETHODIMP CMSVidStreamBufferSource::put_Rate(double lRate){
    HRESULT hr = S_OK;
    try{
        /*** Checking args and init'ing interfaces ***/

        if (!m_spFileSource) {
            // graph not valid
            return Error(IDS_INVALID_STATE, __uuidof(IMSVidPlayback), HRESULT_FROM_WIN32(ERROR_INVALID_STATE));
        }

        // Attempt to set the rate using ISBEMediaSeeking
        PQISBEMSeeking PQIMSeeking(m_spFileSource);
        if(!( !PQIMSeeking)){// not not'ing smart pointer, they assert if p == 0
            return PQIMSeeking->SetRate(lRate);
        }
        // Could Not QI set the error
        return Error(IDS_E_CANTQI , __uuidof(IMSVidPlayback), E_NOINTERFACE);

    }

    catch(HRESULT hrTmp){
        // Something went bad, threw a HRESULT				
        return Error(IDS_INVALID_STATE , __uuidof(IMSVidPlayback), hrTmp);
    }
    catch(...){
        // Something went bad, dont know what it threw
        return E_UNEXPECTED;
    }
}
//-----------------------------------------------------------------------------------------
// Name: get_Rate(double*)
//-----------------------------------------------------------------------------------------
STDMETHODIMP CMSVidStreamBufferSource::get_Rate(double *plRate){
    HRESULT hr = S_OK;
    try{
        /*** Checking args and init'ing interfaces ***/
        if (!plRate){
            return E_POINTER;
        }
        if (!m_spFileSource) {
            // graph not valid
            return Error(IDS_INVALID_STATE, __uuidof(IMSVidPlayback), HRESULT_FROM_WIN32(ERROR_INVALID_STATE));
        }
        PQISBEMSeeking PQIMSeeking(m_spFileSource);
        if(!( !PQIMSeeking)){// not not'ing smart pointer, they assert if p == 0
            return PQIMSeeking->GetRate(plRate);
        }
        // Could Not QI
        return Error(IDS_E_CANTQI , __uuidof(IMSVidPlayback), E_NOINTERFACE);

    }

    catch(HRESULT hrTmp){
        // Something went bad, threw a HRESULT				
        return Error(IDS_INVALID_STATE , __uuidof(IMSVidPlayback), hrTmp);
    }
    catch(...){
        // Something went bad, dont know what it threw
        return E_UNEXPECTED;
    }
}

STDMETHODIMP CMSVidStreamBufferSource::PostStop(){
    HRESULT hr = S_OK;

    try {
#if 0
        // If the graph is not is stopped state
        // we make sure it is
        if (!m_pGraph.IsStopped()) {
            HRESULT hr = PQVidCtl(m_pContainer)->Stop();
        }
#endif
        // If m_fEnableResetOnStop is true then we need to reset 
        // the postion back to the beggining
        // else do nothing
        if(m_fEnableResetOnStop){
            return put_CurrentPosition(0);
        }

    }
    catch(HRESULT hrTmp){
        hr = hrTmp;
    }
    catch(...){
        hr = E_UNEXPECTED;
    }
    return hr;      
}
STDMETHODIMP CMSVidStreamBufferSource::Decompose() {
    return put_Container(NULL);
}
STDMETHODIMP CMSVidStreamBufferSource::Build() {
    if (!m_FileName) {
        return Error(IDS_INVALID_STATE, __uuidof(IMSVidStreamBufferSource), HRESULT_FROM_WIN32(ERROR_INVALID_STATE));
    }

    QIFileSource qiFSource;
    HRESULT hr = S_OK;
    DSFilter pfr;
    if(!m_spFileSource){
        USES_CONVERSION;
        hr = qiFSource.CoCreateInstance(CLSID_StreamBufferSource, NULL, CLSCTX_INPROC_SERVER);
        if (FAILED(hr)){
            _ASSERT(false);
            return E_UNEXPECTED;
        }
        if(!qiFSource){
            _ASSERT(false);
            return E_UNEXPECTED;
        }
        m_spFileSource = qiFSource;
        hr = m_spFileSource->QueryInterface(&pfr);
        if (FAILED(hr) || !pfr) {
            _ASSERT(false);
            TRACELSM(TRACE_ERROR,  (dbgDump << "MSVidStreamBufferSource::Build() Could not create StreamBufferSource hr = " << std::hex << hr), "");
            return Error(IDS_CANT_PLAY_FILE, __uuidof(IMSVidStreamBufferSource), hr);
        }
    }
    else{
        qiFSource = m_spFileSource;
        if(!qiFSource){
            _ASSERT(false);
            return E_UNEXPECTED; 
        }
        hr = m_spFileSource->QueryInterface(&pfr);
        if (FAILED(hr) || !pfr) {
            _ASSERT(false);
            TRACELSM(TRACE_ERROR,  (dbgDump << "MSVidStreamBufferSource::Build() Could not create StreamBufferSource hr = " << std::hex << hr), "");
            return Error(IDS_CANT_PLAY_FILE, __uuidof(IMSVidStreamBufferSource), hr);
        }
    }

    CString csName(_T("SBE Playback"));
    m_Filters.clear();
    hr = m_pGraph.AddFilter(pfr, csName);
    if(FAILED(hr)){
        _ASSERT(false);
        return E_UNEXPECTED;
    }

    hr = qiFSource->Load(m_FileName, NULL);
    if (FAILED(hr)) {
        bool rc = m_pGraph.RemoveFilter(pfr);
        if (!rc) {
            return E_UNEXPECTED;
        }
        TRACELSM(TRACE_ERROR,  (dbgDump << "MSVidStreamBufferSource::Build() Could not create StreamBufferSource hr = " << std::hex << hr), "");
        return Error(IDS_CANT_PLAY_FILE, __uuidof(IMSVidStreamBufferSource), hr);
    }

    m_Filters.push_back(pfr);
    m_iReader = m_Filters.size() - 1;
#if ENCRYPT_NEEDED
    DSFilterList intermediates;
    for(DSFilter::iterator i = pfr.begin(); i != pfr.end(); ++i){
        if((*i).GetDirection() == DOWNSTREAM && !(*i).IsConnected()){
            // Create and add a decoder Tagger Filter 
            CComPtr<IUnknown> spEncTagD(CLSID_DTFilter, NULL, CLSCTX_INPROC_SERVER);
            if (!spEncTagD) {
                TRACELM(TRACE_ERROR, "CMSVidStreamBufferSink::Build() can't load Tagger filter");
                return ImplReportError(__uuidof(IMSVidStreamBufferSink), IDS_CANT_CREATE_FILTER, __uuidof(IStreamBufferSink), E_UNEXPECTED);
            }

            DSFilter vrD(spEncTagD);
            if (!vrD) {
                ASSERT(false);
                return ImplReportError(__uuidof(IMSVidStreamBufferSink), IDS_CANT_CREATE_FILTER, __uuidof(IStreamBufferSink), E_UNEXPECTED);
            }

            m_Filters.push_back(vrD);
            m_decFilters.push_back(vrD);
            csName = _T("Decoder/Tagger Filter");
            m_pGraph.AddFilter(vrD, csName);

            // Connect pin to the Tagger
            hr = (*i).IntelligentConnect(vrD, intermediates);
            if(FAILED(hr)){
                TRACELM(TRACE_DETAIL, "CAnaSinComp::Compose() if you see this line more than once something must have gone wrong");  
            }

        }
    }
    ASSERT(intermediates.begin() == intermediates.end());
    m_Filters.insert(m_Filters.end(), intermediates.begin(), intermediates.end());
#endif
    return NOERROR;
}

STDMETHODIMP CMSVidStreamBufferSource::PreRun(){
#if 0 
    if(m_iReader == -1 || m_Filters.empty()){
        return Error(IDS_INVALID_STATE, __uuidof(IMSVidStreamBufferSource), HRESULT_FROM_WIN32(ERROR_INVALID_STATE));
    }
    
    CComQIPtr<IReferenceClock> pq_IRClock(m_Filters[m_iReader]);
    if(!pq_IRClock){
        return S_FALSE;
    }

    CComQIPtr<IMediaFilter> pq_MFGph(m_pGraph);
    if(!pq_MFGph){
        return E_NOINTERFACE;
    }


    HRESULT hr = pq_MFGph->SetSyncSource(pq_IRClock);
    if(FAILED(hr)){
        ASSERT(false);
    }

    return hr;
#endif
    return E_NOTIMPL;
}

STDMETHODIMP CMSVidStreamBufferSource::OnEventNotify(long lEvent, LONG_PTR lParam1, LONG_PTR lParam2) {
    if (lEvent == EC_COMPLETE) {
        double curRate = 0;
        HRESULT hr = S_OK;
        hr = get_Rate(&curRate);
        if(SUCCEEDED(hr)){
            if(curRate < 0){
                hr = put_Rate(1);
                if(FAILED(hr)){
                    _ASSERT(false);
                }

                // We need to transition to pause then back to play to flush all of the buffers
                // It appears to be a decoder issue, mostly
                PQVidCtl sp_VidCtl(m_pContainer);
                if(sp_VidCtl){
                    hr = sp_VidCtl->Pause();
                    if(FAILED(hr)){
                        _ASSERT(false); // Failed to pause this is really bad
                    }

                    hr = sp_VidCtl->Run();
                    if(FAILED(hr)){
                        _ASSERT(false); // Failed to run this is really bad
                    }
                }
                else{
                    _ASSERT(false); // We got events with no vidctl hosting us, really weird
                }



                CComQIPtr<IMSVidPlayback> ppb(this);
                Fire_EndOfMedia(ppb);
                TRACELM(TRACE_ERROR, "CMSVidStreamBufferSource::OnEventNotify Tossed EndOfMedia at start of sbe stream");  
                return NOERROR;

            }
        }
    }

    if(lEvent == STREAMBUFFER_EC_RATE_CHANGED){
        TRACELM(TRACE_ERROR, "CMSVidStreamBufferSource::OnEventNotify STREAMBUFFER_EC_RATE_CHANGED");
        HRESULT hr = S_OK;
#if 0 // code to try to make up for the lack of a rate change event on the vidctl
        MSVidCtlStateList curState = STATE_UNBUILT;
        hr = PQVidCtl(m_pContainer)->get_State(&curState);
        if(SUCCEEDED(hr) && curState == STATE_PLAY){
            CComQIPtr<IMSVidDevice> pd(this);
            if (!pd) {
                TRACELM(TRACE_ERROR, "CMSVidStreamBufferSource::OnEventNotify Could not qi SBE Source Segment for IMSVidDevice");  
            }
            else{
                Fire_StateChange(pd, STATE_PLAY, STATE_PLAY);
                TRACELM(TRACE_ERROR, "CMSVidStreamBufferSource::OnEventNotify Tossed StateChange STATE_PLAY STATE_PLAY for rate change");  
            }
        }
#endif
        long len;
        long curPos;
        curPos = len = 0;
        hr = get_Length(&len);
        if(SUCCEEDED(hr)){
            hr = get_CurrentPosition(&curPos);
            if(SUCCEEDED(hr)){
                if(len <= (curPos + CLOSE_TO_LIVE)){ // if current position is with in CLOSE_TO_LIVE of the len the we just bounced off of the end of the stream
                    CComQIPtr<IMSVidPlayback> ppb(this);
                    if (!ppb) {
                        TRACELM(TRACE_ERROR, "CMSVidStreamBufferSource::OnEventNotify Could not qi SBE Source Segment for IMSVidPlayback");  
                    }
                    else{
                        Fire_EndOfMedia(ppb);
                        TRACELM(TRACE_ERROR, "CMSVidStreamBufferSource::OnEventNotify Tossed EndOfMedia at end of sbe stream");  
                        return NOERROR;
                    }
                }
            }
        }
    }

    if(lEvent == STREAMBUFFER_EC_TIMEHOLE){
        Fire_TimeHole(lParam1, lParam2);
        return NOERROR;
    }

    if(lEvent == STREAMBUFFER_EC_STALE_DATA_READ){
        Fire_StaleDataRead();
        return NOERROR;
    }

    if(lEvent == STREAMBUFFER_EC_STALE_FILE_DELETED){
        Fire_StaleFilesDeleted();
        return NOERROR;
    }

    if(lEvent == STREAMBUFFER_EC_CONTENT_BECOMING_STALE){
        Fire_ContentBecomingStale();
        return NOERROR;
    }

    return IMSVidPBGraphSegmentImpl<CMSVidStreamBufferSource, MSVidSEG_SOURCE, &GUID_NULL>::OnEventNotify(lEvent, lParam1, lParam2);
}

HRESULT CMSVidStreamBufferSource::Fire(GUID gEventID) {
    TRACELSM(TRACE_DETAIL, (dbgDump << "CMSVidStreamBufferSource::Fire() guid = " << GUID2(gEventID)), "");
    if (gEventID == EVENTID_ETDTFilterLicenseFailure) {
		Fire_CertificateFailure();
    } else if (gEventID == EVENTID_ETDTFilterLicenseOK) {
		Fire_CertificateSuccess();
    } else if (gEventID == EVENTID_DTFilterRatingsBlock) {
        Fire_RatingsBlocked();
    } else if (gEventID == EVENTID_DTFilterRatingsUnblock) {
        Fire_RatingsUnblocked();
    } else if (gEventID == EVENTID_DTFilterRatingChange) {
        Fire_RatingsChanged();
    }

    return NOERROR;
}


#endif