//------------------------------------------------------------------------------ // // Copyright (c) Microsoft Corporation. All rights reserved. // // The use and distribution terms for this software are covered by the // Microsoft Limited Public License (Ms-LPL) // which can be found in the file MS-LPL.txt at the root of this distribution. // By using this software in any fashion, you are agreeing to be bound by // the terms of this license. // // The software is licensed “as-is.” // // You must not remove this notice, or any other, from this software. // // // // Demux code for Windows CE // //------------------------------------------------------------------------- //====================================================================== // Demux code for Windows CE //====================================================================== #include "precomp.h" #include "demxutil.h" #include "mp2demux.h" #include "tsstats.h" #include "mp2seek.h" #include "pin_out.h" #include "filter.h" #include "bufsrc.h" #include "plparse.h" int ParserArgSize ( IN DWORD dwMediaSampleContent ) { switch (dwMediaSampleContent) { case MPEG2_MEDIA_ELEMENTARY_STREAM : return sizeof AUDIO_FILTER_INFO ; } ; return 0 ; } // external to internal HRESULT TransportMediaSampleContentEtoI ( IN DWORD ExtMediaSampleContent, // transport OUT DWORD * pdwIntMediaSampleContent ) { ASSERT (pdwIntMediaSampleContent) ; switch (ExtMediaSampleContent) { case MEDIA_TRANSPORT_PACKET : (* pdwIntMediaSampleContent) = MPEG2_MEDIA_TRANSPORT_PACKET ; break ; case MEDIA_ELEMENTARY_STREAM : (* pdwIntMediaSampleContent) = MPEG2_MEDIA_ELEMENTARY_STREAM ; break ; case MEDIA_MPEG2_PSI : (* pdwIntMediaSampleContent) = MPEG2_MEDIA_TRANSPORT_PSI ; break ; case MEDIA_TRANSPORT_PAYLOAD : (* pdwIntMediaSampleContent) = MPEG2_MEDIA_TRANSPORT_PAYLOAD ; break ; default : return E_INVALIDARG ; } ; return S_OK ; } HRESULT ProgramMediaSampleContentEtoI ( IN DWORD ExtMediaSampleContent, // program OUT DWORD * pdwIntMediaSampleContent ) { ASSERT (pdwIntMediaSampleContent) ; switch (ExtMediaSampleContent) { case MPEG2_PROGRAM_STREAM_MAP : (* pdwIntMediaSampleContent) = MPEG2_MEDIA_PROGRAM_STREAM_MAP ; break ; case MPEG2_PROGRAM_ELEMENTARY_STREAM : (* pdwIntMediaSampleContent) = MPEG2_MEDIA_ELEMENTARY_STREAM ; break ; case MPEG2_PROGRAM_DIRECTORY_PES_PACKET : (* pdwIntMediaSampleContent) = MPEG2_MEDIA_PROGRAM_DIRECTORY_PES_PACKET ; break ; case MPEG2_PROGRAM_PACK_HEADER : (* pdwIntMediaSampleContent) = MPEG2_MEDIA_PROGRAM_PACK_HEADER ; break ; case MPEG2_PROGRAM_SYSTEM_HEADER : (* pdwIntMediaSampleContent) = MPEG2_MEDIA_PROGRAM_SYSTEM_HEADER ; break ; case MPEG2_PROGRAM_PES_STREAM : (* pdwIntMediaSampleContent) = MPEG2_MEDIA_PES_STREAM ; break ; default : return E_INVALIDARG ; } ; return S_OK ; } // internal to external HRESULT TransportMediaSampleContentItoE ( IN DWORD dwIntMediaSampleContent, OUT DWORD * pExtMediaSampleContent // transport ) { ASSERT (pExtMediaSampleContent) ; switch (dwIntMediaSampleContent) { case MPEG2_MEDIA_TRANSPORT_PACKET : (* pExtMediaSampleContent) = MEDIA_TRANSPORT_PACKET ; break ; case MPEG2_MEDIA_ELEMENTARY_STREAM : (* pExtMediaSampleContent) = MEDIA_ELEMENTARY_STREAM ; break ; case MPEG2_MEDIA_TRANSPORT_PSI : (* pExtMediaSampleContent) = MEDIA_MPEG2_PSI ; break ; case MPEG2_MEDIA_TRANSPORT_PAYLOAD : (* pExtMediaSampleContent) = MEDIA_TRANSPORT_PAYLOAD ; break ; default : return E_INVALIDARG ; } ; return S_OK ; } HRESULT ProgramMediaSampleContentItoE ( IN DWORD dwIntMediaSampleContent, OUT DWORD * pExtMediaSampleContent // program ) { ASSERT (pExtMediaSampleContent) ; switch (dwIntMediaSampleContent) { case MPEG2_MEDIA_PROGRAM_STREAM_MAP : (* pExtMediaSampleContent) = MPEG2_PROGRAM_STREAM_MAP ; break ; case MPEG2_MEDIA_ELEMENTARY_STREAM : (* pExtMediaSampleContent) = MPEG2_PROGRAM_ELEMENTARY_STREAM ; break ; case MPEG2_MEDIA_PROGRAM_DIRECTORY_PES_PACKET : (* pExtMediaSampleContent) = MPEG2_PROGRAM_DIRECTORY_PES_PACKET ; break ; case MPEG2_MEDIA_PROGRAM_PACK_HEADER : (* pExtMediaSampleContent) = MPEG2_PROGRAM_PACK_HEADER ; break ; case MPEG2_MEDIA_PROGRAM_SYSTEM_HEADER : (* pExtMediaSampleContent) = MPEG2_PROGRAM_SYSTEM_HEADER ; break ; case MPEG2_MEDIA_PES_STREAM : (* pExtMediaSampleContent) = MPEG2_PROGRAM_PES_STREAM ; break ; default : return E_INVALIDARG ; } ; return S_OK ; } BOOL IsVideoMediaType ( IN AM_MEDIA_TYPE * pmt ) { return (pmt -> majortype == MEDIATYPE_Video ? TRUE : FALSE) ; } BOOL IsAudioMediaType ( IN AM_MEDIA_TYPE * pmt ) { return (pmt -> majortype == MEDIATYPE_Audio ? TRUE : FALSE) ; } BOOL IsAVMediaType ( IN AM_MEDIA_TYPE * pmt ) { return (IsVideoMediaType (pmt) || IsAudioMediaType (pmt) ? TRUE : FALSE) ; } LONG RegGetValIfExist ( IN HKEY hkeyRoot, IN LPCTSTR szValName, IN BOOL fCreateKey, OUT DWORD * pdw ) /*++ purpose: attempts to read the specified value from the passed in hkey; only sets * pdw if the value exists parameters: hkeyRoot hkey root where the value should exist szValName value name fCreateKey creates the key if it does not exist pdw pointer to receive the value, if it exists; only updated if the call succeeds return values: success ERROR_SUCCESS failure win32 error code --*/ { DWORD dw ; LONG l ; DWORD size ; DWORD type ; ASSERT (hkeyRoot) ; ASSERT (szValName) ; ASSERT (pdw) ; size = sizeof dw ; type = REG_DWORD ; l = RegQueryValueEx ( hkeyRoot, szValName, NULL, & type, (LPBYTE) & dw, & size ) ; // only set the passed in parameter's value if we opened the value ok if (l == ERROR_SUCCESS) { ASSERT (type == REG_DWORD) ; * pdw = dw ; } else if (fCreateKey) { l = RegSetValueEx ( hkeyRoot, szValName, NULL, REG_DWORD, (const BYTE *) pdw, sizeof (* pdw) ) ; } return l ; } LPWSTR AnsiToUnicode ( IN LPCSTR string, OUT LPWSTR buffer, IN DWORD buffer_len ) { buffer [0] = L'\0'; MultiByteToWideChar (CP_ACP, 0, string, -1, buffer, buffer_len); return buffer; } LPSTR UnicodeToAnsi ( IN LPCWSTR string, OUT LPSTR buffer, IN DWORD buffer_len ) { buffer [0] = '\0'; WideCharToMultiByte (CP_ACP, 0, string, -1, buffer, buffer_len, NULL, FALSE); return buffer; } BOOL StatsEnabled ( IN TCHAR * pszRegRoot ) { HKEY hkey ; DWORD dwDisposition ; DWORD dwStatsLevelCurrent ; #ifndef UNDER_CE DWORD dw ; #endif //UNDER_CE DWORD dwSize ; DWORD dwType ; LONG l ; BOOL fStatsEnabled ; ASSERT (pszRegRoot) ; // default to FALSE fStatsEnabled = FALSE ; // registry root is transport type l = RegCreateKeyEx ( HKEY_CURRENT_USER, pszRegRoot, NULL, NULL, REG_OPTION_NON_VOLATILE, (KEY_READ | KEY_WRITE), NULL, & hkey, & dwDisposition ) ; if (l == ERROR_SUCCESS) { // retrieve current dwSize = sizeof dwStatsLevelCurrent ; dwType = REG_DWORD ; l = RegQueryValueEx ( hkey, REG_STATS_LEVEL, NULL, & dwType, (LPBYTE) & dwStatsLevelCurrent, & dwSize ) ; if (l == ERROR_SUCCESS) { fStatsEnabled = (dwStatsLevelCurrent != REG_STATS_LEVEL_NONE ? TRUE : FALSE) ; } else { fStatsEnabled = FALSE ; } CLOSE_RESET_REG_KEY (hkey) ; } return fStatsEnabled ; } HRESULT EnableStats ( IN TCHAR * pszRegRoot, IN OUT BOOL * pfEnable ) { HKEY hkey ; DWORD dwDisposition ; DWORD dwStatsLevelDesired ; DWORD dwStatsLevelCurrent ; #ifndef UNDER_CE DWORD dw ; #endif //UNDER_CE DWORD dwSize ; DWORD dwType ; LONG l ; ASSERT (pszRegRoot) ; ASSERT (pfEnable) ; // registry root is transport type l = RegCreateKeyEx ( HKEY_CURRENT_USER, pszRegRoot, NULL, NULL, REG_OPTION_NON_VOLATILE, (KEY_READ | KEY_WRITE), NULL, & hkey, & dwDisposition ) ; if (l == ERROR_SUCCESS) { // retrieve current dwSize = sizeof dwStatsLevelCurrent ; dwType = REG_DWORD ; l = RegQueryValueEx ( hkey, REG_STATS_LEVEL, NULL, & dwType, (LPBYTE) & dwStatsLevelCurrent, & dwSize ) ; // if we failed to read the value, default to none dwStatsLevelCurrent = (l == ERROR_SUCCESS ? dwStatsLevelCurrent : REG_STATS_LEVEL_NONE) ; // this is what we're now going to set dwStatsLevelDesired = ((* pfEnable) ? REG_STATS_LEVEL_ENABLED : REG_STATS_LEVEL_NONE) ; l = RegSetValueEx ( hkey, REG_STATS_LEVEL, NULL, REG_DWORD, (const BYTE *) & dwStatsLevelDesired, sizeof dwStatsLevelDesired ) ; // set outgoing (* pfEnable) = (dwStatsLevelCurrent != REG_STATS_LEVEL_NONE ? TRUE : FALSE) ; CLOSE_RESET_REG_KEY (hkey) ; } return (l == ERROR_SUCCESS ? S_OK : E_FAIL) ; } // ---------------------------------------------------------------------------- // CMpeg2ProgramStreamSniffer // ---------------------------------------------------------------------------- // returns the offset of the first packet with specified stream_id HRESULT CMpeg2ProgramStreamSniffer::SeekToNextPacket ( IN OUT LONGLONG * pllStreamOffset, // IN: start (inclusive); OUT: found OUT BYTE ** ppbPESPacket, // pointer to a buffer with the packet OUT LONG * plBufferLength // available buffer ) { LONG lWindowLen ; LONG lWindowRemaining ; LONG lPacketLength ; BYTE * pb ; BOOL r ; for (;;) { // make the window the max lWindowLen = SCRATCH_BUFFER_LENGTH ; // set the window pb = m_CIAsyncReader.SetWindow ( (* pllStreamOffset), & lWindowLen) ; // if the startingoffset is off, or we don't have a complete packet // remaining in the file, fail out if (!pb || lWindowLen < PES_COMMON_HEADER_FIELD_LEN) { return E_FAIL ; } // we start with a full buffer lWindowRemaining = lWindowLen ; // seek down into it r = SeekToPrefix ( & pb, reinterpret_cast (& lWindowRemaining) ) ; // advance the file offset (* pllStreamOffset) += (lWindowLen - lWindowRemaining) ; if (r && lWindowRemaining >= PES_COMMON_HEADER_FIELD_LEN) { // found a prefix break ; } } // found a prefix ASSERT (IsStartCodePrefix (pb)) ; ASSERT (lWindowRemaining >= PES_COMMON_HEADER_FIELD_LEN) ; // make sure we have the entire packet in lPacketLength = PESPacketLength (pb) ; if (lPacketLength > lWindowRemaining) { // go for a big read lWindowRemaining = SCRATCH_BUFFER_LENGTH ; pb = m_CIAsyncReader.SetWindow ( (* pllStreamOffset), & lWindowRemaining ) ; if (!pb || lWindowRemaining < lPacketLength) { // failed to read in the entire packet return E_FAIL ; } } // set outgoing (* ppbPESPacket) = pb ; (* plBufferLength) = lWindowRemaining ; // success return S_OK ; } HRESULT CMpeg2ProgramStreamSniffer::StepToNextPacketStrict ( IN OUT LONGLONG * pllStreamOffset, // IN: start (inclusive); OUT: found OUT BYTE ** ppbPESPacket, // pointer to a buffer with the packet OUT LONG * plBufferLength // available buffer ) { BYTE * pb ; LONG lBufferLength ; LONG lPacketLength ; // set the window so we have the current packet lBufferLength = PES_COMMON_HEADER_FIELD_LEN ; // gives us enough to learn length pb = m_CIAsyncReader.SetWindow ( (* pllStreamOffset), & lBufferLength ) ; if (!pb || // failed the call lBufferLength < PES_COMMON_HEADER_FIELD_LEN || // insufficient buffer !IsStartCodePrefix (pb)) { // not a prefix return E_FAIL ; } // advance to what should be a prefix lPacketLength = PESPacketLength (pb) ; (* pllStreamOffset) += lPacketLength ; // advance lBufferLength -= lPacketLength ; // less buffer remaining // reset the window if we don't have the entire packet + enough of the // next to get at least its length if (lBufferLength < lPacketLength + PES_COMMON_HEADER_FIELD_LEN) { // go for a big read lBufferLength = SCRATCH_BUFFER_LENGTH ; pb = m_CIAsyncReader.SetWindow ( (* pllStreamOffset), & lBufferLength ) ; if (!pb || lBufferLength < PES_COMMON_HEADER_FIELD_LEN) { // failed to read in enough to do anything with return E_FAIL ; } } else { // otherwise just advance the pointer we got back pb += lPacketLength ; // advance } if (!IsStartCodePrefix (pb)) { // we stepped and do not point to another prefix; unknown file type return VFW_E_UNKNOWN_FILE_TYPE ; } // we now are looking at the next packet; get its length lPacketLength = PESPacketLength (pb) ; // read in the entire packet if we don't have it if (lBufferLength < lPacketLength) { // go for a big read lBufferLength = SCRATCH_BUFFER_LENGTH ; pb = m_CIAsyncReader.SetWindow ( (* pllStreamOffset), & lBufferLength ) ; if (!pb || lBufferLength < lPacketLength) { // failed to read in the entire packet return E_FAIL ; } } // set outgoing (* ppbPESPacket) = pb ; (* plBufferLength) = lBufferLength ; // success return S_OK ; } HRESULT CMpeg2ProgramStreamSniffer::StepToNextPacketNonStrict ( IN OUT LONGLONG * pllStreamOffset, // IN: start (inclusive); OUT: found OUT BYTE ** ppbPESPacket, // pointer to a buffer with the packet OUT LONG * plBufferLength // available buffer ) { HRESULT hr ; hr = StepToNextPacketStrict ( pllStreamOffset, ppbPESPacket, plBufferLength ) ; if (hr == VFW_E_UNKNOWN_FILE_TYPE) { // strict call failed due to missing prefix; we now try to seek // forward from the point where we expected a prefix; above call, // even in failure, has updated the stream offset, so pllStreamOffset // now points to our starting stream offset: where we thought the // next prefix should have been hr = SeekToNextPacket ( pllStreamOffset, ppbPESPacket, plBufferLength ) ; } return hr ; } LONG CMpeg2ProgramStreamSniffer::PESPacketLength ( IN BYTE * pbCurrent ) { LONG l ; // pbCurrent must point to buffer that is >= MIN_PROCESS_LEN !! switch (StartCode (pbCurrent)) { case MPEG2_PACK_START_CODE : l = PACK_HEADER_CORE_LEN + PACK_STUFFING_LENGTH (pbCurrent) ; break ; default : l = PES_PACKET_LENGTH_LAST_BYTE_OFFSET + PACKET_LENGTH_VALUE (pbCurrent) ; break ; } ; return l ; } LONG CMpeg2ProgramStreamSniffer::PESHeaderLength ( IN BYTE * pbPESHeader ) { BYTE bStartCode ; BYTE * pbCurrent ; LONG PES_header_data_length ; LONG l ; ASSERT (IsStartCodePrefix (pbPESHeader)) ; bStartCode = StartCode (pbPESHeader) ; switch (bStartCode) { case MPEG2_PACK_START_CODE : case MPEG2_SYSTEM_HEADER_START_CODE : l = PESPacketLength (pbPESHeader) ; break ; default : if (!IS_TIER1_STREAM (bStartCode)) { // not a tier-1 header; so header is a core header only l = PES_COMMON_HEADER_FIELD_LEN ; } else { // tier1 header; we're going to have to figure out how long .. pbCurrent = pbPESHeader ; // advance to tier-1 fields pbCurrent += PES_COMMON_HEADER_FIELD_LEN ; // get the size of optional fields in header PES_header_data_length = PES_PES_HEADER_DATA_LENGTH_VALUE (pbCurrent) ; // add the above to tier1 required header fields l = PES_TIER1_REQ_HEADER_FIELD_LEN + PES_header_data_length ; } break ; } ; return l ; } LONGLONG CMpeg2ProgramStreamSniffer::PackSCR ( IN BYTE * pbPackHeader ) { LONGLONG llSCR ; LONGLONG system_clock_reference_base ; LONGLONG system_clock_reference_extension ; ASSERT (pbPackHeader) ; if (IsStartCodePrefix (pbPackHeader) && StartCode (pbPackHeader) == MPEG2_PACK_START_CODE) { // get the first SCR system_clock_reference_base = PACK_HEADER_SCR_BASE (pbPackHeader) ; system_clock_reference_extension = PACK_HEADER_SCR_EXT (pbPackHeader) ; // equation 2-1, H.222.0 llSCR = system_clock_reference_base * 300 + system_clock_reference_extension ; } else { llSCR = UNDEFINED ; } return llSCR ; } DWORD CMpeg2ProgramStreamSniffer::PackMuxRate ( IN BYTE * pbPackHeader ) { DWORD dwMuxRate ; ASSERT (pbPackHeader) ; if (IsStartCodePrefix (pbPackHeader) && StartCode (pbPackHeader) == MPEG2_PACK_START_CODE) { dwMuxRate = PACK_PROGRAM_MUX_RATE (pbPackHeader) ; } else { dwMuxRate = UNDEFINED ; } return dwMuxRate ; } // ============================================================================ // ============================================================================ // ============================================================================ CAnalogCopyProtection::CAnalogCopyProtection(IBaseFilter *pFilter) : m_pFilter(pFilter), m_bVRNoKsPS(FALSE), m_dwLastCPBits(0), m_KsPSList(NAME("DVD Internal List"), 20) { DbgLog((LOG_TRACE, 5, TEXT("CAnalogCopyProtection::CAnalogCopyProtection()"))) ; m_bDateExpired = TRUE ; // aways enablel OSVERSIONINFO osvi ; ZeroMemory(&osvi, sizeof(OSVERSIONINFO)) ; osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO) ; if (! GetVersionEx(&osvi) ) { DbgLog((LOG_ERROR, 1, TEXT("ERROR: GetVersionInfo() failed (Error %ld)"), GetLastError())) ; m_bVerNT5 = TRUE ; // to be safe about MV } else // got the OS version info { m_bVerNT5 = (VER_PLATFORM_WIN32_NT == osvi.dwPlatformId && 5 == osvi.dwMajorVersion) ; } if (NULL == pFilter) { DbgLog((LOG_ERROR, 0, TEXT("ERROR: NULL filter passed to CAnalogCopyProtection c-tor"))) ; return ; } } CAnalogCopyProtection::~CAnalogCopyProtection(void) { DbgLog((LOG_TRACE, 5, TEXT("CAnalogCopyProtection::~CAnalogCopyProtection()"))) ; FreeACPFilterList() ; // most probably don't need, but... ??? } // // Create a list of filters that support IKsPropertySet interface so that // the analog copy protection values can be distributed to them. It also // detects if any custom (non-MS) Video Renderer is present in the graph; // we care because that could be used to bypass our analog copy protection // distribution scheme. // HRESULT CAnalogCopyProtection::MakeACPFilterList(void) { DbgLog((LOG_TRACE, 5, TEXT("CAnalogCopyProtection::MakeACPFilterList()"))) ; HRESULT hr = NOERROR ; FILTER_INFO fi ; hr = m_pFilter->QueryFilterInfo(&fi) ; ASSERT(SUCCEEDED(hr) && NULL != fi.pGraph) ; if (fi.pGraph) { // // Enumerate all the filters in the graph and look for those // supporting IKsPropertySet interface. We also look for the // filters that support IBasicVideo/IVideoWindow as they may // be the Video Renderer. // IEnumFilters *pEnumFilters ; hr = fi.pGraph->EnumFilters(&pEnumFilters) ; if (SUCCEEDED(hr) && pEnumFilters) { // Just reset the flag before enum-ing the filters m_bVRNoKsPS = FALSE ; DbgLog((LOG_TRACE, 5, TEXT("Analog copy protection filter enumeration starting..."))) ; ULONG ul ; IBaseFilter *pFilter ; IKsPropertySet *pKsPS ; IVideoWindow *pVR ; IPin *pPin ; ULONG ulTypeSupport ; FILTER_INFO fi2 ; TCHAR achName[MAX_PATH] ; BOOL bPinDoesKsPS ; IEnumPins *pEnumPins ; while (S_OK == pEnumFilters->Next(1, &pFilter, &ul) && 1 == ul) { // // We first want to see if the pins of this filter supports // IKsPropertySet. If it does, we want to keep the pin(s) // on the list rather than the filter. // hr = pFilter->EnumPins(&pEnumPins) ; ASSERT(SUCCEEDED(hr)) ; bPinDoesKsPS = FALSE ; while (S_OK == pEnumPins->Next(1, &pPin, &ul) && 1 == ul) { #ifndef UNDER_CE if (SUCCEEDED(pPin->QueryInterface(IID_IKsPropertySet, (LPVOID *)&pKsPS))) { DbgLog((LOG_TRACE, 5, TEXT("Pin %s supports IKsPropertySet"), (LPCTSTR)CDisp(pPin))) ; if ( S_OK == pKsPS->QuerySupported(AM_KSPROPSETID_CopyProt, AM_PROPERTY_COPY_MACROVISION, &ulTypeSupport) && (ulTypeSupport & KSPROPERTY_SUPPORT_SET) ) { DbgLog((LOG_TRACE, 5, TEXT("QuerySupported() for ACP returned OK"))) ; if(m_KsPSList.AddTail(pKsPS)) { // add the pin to list pKsPS->AddRef(); } bPinDoesKsPS = TRUE ; } else { DbgLog((LOG_TRACE, 5, TEXT("QuerySupported() for ACP FAILED"))) ; } pKsPS->Release(); } #else // !UNDER_CE // On CE 5.0 the overlay mixer incorrectly returns an interface pointer // when queried for IKsPropertySet, but any calls on that returned // interface are bogus as the class doesn't implementt the interface // As we are ignoring analog copy protection at the moment we can just comment // out this code to avoid any crashes #endif // !UNDER_CE pPin->Release() ; // let the pin go now } pEnumPins->Release() ; if (! bPinDoesKsPS ) // if pin doesn't do IKsPropertySet, { // check the filter if (SUCCEEDED(pFilter->QueryInterface(IID_IKsPropertySet, (LPVOID *)&pKsPS))) { pFilter->QueryFilterInfo(&fi2) ; #ifdef UNICODE ::lstrcpy(achName, fi2.achName) ; #else ::WideCharToMultiByte(CP_ACP, 0, fi2.achName, -1, achName, 100, NULL, NULL) ; #endif // UNICODE fi2.pGraph->Release() ; DbgLog((LOG_TRACE, 5, TEXT("Filter %s supports IKsPropertySet"), achName)) ; if ( S_OK == pKsPS->QuerySupported(AM_KSPROPSETID_CopyProt, AM_PROPERTY_COPY_MACROVISION, &ulTypeSupport) && (ulTypeSupport & KSPROPERTY_SUPPORT_SET) ) { DbgLog((LOG_TRACE, 5, TEXT("QuerySupported() for ACP returned OK"))) ; if (m_KsPSList.AddTail(pKsPS)) { // add this filter to the list pKsPS->AddRef(); } } else { DbgLog((LOG_TRACE, 5, TEXT("QuerySupported() for ACP FAILED"))) ; } pKsPS->Release(); } else if (SUCCEEDED(pFilter->QueryInterface(IID_IVideoWindow, (LPVOID *) &pVR)) || SUCCEEDED(pFilter->QueryInterface(IID_IBasicVideo, (LPVOID *) &pVR))) { DbgLog((LOG_ERROR, 0, TEXT("WARNING: A video rendering filter does NOT supports IKsPropertySet"))) ; // // it's a video renderer filter that doesn't support IKsPropertySet, // i.e, not our Video Renderer and hence a potential candidate for // analog copy protection bypassing effort. Remember this info (we want to // NOT to playback ONLY IF we get copy-protected content). // m_bVRNoKsPS = TRUE ; pVR->Release() ; } } // end of if (! bPinDoesKsPS) pFilter->Release() ; // done now -- let it go. } DbgLog((LOG_TRACE, 5, TEXT("Analog copy protection filter enumeration over"))) ; pEnumFilters->Release() ; // filter enum complete // // Always set/init ACP level to 0 before doing anything else // DistributeAnalogCopyProtection(0) ; } else { DbgLog((LOG_ERROR, 0, TEXT("ERROR: EnumFilters() failed (Error 0x%lx)"), hr)) ; hr = E_UNEXPECTED ; } fi.pGraph->Release() ; // else we'll leak } else { DbgLog((LOG_ERROR, 0, TEXT("ERROR: This (DVD Nav) filter is not in any graph!!"))) ; hr = E_UNEXPECTED ; } return hr ; } // // Empty the list and release the interfaces // HRESULT CAnalogCopyProtection::FreeACPFilterList(void) { DbgLog((LOG_TRACE, 5, TEXT("CAnalogCopyProtection::FreeACPFilterList()"))) ; // // Remove the filters from the distirbution list and release them. // IKsPropertySet *pKsPS ; do { pKsPS = m_KsPSList.RemoveHead() ; if (pKsPS) pKsPS->Release() ; } while (pKsPS) ; return NOERROR ; // success } // // Send analog copy protection values to all the enumerated filters. // HRESULT CAnalogCopyProtection::DistributeAnalogCopyProtection(DWORD dwCPBits) { DbgLog((LOG_TRACE, 5, TEXT("CAnalogCopyProtection::DistributeAnalogCopyProtection()"))) ; HRESULT hrFinal = NOERROR ; HRESULT hr ; POSITION pos = m_KsPSList.GetHeadPosition() ; while (pos) { IKsPropertySet *pKsPS = m_KsPSList.GetNext(pos) ; // // Should we also do a Get() to verify that this filter is not just // faking CSS compliance by returning success for the following call? // hr = pKsPS->Set(AM_KSPROPSETID_CopyProt, AM_PROPERTY_COPY_MACROVISION, NULL, 0, &dwCPBits, sizeof(dwCPBits)) ; if (FAILED(hr)) { DbgLog((LOG_ERROR, 0, TEXT("Failed IKsPropSet::Set() for analog copy protection (Error 0x%lx)"), hr)) ; if (SUCCEEDED(hrFinal)) hrFinal = hr ; // Should we jump out of the loop, because anyway we'll report // error to the caller and that will stop playback (??) } } if (SUCCEEDED(hrFinal)) { DbgLog((LOG_TRACE, 5, TEXT("Analog copy protection distribution succedded"))) ; m_dwLastCPBits = dwCPBits ; } else DbgLog((LOG_ERROR, 0, TEXT("Analog copy protection distribution failed"))) ; return hrFinal ; }