//------------------------------------------------------------------------------
//
// 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 ;
}