Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

2527 lines
75 KiB

//==========================================================================;
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
// PURPOSE.
//
// Copyright (C) Microsoft Corporation, 1992 - 1999 All Rights Reserved.
//
//--------------------------------------------------------------------------;
//
// ctvtuner.cpp Class that encapsulates knowledge of Broadcast, ATSC,
// FM, AM, and (someday?) DSS tuning
//
// Some basic assumptions:
//
// 1) The "country" setting is global to all subtuners
// 2) The VideoFormat list is unique to each subtuner
// 3) The OptimalTuning list is global to all tuners
// 4) Input connections are global to all tuners
//
#include <streams.h> // quartz, includes windows
#include <measure.h> // performance measurement (MSR_)
#include <devioctl.h>
#include <ks.h>
#include <ksmedia.h>
#include <ksproxy.h>
#include "amkspin.h"
#include "kssupp.h"
#include "ctvtuner.h"
#include "tvtuner.h"
// define this to enable the 1MHz linear search mechanism to be delivered with the test kit
#undef LINEAR_SEARCH_FOR_TEST_KIT
// -------------------------------------------------------------------------
// CTunerMode
//
// Virtual members are generally used for AM and FM
// TV overrides
// -------------------------------------------------------------------------
CTunerMode::CTunerMode(CTVTunerFilter *pFilter
, CTVTuner *pTVTuner
, long Mode
, long lChannel
, long lVideoSubChannel
, long lAudioSubChannel
, long ChannelStep)
: m_pFilter (pFilter)
, m_pTVTuner(pTVTuner)
, m_Mode (Mode)
, m_Active (FALSE)
, m_lCountryCode (-1) // Init to an illegal value
, m_lChannel(lChannel)
, m_lVideoSubChannel(lVideoSubChannel)
, m_lAudioSubChannel(lAudioSubChannel)
, m_lVideoCarrier(0)
, m_lAudioCarrier(0)
, m_InAutoTune (reinterpret_cast<void*>(FALSE))
, m_ChannelStep (ChannelStep)
{
}
HRESULT
CTunerMode::Init(void)
{
ULONG cbReturned;
BOOL fOk;
int j;
ZeroMemory (&m_ModeCaps, sizeof (m_ModeCaps));
ZeroMemory (&m_Frequency, sizeof (m_Frequency));
ZeroMemory (&m_Status, sizeof (m_Status));
// Get the capabilities for this mode
m_ModeCaps.Property.Set = PROPSETID_TUNER;
m_ModeCaps.Property.Id = KSPROPERTY_TUNER_MODE_CAPS;
m_ModeCaps.Property.Flags = KSPROPERTY_TYPE_GET;
m_ModeCaps.Mode = m_Mode;
fOk = KsControl(m_pTVTuner->Device(),
(DWORD) IOCTL_KS_PROPERTY,
&m_ModeCaps, sizeof( m_ModeCaps),
&m_ModeCaps, sizeof( m_ModeCaps),
&cbReturned);
if (!fOk) {
DbgLog(( LOG_ERROR, 0,
TEXT("FAILED: KSPROPERTY_TUNER_MODE_CAPS, KSPROPERTY_TYPE_GET, cbReturned = %d"), cbReturned));
return E_FAIL;
}
// Create a local version of TVTunerFormatCaps to
// allow subtuners with different capabilities
for (j = 0; j < NUM_TVTUNER_FORMATS; j++) {
m_TVTunerFormatCaps[j] = TVTunerFormatCaps[j];
}
TVFORMATINFO * pTVInfo = m_TVTunerFormatCaps;
// Walk the list of supported formats and set the flag in the table if supported
for (j = 0; j < NUM_TVTUNER_FORMATS; j++, pTVInfo++) {
if (pTVInfo->AVStandard & m_ModeCaps.StandardsSupported) {
pTVInfo->fSupported = TRUE;
}
else {
pTVInfo->fSupported = FALSE;
}
}
return S_OK;
}
CTunerMode::~CTunerMode()
{
}
HRESULT
CTunerMode::HW_Tune( long VideoCarrier,
long AudioCarrier)
{
BOOL fOk;
ULONG cbReturned;
m_Frequency.Property.Set = PROPSETID_TUNER;
m_Frequency.Property.Id = KSPROPERTY_TUNER_FREQUENCY;
m_Frequency.Property.Flags = KSPROPERTY_TYPE_SET;
m_Frequency.Frequency = VideoCarrier;
m_Frequency.LastFrequency = VideoCarrier; // not used
m_Frequency.TuningFlags = KS_TUNER_TUNING_EXACT;
m_Frequency.Channel = m_lChannel;
m_Frequency.VideoSubChannel = m_lVideoSubChannel;
m_Frequency.AudioSubChannel = m_lAudioSubChannel;
m_Frequency.Country = m_lCountryCode;
fOk = KsControl(m_pTVTuner->Device(),
(DWORD) IOCTL_KS_PROPERTY,
&m_Frequency, sizeof(m_Frequency),
&m_Frequency, sizeof(m_Frequency),
&cbReturned);
if (!fOk) {
DbgLog(( LOG_ERROR, 0,
TEXT("FAILED: KSPROPERTY_TUNER_FREQUENCY, KSPROPERTY_TYPE_SET, cbReturned = %d"), cbReturned));
}
return fOk ? S_OK : S_FALSE;
}
HRESULT
CTunerMode::HW_GetStatus ()
{
BOOL fOk;
ULONG cbReturned;
KSPROPERTY_TUNER_STATUS_S Status;
Status.Property.Set = PROPSETID_TUNER;
Status.Property.Id = KSPROPERTY_TUNER_STATUS;
Status.Property.Flags = KSPROPERTY_TYPE_GET;
fOk = KsControl(m_pTVTuner->Device(),
(DWORD) IOCTL_KS_PROPERTY,
&Status, sizeof( Status),
&Status, sizeof( Status),
&cbReturned);
if (fOk) {
m_Status = Status;
}
else {
DbgLog(( LOG_ERROR, 0, TEXT("FAILED: KSPROPERTY_TUNER_STATUS, KSPROPERTY_TYPE_GET, cbReturned = %d"), cbReturned));
}
return fOk ? S_OK : S_FALSE;
}
HRESULT
CTunerMode::HW_SetVideoStandard( long lVideoStandard)
{
BOOL fOk;
ULONG cbReturned;
KSPROPERTY_TUNER_STANDARD_S Standard;
Standard.Property.Set = PROPSETID_TUNER;
Standard.Property.Id = KSPROPERTY_TUNER_STANDARD;
Standard.Property.Flags = KSPROPERTY_TYPE_SET;
Standard.Standard = lVideoStandard;
fOk = KsControl(m_pTVTuner->Device(),
(DWORD) IOCTL_KS_PROPERTY,
&Standard, sizeof(Standard),
&Standard, sizeof(Standard),
&cbReturned);
if (!fOk) {
DbgLog(( LOG_ERROR, 0,
TEXT("FAILED: KSPROPERTY_TUNER_STANDARD, KSPROPERTY_TYPE_SET, cbReturned = %d"), cbReturned));
}
return fOk ? S_OK : S_FALSE;
}
STDMETHODIMP
CTunerMode::put_Channel(
long lChannel,
long lVideoSubChannel,
long lAudioSubChannel)
{
m_lChannel = lChannel;
m_lVideoSubChannel = lVideoSubChannel;
m_lAudioSubChannel = lAudioSubChannel;
return HW_Tune(m_lChannel, m_lChannel);
}
STDMETHODIMP
CTunerMode::get_Channel(
long * plChannel,
long * plVideoSubChannel,
long * plAudioSubChannel)
{
*plChannel = m_lChannel;
*plVideoSubChannel = m_lVideoSubChannel;
*plAudioSubChannel = m_lAudioSubChannel;
return NOERROR;
}
STDMETHODIMP
CTunerMode::ChannelMinMax(long *plChannelMin, long *plChannelMax)
{
// Total hack follows. Only the CTVTuner class knows about the
// step to get to adjacent frequencies, and this is not exposed
// anywhere in the COM interface. As a special case for
// ChannelMinMax, if both the min and max values point at the same
// location (which would normally be an app bug), then return
// the UI step value instead.
if (plChannelMin == plChannelMax) {
*plChannelMin = GetChannelStep();
}
else {
// For non TV Modes, the MinMax can be found directly from the Caps
*plChannelMin = m_ModeCaps.MinFrequency;
*plChannelMax = m_ModeCaps.MaxFrequency;
}
return NOERROR;
}
STDMETHODIMP
CTunerMode::AutoTune (long lChannel, long * plFoundSignal)
{
return S_OK;
}
STDMETHODIMP
CTunerMode::StoreAutoTune ()
{
return S_OK;
}
//
// In general, the possible return values are:
// AMTUNER_HASNOSIGNALSTRENGTH = -1,
// AMTUNER_NOSIGNAL = 0,
// AMTUNER_SIGNALPRESENT = 1
//
// But the virtual case for AM/FM gets the actual signal strength
// from the hardware.
STDMETHODIMP
CTunerMode::SignalPresent(
/* [out] */ long *plSignalStrength)
{
HW_GetStatus ();
*plSignalStrength = m_Status.SignalStrength;
return NOERROR;
}
// Returns the "Mode" of this subtuner, not the mode of the overall tuner!!!
STDMETHODIMP
CTunerMode::get_Mode(/* [in] */ AMTunerModeType *plMode)
{
*plMode = (AMTunerModeType) m_Mode;
return NOERROR;
}
//
// All subtuners are informed of global tuner mode changes
//
STDMETHODIMP
CTunerMode::put_Mode(/* [in] */ AMTunerModeType lMode)
{
BOOL fOk;
KSPROPERTY_TUNER_MODE_S TunerMode;
ULONG cbReturned;
m_Active = (lMode == m_Mode);
if (m_Active) {
TunerMode.Property.Set = PROPSETID_TUNER;
TunerMode.Property.Id = KSPROPERTY_TUNER_MODE;
TunerMode.Property.Flags = KSPROPERTY_TYPE_SET;
TunerMode.Mode = m_Mode;
fOk = KsControl(m_pTVTuner->Device(),
(DWORD) IOCTL_KS_PROPERTY,
&TunerMode, sizeof( KSPROPERTY_TUNER_MODE_S),
&TunerMode, sizeof( KSPROPERTY_TUNER_MODE_S),
&cbReturned);
if (fOk) {
return put_Channel (m_lChannel, m_lVideoSubChannel, m_lAudioSubChannel);
}
else {
DbgLog(( LOG_ERROR, 0,
TEXT("FAILED: KSPROPERTY_TUNER_MODE, KSPROPERTY_TYPE_SET, cbReturned = %d"), cbReturned));
return E_INVALIDARG;
}
}
return S_OK;
}
STDMETHODIMP
CTunerMode::get_VideoFrequency (long * plFreq)
{
*plFreq = m_lVideoCarrier;
return NOERROR;
}
STDMETHODIMP
CTunerMode::get_AudioFrequency (long * plFreq)
{
*plFreq = m_lAudioCarrier;
return NOERROR;
}
STDMETHODIMP
CTunerMode::get_AvailableTVFormats (long * plAnalogVideoStandard)
{
*plAnalogVideoStandard = m_ModeCaps.StandardsSupported;
return NOERROR;
}
STDMETHODIMP
CTunerMode::get_TVFormat (long *plAnalogVideoStandard)
{
// This will be zero for AM and FM, so don't ASSERT (m_TVFormatInfo.AVStandard);
*plAnalogVideoStandard = m_TVFormatInfo.AVStandard;
return NOERROR;
}
STDMETHODIMP
CTunerMode::put_CountryCode(long lCountryCode)
{
HRESULT hr = S_OK;
m_lCountryCode = lCountryCode;
if (m_Active) {
hr = put_Channel (m_lChannel, m_lVideoSubChannel, m_lAudioSubChannel);
}
return hr;
}
// The fForce parameter forces the first enumerated videostandard
// to be selected
BOOL
CTunerMode::SetTVFormatInfo(
AnalogVideoStandard AVStandard,
TVFORMATINFO *pTVFormatInfo,
BOOL fForce)
{
TVFORMATINFO * pTVInfo = m_TVTunerFormatCaps;
// Walk the list of supported formats
for (int j = 0; j < NUM_TVTUNER_FORMATS; j++, pTVInfo++) {
if (pTVInfo->fSupported == TRUE) {
if ((pTVInfo->AVStandard == AVStandard) || fForce) {
*pTVFormatInfo = *pTVInfo;
return TRUE;
}
}
}
return FALSE;
}
// -------------------------------------------------------------------------
// CTunerMode_AMFM
// -------------------------------------------------------------------------
STDMETHODIMP
CTunerMode_AMFM::put_Channel(long lChannel, long, long)
{
long junkFoundSignal;
return AutoTune(lChannel, &junkFoundSignal);
}
/* Search either up or down from the given frequency looking
* for a perfect lock. If we are close on the first call, call
* ourselves recursively, noting the trend.
*
* There is a limit on the number of times we can recurse. If
* the new trend conflicts with the previous trend, then we must
* have hopped over the "perfect" frequency, in which case the
* most recent frequency will be considered a perfect lock.
*/
BOOL
CTunerMode_AMFM::SearchNeighborhood(
long freq,
TuningTrend trend = AdjustingNone,
int depth = 0
)
{
BOOL rc = FALSE;
#ifdef DEBUG
LPTSTR lpszTrend;
switch (trend)
{
case AdjustingUp:
lpszTrend = TEXT("Adjusting Up");
break;
case AdjustingDown:
lpszTrend = TEXT("Adjusting Down");
break;
case AdjustingNone:
lpszTrend = TEXT("Initial Try");
break;
}
DbgLog(
( LOG_TRACE, 2
, TEXT("SearchNeighborhood(freq = %d) %s")
, freq
, lpszTrend
)
);
#endif
if (depth > SearchLimit)
{
DbgLog(
( LOG_TRACE, 2
, TEXT("Recursion limit reached, bailing out")
)
);
return FALSE;
}
/* Set the Video and Audio frequencies to be the same
*/
m_lVideoCarrier = m_lAudioCarrier = freq;
/* Check if the frequencies are beyond the limits of the tuner
*/
if (m_lVideoCarrier < (long) m_ModeCaps.MinFrequency || m_lVideoCarrier > (long) m_ModeCaps.MaxFrequency)
{
DbgLog(
( LOG_TRACE, 2
, TEXT("Frequency out of range")
)
);
return FALSE;
}
/* Try the given frequencies without adjustment
*/
HRESULT hr = HW_Tune(m_lVideoCarrier, m_lAudioCarrier);
if (hr == S_OK)
{
// In order to improve frequency search times, the gross assumption here is that
// the SettlingTime must be honored for large jumps, but that smaller frequency
// changes (made by us, incidentally) can get by with smaller settling times.
if (AdjustingNone == trend)
Sleep(m_ModeCaps.SettlingTime);
else
Sleep(5);
// For some reason, the Philips FR1236 returns Busy (FL == 0) for a much longer time
// than it should. Keep looping, hoping it will become unbusy.
for (int j = 0; j < 5; j++) {
hr = HW_GetStatus();
if (hr == S_OK && !m_Status.Busy) {
break;
}
Sleep(5);
}
DbgLog(( LOG_TRACE, 5, TEXT("TUNING: PLLOffset = %d, Busy = %d"), (LONG) m_Status.PLLOffset, (LONG) m_Status.Busy));
if (hr == S_OK && !m_Status.Busy)
{
/* Act according to the tuning trend (if any)
*/
switch (trend)
{
case AdjustingNone:
switch ((LONG) m_Status.PLLOffset)
{
case 1: // Need to adjust up
rc = SearchNeighborhood
( freq + m_ModeCaps.TuningGranularity
, AdjustingUp, depth+1
);
break;
case -1: // Need to adjust down
rc = SearchNeighborhood
( freq - m_ModeCaps.TuningGranularity
, AdjustingDown, depth+1
);
break;
case 0: // Perfect
rc = TRUE;
break;
default: // Just plain missed
rc = FALSE;
}
break;
case AdjustingUp:
switch ((LONG) m_Status.PLLOffset)
{
case 1: // Close but still low
rc = SearchNeighborhood
( freq + m_ModeCaps.TuningGranularity
, AdjustingUp, depth+1
);
break;
case -1: // Switched trend
case 0: // Perfect
rc = TRUE;
break;
default: // Something is very wrong
rc = FALSE;
}
break;
case AdjustingDown:
switch ((LONG) m_Status.PLLOffset)
{
case -1: // Close but still high
rc = SearchNeighborhood
(freq - m_ModeCaps.TuningGranularity
, AdjustingDown, depth+1
);
break;
case 1: // Switched trend
case 0: // Perfect
rc = TRUE;
break;
default: // Something is very wrong
rc = FALSE;
}
break;
}
}
else
m_Status.PLLOffset = 100; // set to a really bad value
}
return rc;
}
STDMETHODIMP
CTunerMode_AMFM::AutoTune (long lChannel, long * plFoundSignal)
{
long SignalStrength = AMTUNER_NOSIGNAL;
// Set a flag so everybody else knows we're in the midst of an AutoTune operation
if (InterlockedCompareExchangePointer(&m_InAutoTune, reinterpret_cast<void*>(TRUE), reinterpret_cast<void*>(FALSE)) != reinterpret_cast<void*>(FALSE))
return S_FALSE; // already set to TRUE, don't interrupt the current autotune
m_lChannel = lChannel;
m_lVideoSubChannel = -1;
m_lAudioSubChannel = -1;
DbgLog(( LOG_TRACE, 2, TEXT("Start AutoTune(channel = %d)"), m_lChannel));
HRESULT hr = AutoTune();
if (NOERROR == hr)
SignalPresent(&SignalStrength);
// Assume AMTUNER_HASNOSIGNALSTRENGTH means tuned
*plFoundSignal = (SignalStrength != AMTUNER_NOSIGNAL);
InterlockedExchangePointer(&m_InAutoTune, reinterpret_cast<void*>(FALSE));
return hr;
}
HRESULT
CTunerMode_AMFM::AutoTune(void)
{
BOOL bFoundSignal = FALSE;
long freq = m_lChannel;
// if the driver has the tuning logic, let it do the hard part
if (m_ModeCaps.Strategy == KS_TUNER_STRATEGY_DRIVER_TUNES) {
// Set the Video and Audio frequencies to the same value
m_lVideoCarrier = m_lAudioCarrier = freq;
// Try the given frequencies without adjustment
HRESULT hr = HW_Tune(m_lVideoCarrier, m_lAudioCarrier);
if (hr == S_OK) {
DbgLog(( LOG_TRACE, 5, TEXT("TUNING: Started")));
Sleep(m_ModeCaps.SettlingTime);
DbgLog(( LOG_TRACE, 5, TEXT("TUNING: Finished")));
}
else {
DbgLog(( LOG_TRACE, 5, TEXT("TUNING: Unsuccessful")));
}
return hr;
}
// Broadcast (Antenna)
bFoundSignal = SearchNeighborhood(freq); // anchor the search at the default
if (!bFoundSignal) {
DbgLog(( LOG_TRACE, 5, TEXT("TUNING: Starting Exhaustive Search")));
/* The default frequency isn't working. Take the brute-force
* approach.
*
* Search through a band around the target frequency, ending
* up at the default target frequency if no signal is found.
*/
long halfband = (m_Mode == AMTUNER_MODE_FM_RADIO ? 100000 : 5000); // 100KHz (FM) or 5KHz (AM)
long slices = halfband / m_ModeCaps.TuningGranularity;
long idx;
// Step through the band above target frequency
for (idx = 1; !bFoundSignal && idx <= slices; idx++) {
bFoundSignal = SearchNeighborhood
( freq + (m_ModeCaps.TuningGranularity * idx)
, AdjustingUp // Wait for one frame
, SearchLimit-1 // Let the search fine-tune
);
}
if (!bFoundSignal) {
// Step through the band below the target frequency
bFoundSignal = SearchNeighborhood(freq - halfband); // anchor the search
for (idx = slices - 1; !bFoundSignal && idx >= 0; idx--) {
bFoundSignal = SearchNeighborhood
( freq - (m_ModeCaps.TuningGranularity * idx)
, AdjustingUp // Wait for one frame
, SearchLimit-1 // Let the search fine-tune
);
}
}
}
#ifdef DEBUG
// That's all we can do, check the flag, display the results, and save (or clear) the frequency
if (bFoundSignal)
{
DbgLog(
( LOG_TRACE, 2
, TEXT("Exiting AutoTune(channel = %d) Locked")
, m_lChannel
)
);
}
else
{
DbgLog(
( LOG_TRACE, 2
, TEXT("Exiting AutoTune(channel = %d) Not Locked")
, m_lChannel
)
);
}
#endif
return bFoundSignal ? NOERROR : S_FALSE;
}
// -------------------------------------------------------------------------
// CTunerMode_TV
// -------------------------------------------------------------------------
//
// Channel of -1 means just propogate tuning information downstream for VBI codecs
//
STDMETHODIMP
CTunerMode_TV::put_Channel(
long lChannel,
long lVideoSubChannel,
long lAudioSubChannel)
{
KS_TVTUNER_CHANGE_INFO ChangeInfo;
HRESULT hr = NOERROR;
#ifdef DEBUG
DWORD dwTimeStart, dwTimeToTune, dwTimeToDeliver, dwTimeTotal;
dwTimeStart = timeGetTime();
#endif
//
// -1 is used to indicate that we only want to propogate previous tuning info
// onto the output pin, but not actually tune!
//
/* Inform the filter of the start of the channel change
*/
ChangeInfo.dwChannel = lChannel;
ChangeInfo.dwAnalogVideoStandard = m_TVFormatInfo.AVStandard;
m_pTVTuner->get_CountryCode ((long *) &ChangeInfo.dwCountryCode);
ChangeInfo.dwFlags = KS_TVTUNER_CHANGE_BEGIN_TUNE;
m_pTVTuner->DeliverChannelChangeInfo(ChangeInfo, m_Mode);
#ifdef DEBUG
dwTimeToDeliver = timeGetTime();
#endif
if (lChannel != -1) {
// Set a flag so everybody else knows we're in the midst of an AutoTune operation
if (InterlockedCompareExchangePointer(&m_InAutoTune, reinterpret_cast<void*>(TRUE), reinterpret_cast<void*>(FALSE)) == reinterpret_cast<void*>(FALSE)) {
m_lChannel = lChannel;
m_lVideoSubChannel = lVideoSubChannel;
m_lAudioSubChannel = lAudioSubChannel;
DbgLog(( LOG_TRACE, 2, TEXT("Start AutoTune(channel = %d)"), m_lChannel));
hr = AutoTune(FALSE);
InterlockedExchangePointer(&m_InAutoTune, reinterpret_cast<void*>(FALSE));
}
else {
hr = S_FALSE; // don't interrupt the current autotune
}
}
#ifdef DEBUG
dwTimeToTune = timeGetTime();
#endif
/* Inform the filter of the end of the channel change
*/
ChangeInfo.dwFlags = KS_TVTUNER_CHANGE_END_TUNE;
m_pTVTuner->DeliverChannelChangeInfo(ChangeInfo, m_Mode);
#ifdef DEBUG
dwTimeTotal = timeGetTime();
DbgLog(
( LOG_TRACE, 5
, TEXT("Channel=%d, Deliver (time ms)=%d, Tune (time ms)=%d, Total (time ms)=%d")
, lChannel
, dwTimeToDeliver - dwTimeStart
, dwTimeToTune - dwTimeStart
, dwTimeTotal - dwTimeStart
)
);
#endif
return hr;
}
STDMETHODIMP
CTunerMode_TV::ChannelMinMax(long *plChannelMin, long *plChannelMax)
{
CChanList * pListCurrent = m_pTVTuner->GetCurrentChannelList();
if (!pListCurrent) {
return E_FAIL;
}
// Total hack follows. Only the CTVTuner class knows about the
// step to get to adjacent frequencies, and this is not exposed
// anywhere in the COM interface. As a special case for
// ChannelMinMax, if both the min and max values point at the same
// location (which would normally be an app bug), then return
// the UI step value instead.
if (plChannelMin == plChannelMax) {
*plChannelMin = GetChannelStep();
}
else {
pListCurrent->GetChannelMinMax (plChannelMin, plChannelMax,
m_ModeCaps.MinFrequency, m_ModeCaps.MaxFrequency);
}
return NOERROR;
}
/* Search either up or down from the given frequency looking
* for a perfect lock. If we are close on the first call, call
* ourselves recursively, noting the trend.
*
* There is a limit on the number of times we can recurse. If
* the new trend conflicts with the previous trend, then we must
* have hopped over the "perfect" frequency, in which case the
* most recent frequency will be considered a perfect lock.
*/
BOOL
CTunerMode_TV::SearchNeighborhood(
long freq,
TuningTrend trend = AdjustingNone,
int depth = 0
)
{
BOOL rc = FALSE;
#ifdef DEBUG
LPTSTR lpszTrend;
switch (trend)
{
case AdjustingUp:
lpszTrend = TEXT("Adjusting Up");
break;
case AdjustingDown:
lpszTrend = TEXT("Adjusting Down");
break;
case AdjustingNone:
lpszTrend = TEXT("Initial Try");
break;
}
DbgLog(
( LOG_TRACE, 2
, TEXT("SearchNeighborhood(freq = %d) %s")
, freq
, lpszTrend
)
);
#endif
if (depth > SearchLimit)
{
DbgLog(
( LOG_TRACE, 2
, TEXT("Recursion limit reached, bailing out")
)
);
return FALSE;
}
/* Save the Video and Audio frequencies
*/
m_lVideoCarrier = freq;
m_lAudioCarrier = freq + m_TVFormatInfo.lSoundOffset;
/* Check if the frequencies are beyond the limits of the tuner
*/
if (m_lVideoCarrier < (long) m_ModeCaps.MinFrequency || m_lVideoCarrier > (long) m_ModeCaps.MaxFrequency)
{
DbgLog(
( LOG_TRACE, 2
, TEXT("Frequency out of range")
)
);
return FALSE;
}
/* Try the given frequencies without adjustment
*/
HRESULT hr = HW_Tune(m_lVideoCarrier, m_lAudioCarrier);
if (hr == S_OK)
{
// convert REFERENCE_TIME [100ns units] to ms
DWORD dwFrameTime_ms = static_cast<DWORD>(m_TVFormatInfo.AvgTimePerFrame/10000L) + 1L;
// In order to improve channel switching times, the gross assumption here is that
// the SettlingTime must be honored for large jumps, but that smaller, in-channel frequency
// changes (made by us, incidentally) can get by with smaller settling times.
if (AdjustingNone == trend)
Sleep(m_ModeCaps.SettlingTime);
else
Sleep(dwFrameTime_ms);
// For some reason, the Philips FR1236 returns Busy (FL == 0) for a much longer time
// than it should. Keep looping, hoping it will become unbusy.
for (int j = 0; j < 5; j++) {
hr = HW_GetStatus();
if (hr == S_OK && !m_Status.Busy) {
break;
}
Sleep(dwFrameTime_ms); // wait a frame
}
DbgLog(( LOG_TRACE, 5, TEXT("TUNING: PLLOffset = %d, Busy = %d"), (LONG) m_Status.PLLOffset, (LONG) m_Status.Busy));
if (hr == S_OK && !m_Status.Busy)
{
/* Act according to the tuning trend (if any)
*/
switch (trend)
{
case AdjustingNone:
switch ((LONG) m_Status.PLLOffset)
{
case 1: // Need to adjust up
rc = SearchNeighborhood
( freq + m_ModeCaps.TuningGranularity
, AdjustingUp, depth+1
);
break;
case -1: // Need to adjust down
rc = SearchNeighborhood
( freq - m_ModeCaps.TuningGranularity
, AdjustingDown, depth+1
);
break;
case 0: // Perfect
rc = TRUE;
break;
default: // Just plain missed
rc = FALSE;
}
break;
case AdjustingUp:
switch ((LONG) m_Status.PLLOffset)
{
case 1: // Close but still low
rc = SearchNeighborhood
( freq + m_ModeCaps.TuningGranularity
, AdjustingUp, depth+1
);
break;
case -1: // Switched trend
case 0: // Perfect
rc = TRUE;
break;
default: // Something is very wrong
rc = FALSE;
}
break;
case AdjustingDown:
switch ((LONG) m_Status.PLLOffset)
{
case -1: // Close but still high
rc = SearchNeighborhood
(freq - m_ModeCaps.TuningGranularity
, AdjustingDown, depth+1
);
break;
case 1: // Switched trend
case 0: // Perfect
rc = TRUE;
break;
default: // Something is very wrong
rc = FALSE;
}
break;
}
}
else
m_Status.PLLOffset = 100; // set to a really bad value
}
return rc;
}
STDMETHODIMP
CTunerMode_TV::AutoTune (long lChannel, long * plFoundSignal)
{
long SignalStrength = AMTUNER_NOSIGNAL;
// Set a flag so everybody else knows we're in the midst of an AutoTune operation
if (InterlockedCompareExchangePointer(&m_InAutoTune, reinterpret_cast<void*>(TRUE), reinterpret_cast<void*>(FALSE)) != reinterpret_cast<void*>(FALSE))
return S_FALSE; // already set to TRUE, don't interrupt the current autotune
m_lChannel = lChannel;
m_lVideoSubChannel = -1;
m_lAudioSubChannel = -1;
DbgLog(( LOG_TRACE, 2, TEXT("Start AutoTune(channel = %d)"), m_lChannel));
// Attempt to tune without using any previous autotune frequency (start from scratch)
HRESULT hr = AutoTune(TRUE);
if (NOERROR == hr)
SignalPresent(&SignalStrength);
// Assume AMTUNER_HASNOSIGNALSTRENGTH means tuned
*plFoundSignal = (SignalStrength != AMTUNER_NOSIGNAL);
InterlockedExchangePointer(&m_InAutoTune, reinterpret_cast<void*>(FALSE));
return hr;
}
HRESULT
CTunerMode_TV::AutoTune(BOOL bFromScratch)
{
BOOL bFoundSignal = FALSE;
long freq = 0L;
CChanList * pListCurrent = m_pTVTuner->GetCurrentChannelList();
if (!pListCurrent) {
return E_FAIL;
}
// this could override bFromScratch if no autotune frequency is found
bFromScratch = pListCurrent->GetFrequency(m_lChannel, &freq, bFromScratch);
if (0 == freq) {
return S_FALSE; // no frequency for this channel
}
// if the driver has the tuning logic, let it do the hard part
if (m_ModeCaps.Strategy == KS_TUNER_STRATEGY_DRIVER_TUNES) {
// Save the Video and Audio frequencies
m_lVideoCarrier = freq;
m_lAudioCarrier = freq + m_TVFormatInfo.lSoundOffset;
// Try the given frequencies without adjustment
HRESULT hr = HW_Tune(m_lVideoCarrier, m_lAudioCarrier);
if (hr == S_OK) {
DbgLog(( LOG_TRACE, 5, TEXT("TUNING: Started")));
Sleep(m_ModeCaps.SettlingTime);
DbgLog(( LOG_TRACE, 5, TEXT("TUNING: Finished")));
}
else {
DbgLog(( LOG_TRACE, 5, TEXT("TUNING: Unsuccessful")));
}
return hr;
}
// if we have a previously fine-tuned frequency, then give the frequency
// the benefit of the doubt before doing anything fancy
if (!bFromScratch) {
bFoundSignal = SearchNeighborhood(freq);
if (!bFoundSignal) {
// what once worked no longer works, start over with the default
bFromScratch = pListCurrent->GetFrequency(m_lChannel, &freq, TRUE);
}
else {
DbgLog(( LOG_TRACE, 5, TEXT("TUNING: Found fine-tuned")));
}
}
if (!bFoundSignal) {
if (TunerInputCable == m_pTVTuner->GetCurrentInputType()) {
int iCableSystem, preferredCableSystem;
// --------------------------------------------------------
// The main Cable TV Tuning loop
// Keeps track of the CableSystem which has been used most
// successfully in the past and uses it preferentially.
// --------------------------------------------------------
// The following must be true for the following code to work
ASSERT
(
CableSystem_Current == 0 &&
CableSystem_Standard == 1 &&
CableSystem_IRC == 2 &&
CableSystem_HRC == 3 &&
CableSystem_End == 4
);
// Figure out which cable tuning space has been most successful (give preferrence
// to CableSystem_Standard in the absence of any other hits)
int *pCableSystemCounts = m_pTVTuner->GetCurrentCableSystemCountsForCurrentInput();
pCableSystemCounts[CableSystem_Current] = pCableSystemCounts[CableSystem_Standard];
preferredCableSystem = CableSystem_Standard;
// Sort for the system with the most hits (CableSystem_Standard will never have any
// hits, so as to allow IRC a chance to be best)
for (iCableSystem = CableSystem_Standard; iCableSystem < CableSystem_End; iCableSystem++) {
if (pCableSystemCounts[CableSystem_Current] < pCableSystemCounts[iCableSystem]) {
pCableSystemCounts[CableSystem_Current] = pCableSystemCounts[iCableSystem];
preferredCableSystem = iCableSystem;
}
}
for (int cs = CableSystem_Current; !bFoundSignal && cs < CableSystem_End; cs++) {
if (cs == CableSystem_Current) {
// Try whichever system has been most successful first
m_CableSystem = preferredCableSystem;
}
else {
m_CableSystem = cs;
}
// Avoid testing the default or current system twice
if (cs != CableSystem_Current && cs == preferredCableSystem) {
continue;
}
switch (m_CableSystem) {
case CableSystem_Standard:
bFoundSignal = SearchNeighborhood(freq); // Std
if (bFoundSignal) {
// Never bump this count, otherwise IRC would never win
// pCableSystemCounts[CableSystem_Standard]++;
DbgLog(( LOG_TRACE, 5, TEXT("TUNING: Found Standard")));
}
break;
case CableSystem_IRC:
if (m_lChannel == 5 || m_lChannel == 6) {
bFoundSignal = SearchNeighborhood(freq + 2000000); // IRC (5,6)
if (bFoundSignal) {
// this is the only time we are sure we have an IRC cable system
pCableSystemCounts[CableSystem_IRC]++;
}
}
// No need to check the other channels since IRC is otherwise identical to
// CableSystem_Standard, which has already been done (or will be done next)
if (bFoundSignal) {
DbgLog(( LOG_TRACE, 5, TEXT("TUNING: Found IRC")));
}
break;
case CableSystem_HRC:
if (m_lChannel == 5 || m_lChannel == 6) {
bFoundSignal = SearchNeighborhood(freq + 750000); // HRC (5,6)
}
else {
bFoundSignal = SearchNeighborhood(freq - 1250000); // HRC
}
if (bFoundSignal) {
pCableSystemCounts[CableSystem_HRC]++;
DbgLog(( LOG_TRACE, 5, TEXT("TUNING: Found HRC")));
}
break;
default:
ASSERT(CableSystem_End != m_CableSystem); // Shouldn't get here
}
// Don't try IRC or HRC if not USA Cable
if ( !(F_USA_CABLE == pListCurrent->GetFreqListID()) )
break;
}
#ifdef LINEAR_SEARCH_FOR_TEST_KIT
if (!bFoundSignal) {
// Check if using the "Uni-Cable" frequency list
if (F_UNI_CABLE == pListCurrent->GetFreqListID()) {
DbgLog(( LOG_TRACE, 5, TEXT("TUNING: Starting Exhaustive Search")));
/* The default frequency isn't working. Take the brute-force
* approach.
*
* Start at the target frequency, and work up to almost 1MHz
* above the target frequency. This is optimized for a cable
* channel line-up consisting of a channel at each 1MHz
* increment.
*/
long slices = 1000000 / m_ModeCaps.TuningGranularity;
long idx;
// Step upward from the target frequency
for (idx = 1; idx < slices; idx++) {
bFoundSignal = SearchNeighborhood
( freq + (m_ModeCaps.TuningGranularity * idx)
, AdjustingUp // Wait for one frame
, SearchLimit-1 // Let the search fine-tune
);
if (bFoundSignal)
break;
}
}
}
#endif
}
else {
// Broadcast (Antenna)
bFoundSignal = SearchNeighborhood(freq); // anchor the search at the default
if (!bFoundSignal) {
DbgLog(( LOG_TRACE, 5, TEXT("TUNING: Starting Exhaustive Search")));
/* The default frequency isn't working. Take the brute-force
* approach.
*
* Search through the .5MHz band around the target frequency,
* ending up at the default target frequency.
*/
long slices = 250000 / m_ModeCaps.TuningGranularity;
long idx;
// Step through a .25MHz range (inclusive) above target frequency
for (idx = 1; !bFoundSignal && idx <= slices; idx++) {
bFoundSignal = SearchNeighborhood
( freq + (m_ModeCaps.TuningGranularity * idx)
, AdjustingUp // Wait for one frame
, SearchLimit-1 // Let the search fine-tune
);
}
if (!bFoundSignal) {
// Step through a .25MHz range (inclusive) below the target frequency
bFoundSignal = SearchNeighborhood(freq - 250000); // anchor the search
for (idx = slices - 1; !bFoundSignal && idx >= 0; idx--) {
bFoundSignal = SearchNeighborhood
( freq - (m_ModeCaps.TuningGranularity * idx)
, AdjustingUp // Wait for one frame
, SearchLimit-1 // Let the search fine-tune
);
}
}
}
}
}
// That's all we can do, check the flag, display the results, and save (or clear) the frequency
if (bFoundSignal)
{
DbgLog(
( LOG_TRACE, 2
, TEXT("Exiting AutoTune(channel = %d) Locked")
, m_lChannel
)
);
pListCurrent->SetAutoTuneFrequency (m_lChannel, m_lVideoCarrier);
}
else
{
DbgLog(
( LOG_TRACE, 2
, TEXT("Exiting AutoTune(channel = %d) Not Locked")
, m_lChannel
)
);
pListCurrent->SetAutoTuneFrequency (m_lChannel, 0);
}
return bFoundSignal ? NOERROR : S_FALSE;
}
STDMETHODIMP
CTunerMode_TV::put_CountryCode(long lCountryCode)
{
AnalogVideoStandard lAnalogVideoStandard;
BOOL fSupported;
DbgLog(
( LOG_TRACE, 2
, TEXT("put_CountryCode(lCountryCode = %d)")
, lCountryCode
)
);
if (m_lCountryCode == lCountryCode)
return NOERROR;
CChanList * pListCurrent = m_pTVTuner->GetCurrentChannelList();
if (!pListCurrent) {
return E_FAIL;
}
lAnalogVideoStandard = m_pTVTuner->GetVideoStandardForCurrentCountry();
ASSERT (lAnalogVideoStandard);
// Check if the format of this country is supported by the tuner
// Even if it's not, continue with initialization so we don't blow
// up later trying to reference invalid frequency lists!
fSupported = SetTVFormatInfo(lAnalogVideoStandard, &m_TVFormatInfo, FALSE);
if (!fSupported) {
if (m_lCountryCode == -1) {
SetTVFormatInfo(lAnalogVideoStandard, &m_TVFormatInfo, TRUE);
}
else {
return HRESULT_FROM_WIN32 (ERROR_INVALID_PARAMETER);
}
}
// Post Win98 SP1, inform the tuner of the new video standard!!!
HW_SetVideoStandard(lAnalogVideoStandard);
m_lCountryCode = lCountryCode;
if (m_Active) {
put_Channel (m_lChannel, AMTUNER_SUBCHAN_DEFAULT, AMTUNER_SUBCHAN_DEFAULT);
}
return NOERROR;
}
// Look downstream for a locked signal on the VideoDecoder
//
// Possible plSignalStrength values are:
// AMTUNER_HASNOSIGNALSTRENGTH = -1,
// AMTUNER_NOSIGNAL = 0,
// AMTUNER_SIGNALPRESENT = 1
//
STDMETHODIMP
CTunerMode_TV::SignalPresent(
/* [out] */ long *plSignalStrength)
{
HRESULT hr;
long Locked;
IAMAnalogVideoDecoder *pAnalogVideoDecoder;
IPin *pPinConnected;
CBasePin *pPin = m_pFilter->GetPinFromType(TunerPinType_Video);
// Assume the worst
*plSignalStrength = AMTUNER_HASNOSIGNALSTRENGTH;
// if the video output pin is connected
if (pPin && pPin->IsConnected()) {
// get the pin on the downstream filter
if (SUCCEEDED (hr = pPin->ConnectedTo(&pPinConnected))) {
// recursively search for the requested interface
if (SUCCEEDED (hr = m_pFilter->FindDownstreamInterface (
pPinConnected,
__uuidof (IAMAnalogVideoDecoder),
(void **) &pAnalogVideoDecoder))) {
pAnalogVideoDecoder->get_HorizontalLocked(&Locked);
pAnalogVideoDecoder->Release();
*plSignalStrength = Locked ? AMTUNER_SIGNALPRESENT : AMTUNER_NOSIGNAL;
}
pPinConnected->Release();
}
}
return NOERROR;
}
// -------------------------------------------------------------------------
// CTunerMode_ATSC
// -------------------------------------------------------------------------
// Look downstream for a locked signal on the Demodulator
//
// Possible plSignalStrength values are:
// AMTUNER_HASNOSIGNALSTRENGTH = -1,
// AMTUNER_NOSIGNAL = 0,
// AMTUNER_SIGNALPRESENT = 1
//
STDMETHODIMP
CTunerMode_ATSC::SignalPresent(
/* [out] */ long *plSignalStrength)
{
*plSignalStrength = AMTUNER_SIGNALPRESENT;
// Hack, for now, DTV lock is returned from the tuner via
// SignalStrength!!!
HW_GetStatus ();
*plSignalStrength = m_Status.SignalStrength;
#if 0 // update for Demodulator interface
HRESULT hr;
long Locked;
IAMDemodulator *pDemodulator;
IPin *pPinConnected;
CBasePin *pPin = m_pFilter->GetPinFromType(TunerPinType_IF);
// Assume the worst
*plSignalStrength = AMTUNER_HASNOSIGNALSTRENGTH;
// if the video output pin is connected
if (pPin && pPin->IsConnected()) {
// get the pin on the downstream filter
if (SUCCEEDED (hr = pPin->ConnectedTo(&pPinConnected))) {
// recursively search for the requested interface
if (SUCCEEDED (hr = m_pFilter->FindDownstreamInterface (
pPinConnected,
__uuidof (IAMDemodulator),
(void **) &pDemodulator))) {
pDemodulator->get_Locked(&Locked);
pDemodulator->Release();
*plSignalStrength = Locked ? AMTUNER_SIGNALPRESENT : AMTUNER_NOSIGNAL;
}
pPinConnected->Release();
}
}
#endif
return NOERROR;
}
// -------------------------------------------------------------------------
// CTVTuner class
//
// Encapsulates multiple subtuners
// -------------------------------------------------------------------------
// Get the formats supported by the device
// This should be the first call made during initialization
HRESULT
CTVTuner::HW_GetTVTunerCaps()
{
BOOL fOk;
ULONG cbReturned;
KSPROPERTY_TUNER_CAPS_S TunerCaps;
if ( !m_hDevice )
return E_INVALIDARG;
// Get the overall modes supported, ie.
// TV, FM, AM, DSS, ...
ZeroMemory (&TunerCaps, sizeof (TunerCaps));
TunerCaps.Property.Set = PROPSETID_TUNER;
TunerCaps.Property.Id = KSPROPERTY_TUNER_CAPS;
TunerCaps.Property.Flags = KSPROPERTY_TYPE_GET;
fOk = KsControl(m_hDevice,
(DWORD) IOCTL_KS_PROPERTY,
&TunerCaps, sizeof( KSPROPERTY_TUNER_CAPS_S),
&TunerCaps, sizeof( KSPROPERTY_TUNER_CAPS_S),
&cbReturned);
if (!fOk) {
DbgLog(( LOG_ERROR, 0,
TEXT("FAILED: KSPROPERTY_TUNER_CAPS, KSPROPERTY_TYPE_GET, cbReturned = %d"), cbReturned));
return E_FAIL;
}
m_TunerCaps = TunerCaps;
// Check if m_lTotalInputs not already gotten from a previous instance (via IPersistStream)
if (m_lTotalInputs == 0)
{
KSPROPERTY_TUNER_MODE_CAPS_S TunerModeCaps;
// GAK!!! Now get details of the TV mode capabilities
// only to get the number of inputs!!! Very Ugly!!!
TunerModeCaps.Property.Set = PROPSETID_TUNER;
TunerModeCaps.Property.Id = KSPROPERTY_TUNER_MODE_CAPS;
TunerModeCaps.Property.Flags = KSPROPERTY_TYPE_GET;
TunerModeCaps.Mode = KSPROPERTY_TUNER_MODE_TV;
fOk = KsControl(m_hDevice,
(DWORD) IOCTL_KS_PROPERTY,
&TunerModeCaps, sizeof( KSPROPERTY_TUNER_MODE_CAPS_S),
&TunerModeCaps, sizeof( KSPROPERTY_TUNER_MODE_CAPS_S),
&cbReturned);
if ( fOk ) {
m_lTotalInputs = TunerModeCaps.NumberOfInputs;
}
else {
DbgLog(( LOG_ERROR, 0,
TEXT("FAILED: KSPROPERTY_TUNER_MODE_CAPS, KSPROPERTY_TYPE_GET, cbReturned = %d"), cbReturned));
}
}
// Figure out if the device supports an IntermediateFreq Pin
// Post Win98 SP1
KSPROPERTY_TUNER_IF_MEDIUM_S IFMedium;
ZeroMemory (&IFMedium, sizeof (IFMedium));
IFMedium.Property.Set = PROPSETID_TUNER;
IFMedium.Property.Id = KSPROPERTY_TUNER_IF_MEDIUM;
IFMedium.Property.Flags = KSPROPERTY_TYPE_GET;
KsControl(m_hDevice,
(DWORD) IOCTL_KS_PROPERTY,
&IFMedium, sizeof( KSPROPERTY_TUNER_IF_MEDIUM_S),
&IFMedium, sizeof( KSPROPERTY_TUNER_IF_MEDIUM_S),
&cbReturned);
if (!fOk) {
DbgLog(( LOG_ERROR, 0, TEXT("BENIGN: KSPROPERTY_TUNER_IF_MEDIUM, KSPROPERTY_TYPE_GET, cbReturned = %d"), cbReturned));
}
m_IFMedium = IFMedium.IFMedium;
return fOk ? S_OK : S_FALSE;
}
HRESULT
CTVTuner::HW_SetInput (long lIndex)
{
BOOL fOk;
ULONG cbReturned;
KSPROPERTY_TUNER_INPUT_S Input;
if ( !m_hDevice )
return E_INVALIDARG;
Input.Property.Set = PROPSETID_TUNER;
Input.Property.Id = KSPROPERTY_TUNER_INPUT;
Input.Property.Flags = KSPROPERTY_TYPE_SET;
Input.InputIndex = lIndex;
fOk = KsControl(m_hDevice,
(DWORD) IOCTL_KS_PROPERTY,
&Input, sizeof( Input),
&Input, sizeof( Input),
&cbReturned);
if (!fOk) {
DbgLog(( LOG_ERROR, 0,
TEXT("FAILED: KSPROPERTY_TUNER_INPUT, KSPROPERTY_TYPE_SET, cbReturned = %d"), cbReturned));
}
return fOk ? S_OK : S_FALSE;
}
// -------------------------------------------------------------------------
// CTVTuner
// -------------------------------------------------------------------------
CTVTuner::CTVTuner(CTVTunerFilter *pTVTunerFilter)
: m_pFilter(pTVTunerFilter)
, m_lTotalInputs (0)
, m_lCountryCode (-1) // Init to an illegal value
, m_VideoStandardForCountry ((AnalogVideoStandard) 0)
, m_lTuningSpace (0)
, m_CurrentInputType (TunerInputCable)
, m_lInputIndex (0)
, m_pInputTypeArray (NULL)
, m_pListCountry (NULL)
, m_pListBroadcast (NULL)
, m_pListCable (NULL)
, m_pListCurrent (NULL)
, m_hDevice (NULL)
, m_CurrentMode (0)
, m_ModesSupported (0)
, m_CableSystemCounts (NULL)
, m_pMode_Current (NULL)
{
ZeroMemory (m_pModeList, sizeof (m_pModeList));
//
// Create the master list of all countries and their video standards
//
m_pListCountry = new CCountryList ();
}
STDMETHODIMP
CTVTuner::Load(LPPROPERTYBAG pPropBag,
LPERRORLOG pErrorLog,
PKSPROPERTY_TUNER_CAPS_S pTunerCaps,
PKSPIN_MEDIUM pIFMedium)
{
HRESULT hr = S_OK;
int j;
VARIANT var;
VariantInit(&var);
// This should be NULL, but recover gracefully
ASSERT(m_hDevice == NULL);
if (m_hDevice)
CloseHandle(m_hDevice);
V_VT(&var) = VT_BSTR;
if(SUCCEEDED(pPropBag->Read(L"DevicePath", &var, 0)))
{
#ifndef _UNICODE
WideCharToMultiByte(CP_ACP, 0, V_BSTR(&var), -1,
m_pDeviceName, sizeof(m_pDeviceName), 0, 0);
#else
lstrcpy(m_pDeviceName, V_BSTR(&var));
#endif
VariantClear(&var);
DbgLog((LOG_TRACE,2,TEXT("CTVTunerFilter::Load: use %s"), m_pDeviceName));
if (!CreateDevice()) {
DbgLog((LOG_TRACE,2,TEXT("CTVTunerr::Load Failed: use %s"), m_pDeviceName));
return E_FAIL;
}
}
//
// Determine the overall capabilities of the tuner
//
HW_GetTVTunerCaps ();
ASSERT (m_TunerCaps.ModesSupported &
KSPROPERTY_TUNER_MODE_TV |
KSPROPERTY_TUNER_MODE_FM_RADIO |
KSPROPERTY_TUNER_MODE_AM_RADIO |
KSPROPERTY_TUNER_MODE_DSS |
KSPROPERTY_TUNER_MODE_ATSC
);
// Validate m_lTotalInputs
if (m_lTotalInputs <= 0)
{
DbgLog((LOG_TRACE,2,TEXT("CTVTunerr::Load Failed: invalid NumberOfInputs"), m_lTotalInputs));
return E_FAIL;
}
// This will be NULL if no previous instance to init from
if (m_pInputTypeArray == NULL)
{
m_pInputTypeArray = new TunerInputType [m_lTotalInputs];
if (m_pInputTypeArray == NULL)
return E_OUTOFMEMORY;
// Initialize the input types
for (j = 0; j < m_lTotalInputs; j++) {
m_pInputTypeArray[j] = TunerInputCable;
}
// Set the current input type
m_CurrentInputType = TunerInputCable;
// This should be NULL, but recover gracefully
ASSERT(m_CableSystemCounts == NULL);
delete [] m_CableSystemCounts;
m_CableSystemCounts = new int [m_lTotalInputs * CableSystem_End];
if (m_CableSystemCounts == NULL)
return E_OUTOFMEMORY;
for (j = 0; j < CableSystem_End * m_lTotalInputs; j++) {
m_CableSystemCounts[j] = 0;
}
m_lCountryCode = GetProfileInt(TEXT("intl"), TEXT ("iCountry"), 1); // for backward compatibility
// Note that this must be done BEFORE creating the subtuners
hr = put_CountryCode (m_lCountryCode);
if (FAILED(hr) && m_lCountryCode != 1) {
hr = put_CountryCode (1); // USA, Canada, Caribbean by default
}
}
// Proceed only if we could set the country code
if (SUCCEEDED(hr))
{
put_ConnectInput (m_lInputIndex);
if (m_ModesSupported == 0)
m_ModesSupported = m_TunerCaps.ModesSupported;
else
ASSERT(m_ModesSupported == m_TunerCaps.ModesSupported);
//
// Create all of the "sub tuners" supported
//
if (m_TunerCaps.ModesSupported & KSPROPERTY_TUNER_MODE_TV) {
if (!m_pModeList[TuningMode_TV]) {
m_pModeList[TuningMode_TV] = new CTunerMode_TV(
m_pFilter,
this,
AMTUNER_MODE_TV,
CHANNEL_DEFAULT_TV,
AMTUNER_SUBCHAN_DEFAULT,
AMTUNER_SUBCHAN_DEFAULT
);
m_CurrentMode = m_CurrentMode ? m_CurrentMode : KSPROPERTY_TUNER_MODE_TV;
}
}
if (m_TunerCaps.ModesSupported & KSPROPERTY_TUNER_MODE_ATSC) {
if (!m_pModeList[TuningMode_ATSC]) {
m_pModeList[TuningMode_ATSC] = new CTunerMode_ATSC(
m_pFilter,
this,
CHANNEL_DEFAULT_ATSC,
AMTUNER_SUBCHAN_DEFAULT,
AMTUNER_SUBCHAN_DEFAULT
);
m_CurrentMode = m_CurrentMode ? m_CurrentMode : KSPROPERTY_TUNER_MODE_ATSC;
}
}
if (m_TunerCaps.ModesSupported & KSPROPERTY_TUNER_MODE_DSS) {
if (!m_pModeList[TuningMode_DSS]) {
m_pModeList[TuningMode_DSS] = new CTunerMode_DSS(
m_pFilter,
this,
CHANNEL_DEFAULT_DSS,
AMTUNER_SUBCHAN_DEFAULT,
AMTUNER_SUBCHAN_DEFAULT
);
m_CurrentMode = m_CurrentMode ? m_CurrentMode : KSPROPERTY_TUNER_MODE_DSS;
}
}
if (m_TunerCaps.ModesSupported & KSPROPERTY_TUNER_MODE_AM_RADIO) {
if (!m_pModeList[TuningMode_AM]) {
m_pModeList[TuningMode_AM] = new CTunerMode_AM(
m_pFilter,
this,
CHANNEL_DEFAULT_AM
);
m_CurrentMode = m_CurrentMode ? m_CurrentMode : KSPROPERTY_TUNER_MODE_AM_RADIO;
}
}
if (m_TunerCaps.ModesSupported & KSPROPERTY_TUNER_MODE_FM_RADIO) {
if (!m_pModeList[TuningMode_FM]) {
m_pModeList[TuningMode_FM] = new CTunerMode_FM(
m_pFilter,
this,
CHANNEL_DEFAULT_FM
);
m_CurrentMode = m_CurrentMode ? m_CurrentMode : KSPROPERTY_TUNER_MODE_FM_RADIO;
}
}
// Now that the subtuners have been created, finish initializing them, and
// tell all of the subtuners the country code has changed
for (j = 0; SUCCEEDED(hr) && j < TuningMode_Last; j++) {
if (m_pModeList[j]) {
hr = m_pModeList[j]->Init();
if (SUCCEEDED(hr))
hr = m_pModeList[j]->put_CountryCode (m_lCountryCode);
}
}
if (SUCCEEDED(hr))
{
// Finally, complete the process by activating the desired mode
hr = put_Mode((AMTunerModeType)m_CurrentMode);
}
}
// Give the caller a copy of the TunerCaps structure
*pTunerCaps = m_TunerCaps;
// Post Win98 SP1, copy the IF Freq medium
*pIFMedium = m_IFMedium;
return hr;
}
HRESULT CTVTuner::ReadFromStream(IStream *pStream)
{
ULONG cb = 0;
HRESULT hr;
// Get the input count
hr = pStream->Read(&m_lTotalInputs, sizeof(m_lTotalInputs), &cb);
if (FAILED(hr) || sizeof(m_lTotalInputs) != cb)
return hr;
// This should be NULL, but recover gracefully
ASSERT(m_pInputTypeArray == NULL);
delete [] m_pInputTypeArray;
m_pInputTypeArray = new TunerInputType[m_lTotalInputs];
if (m_pInputTypeArray)
{
// Initialize the input types
for (long j = 0; j < m_lTotalInputs; j++)
{
hr = pStream->Read(&m_pInputTypeArray[j], sizeof(TunerInputType), &cb);
if (FAILED(hr) || sizeof(TunerInputType) != cb)
return hr;
}
}
else
return E_OUTOFMEMORY;
// This should be NULL, but recover gracefully
ASSERT(m_CableSystemCounts == NULL);
delete [] m_CableSystemCounts;
m_CableSystemCounts = new int [CableSystem_End * m_lTotalInputs];
if (m_CableSystemCounts)
{
for (long j = 0; j < CableSystem_End * m_lTotalInputs; j++)
m_CableSystemCounts[j] = 0;
}
else
return E_OUTOFMEMORY;
// Get the input index
hr = pStream->Read(&m_lInputIndex, sizeof(m_lInputIndex), &cb);
if (FAILED(hr) || sizeof(m_lInputIndex) != cb)
return hr;
// Set the current input type
m_CurrentInputType = m_pInputTypeArray[m_lInputIndex];
// Get the country code
hr = pStream->Read(&m_lCountryCode, sizeof(m_lCountryCode), &cb);
if (FAILED(hr) || sizeof(m_lCountryCode) != cb)
return hr;
// Get the tuning space
hr = pStream->Read(&m_lTuningSpace, sizeof(m_lTuningSpace), &cb);
if (FAILED(hr) || sizeof(m_lTuningSpace) != cb)
return hr;
// Note that this must be done BEFORE creating the subtuners
hr = put_CountryCode (m_lCountryCode);
if (FAILED(hr) && m_lCountryCode != 1) {
hr = put_CountryCode (1); // USA, Canada, Caribbean by default
}
if (FAILED(hr))
return hr;
// Get the modes supported
hr = pStream->Read(&m_ModesSupported, sizeof(m_ModesSupported), &cb);
if (FAILED(hr) || sizeof(m_ModesSupported) != cb)
return hr;
// Get the current mode
hr = pStream->Read(&m_CurrentMode, sizeof(m_CurrentMode), &cb);
if (FAILED(hr) || sizeof(m_CurrentMode) != cb)
return hr;
// Create all of the supported sub tuners
if (m_ModesSupported & KSPROPERTY_TUNER_MODE_TV) {
long Channel = CHANNEL_DEFAULT_TV;
// Get the channel
hr = pStream->Read(&Channel, sizeof(Channel), &cb);
if (FAILED(hr) || sizeof(Channel) != cb)
return hr;
m_pModeList[TuningMode_TV] = new CTunerMode_TV (m_pFilter, this, AMTUNER_MODE_TV, Channel, AMTUNER_SUBCHAN_DEFAULT, AMTUNER_SUBCHAN_DEFAULT);
}
if (m_ModesSupported & KSPROPERTY_TUNER_MODE_ATSC) {
long Channel = CHANNEL_DEFAULT_ATSC;
long VideoSubChannel = AMTUNER_SUBCHAN_DEFAULT;
long AudioSubChannel = AMTUNER_SUBCHAN_DEFAULT;
// Get the ATSC Channels
hr = pStream->Read(&Channel, sizeof(Channel), &cb);
if (FAILED(hr) || sizeof(Channel) != cb)
return hr;
hr = pStream->Read(&VideoSubChannel, sizeof(VideoSubChannel), &cb);
if (FAILED(hr) || sizeof(VideoSubChannel) != cb)
return hr;
hr = pStream->Read(&AudioSubChannel, sizeof(AudioSubChannel), &cb);
if (FAILED(hr) || sizeof(AudioSubChannel) != cb)
return hr;
m_pModeList[TuningMode_ATSC] = new CTunerMode_ATSC (m_pFilter, this, Channel, VideoSubChannel, AudioSubChannel);
}
if (m_ModesSupported & KSPROPERTY_TUNER_MODE_DSS) {
long Channel = CHANNEL_DEFAULT_DSS;
long VideoSubChannel = AMTUNER_SUBCHAN_DEFAULT;
long AudioSubChannel = AMTUNER_SUBCHAN_DEFAULT;
// Get the DSS Channels
hr = pStream->Read(&Channel, sizeof(Channel), &cb);
if (FAILED(hr) || sizeof(Channel) != cb)
return hr;
hr = pStream->Read(&VideoSubChannel, sizeof(VideoSubChannel), &cb);
if (FAILED(hr) || sizeof(VideoSubChannel) != cb)
return hr;
hr = pStream->Read(&AudioSubChannel, sizeof(AudioSubChannel), &cb);
if (FAILED(hr) || sizeof(AudioSubChannel) != cb)
return hr;
m_pModeList[TuningMode_DSS] = new CTunerMode_DSS (m_pFilter, this, Channel, VideoSubChannel, AudioSubChannel);
}
if (m_ModesSupported & KSPROPERTY_TUNER_MODE_AM_RADIO) {
long Channel = CHANNEL_DEFAULT_AM;
// Get the AM station
hr = pStream->Read(&Channel, sizeof(Channel), &cb);
if (FAILED(hr) || sizeof(Channel) != cb)
return hr;
m_pModeList[TuningMode_AM] = new CTunerMode_AM (m_pFilter, this, Channel);
}
if (m_ModesSupported & KSPROPERTY_TUNER_MODE_FM_RADIO) {
long Channel = CHANNEL_DEFAULT_FM;
// Get the FM station
hr = pStream->Read(&Channel, sizeof(Channel), &cb);
if (FAILED(hr) || sizeof(Channel) != cb)
return hr;
m_pModeList[TuningMode_FM] = new CTunerMode_FM (m_pFilter, this, Channel);
}
return hr;
}
HRESULT CTVTuner::WriteToStream(IStream *pStream)
{
HRESULT hr = S_OK;
// Save the Inputs count
hr = pStream->Write(&m_lTotalInputs, sizeof(m_lTotalInputs), NULL);
if (FAILED(hr))
return hr;
// This shouldn't be NULL
ASSERT(m_pInputTypeArray != NULL);
if (m_pInputTypeArray)
{
// Initialize the input types
for (long j = 0; j < m_lTotalInputs; j++)
{
hr = pStream->Write(&m_pInputTypeArray[j], sizeof(TunerInputType), NULL);
if (FAILED(hr))
return hr;
}
}
// Save the input index
hr = pStream->Write(&m_lInputIndex, sizeof(m_lInputIndex), NULL);
if (FAILED(hr))
return hr;
// Save the country code
hr = pStream->Write(&m_lCountryCode, sizeof(m_lCountryCode), NULL);
if (FAILED(hr))
return hr;
// Save the tuning space
hr = pStream->Write(&m_lTuningSpace, sizeof(m_lTuningSpace), NULL);
if (FAILED(hr))
return hr;
// Save the modes supported
hr = pStream->Write(&m_ModesSupported, sizeof(m_ModesSupported), NULL);
if (FAILED(hr))
return hr;
// Save the current mode
hr = pStream->Write(&m_CurrentMode, sizeof(m_CurrentMode), NULL);
if (FAILED(hr))
return hr;
// Save all of the supported sub tuners' states
if (m_ModesSupported & KSPROPERTY_TUNER_MODE_TV) {
long Channel;
long junk;
hr = m_pModeList[TuningMode_TV]->get_Channel(&Channel, &junk, &junk);
if (FAILED(hr))
return hr;
// Save the channel
hr = pStream->Write(&Channel, sizeof(Channel), NULL);
if (FAILED(hr))
return hr;
}
if (m_ModesSupported & KSPROPERTY_TUNER_MODE_ATSC) {
long Channel;
long VideoSubChannel;
long AudioSubChannel;
hr = m_pModeList[TuningMode_ATSC]->get_Channel(&Channel, &VideoSubChannel, &AudioSubChannel);
if (FAILED(hr))
return hr;
// Save the ATSC Channels
hr = pStream->Write(&Channel, sizeof(Channel), NULL);
if (FAILED(hr))
return hr;
hr = pStream->Write(&VideoSubChannel, sizeof(VideoSubChannel), NULL);
if (FAILED(hr))
return hr;
hr = pStream->Write(&AudioSubChannel, sizeof(AudioSubChannel), NULL);
if (FAILED(hr))
return hr;
}
if (m_ModesSupported & KSPROPERTY_TUNER_MODE_DSS) {
long Channel;
long VideoSubChannel;
long AudioSubChannel;
hr = m_pModeList[TuningMode_DSS]->get_Channel(&Channel, &VideoSubChannel, &AudioSubChannel);
if (FAILED(hr))
return hr;
// Save the DSS Channels
hr = pStream->Write(&Channel, sizeof(Channel), NULL);
if (FAILED(hr))
return hr;
hr = pStream->Write(&VideoSubChannel, sizeof(VideoSubChannel), NULL);
if (FAILED(hr))
return hr;
hr = pStream->Write(&AudioSubChannel, sizeof(AudioSubChannel), NULL);
if (FAILED(hr))
return hr;
}
if (m_ModesSupported & KSPROPERTY_TUNER_MODE_AM_RADIO) {
long Channel;
long junk;
hr = m_pModeList[TuningMode_AM]->get_Channel(&Channel, &junk, &junk);
if (FAILED(hr))
return hr;
// Save the AM station
hr = pStream->Write(&Channel, sizeof(Channel), NULL);
if (FAILED(hr))
return hr;
}
if (m_ModesSupported & KSPROPERTY_TUNER_MODE_FM_RADIO) {
long Channel;
long junk;
hr = m_pModeList[TuningMode_FM]->get_Channel(&Channel, &junk, &junk);
if (FAILED(hr))
return hr;
// Save the FM station
hr = pStream->Write(&Channel, sizeof(Channel), NULL);
if (FAILED(hr))
return hr;
}
return hr;
}
int CTVTuner::SizeMax(void)
{
return
// The Inputs count
sizeof(m_lTotalInputs)
+
// InputTypes array
(m_lTotalInputs * sizeof(TunerInputType))
+
// The input index
sizeof(m_lInputIndex)
+
// The country code
sizeof(m_lCountryCode)
+
// The tuning space
sizeof(m_lTuningSpace)
+
// The modes supported
sizeof(m_ModesSupported)
+
// The current mode
sizeof(m_CurrentMode)
+
// calculate the space used by the supported tuners
m_ModesSupported & KSPROPERTY_TUNER_MODE_TV ? sizeof(long) : 0
+
m_ModesSupported & KSPROPERTY_TUNER_MODE_ATSC ? sizeof(long) * 3 : 0
+
m_ModesSupported & KSPROPERTY_TUNER_MODE_DSS ? sizeof(long) * 3 : 0
+
m_ModesSupported & KSPROPERTY_TUNER_MODE_AM_RADIO ? sizeof(long) : 0
+
m_ModesSupported & KSPROPERTY_TUNER_MODE_FM_RADIO ? sizeof(long) : 0
;
}
CTVTuner::~CTVTuner()
{
delete [] m_pInputTypeArray; m_pInputTypeArray = NULL;
delete m_pListCable; m_pListCable = NULL;
delete m_pListBroadcast; m_pListBroadcast = NULL;
delete m_pListCountry; m_pListCountry = NULL;
delete [] m_CableSystemCounts; m_CableSystemCounts = NULL;
m_pListCurrent = NULL;
// Delete all of the "sub tuners"
for (int j = 0; j < TuningMode_Last; j++) {
if (m_pModeList[j] != NULL) {
delete m_pModeList[j];
m_pModeList[j] = NULL;
}
}
if ( m_hDevice ) {
CloseHandle( m_hDevice );
}
}
int CTVTuner::CreateDevice()
{
HANDLE hDevice ;
hDevice = CreateFile( m_pDeviceName,
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
NULL ) ;
if ( hDevice == (HANDLE) -1 ) {
DbgLog(( LOG_TRACE, 0, TEXT("CTVTuner::CreateDevice FAILED!")));
return 0 ;
} else {
m_hDevice = hDevice;
return 1;
}
}
// -------------------------------------------------------------------------
// IAMTVTuner
// -------------------------------------------------------------------------
STDMETHODIMP
CTVTuner::put_Channel(
long lChannel,
long lVideoSubChannel,
long lAudioSubChannel)
{
ASSERT (m_pMode_Current);
return m_pMode_Current->put_Channel(
lChannel,
lVideoSubChannel,
lAudioSubChannel);
}
STDMETHODIMP
CTVTuner::get_Channel(
long *plChannel,
long * plVideoSubChannel,
long * plAudioSubChannel)
{
ASSERT (m_pMode_Current);
return m_pMode_Current->get_Channel(
plChannel,
plVideoSubChannel,
plAudioSubChannel);
}
STDMETHODIMP
CTVTuner::ChannelMinMax(long *plChannelMin, long *plChannelMax)
{
ASSERT (m_pMode_Current);
return m_pMode_Current->ChannelMinMax (plChannelMin, plChannelMax);
}
STDMETHODIMP
CTVTuner::AutoTune (long lChannel, long * plFoundSignal)
{
ASSERT (m_pMode_Current);
return m_pMode_Current->AutoTune(lChannel, plFoundSignal);
}
STDMETHODIMP
CTVTuner::StoreAutoTune ()
{
DbgLog(( LOG_TRACE, 2, TEXT("StoreAutoTune() called")));
if (m_pListCurrent)
{
BOOL fOK;
fOK = m_pListCurrent->WriteListToRegistry (m_lTuningSpace);
return fOK ? NOERROR : HRESULT_FROM_WIN32 (ERROR_INVALID_PARAMETER);
}
else
return E_FAIL; // somebody ignored a previous error code
}
STDMETHODIMP
CTVTuner::put_CountryCode(long lCountryCode)
{
long lIndexCable, lIndexBroad;
HRESULT hr = S_OK;
HRESULT hrFinal = S_OK;
long lCountryCodeOriginal = m_lCountryCode;
// if (m_lCountryCode == lCountryCode)
// return NOERROR;
// Get the RCDATA indices for the two tuning spaces, given a country
if (!m_pListCountry->GetFrequenciesAndStandardFromCountry (
lCountryCode,
&lIndexCable,
&lIndexBroad,
&m_VideoStandardForCountry))
return HRESULT_FROM_WIN32 (ERROR_INVALID_PARAMETER);
m_lCountryCode = lCountryCode;
ASSERT (m_pListCountry);
delete m_pListCable;
delete m_pListBroadcast;
m_pListCable = new CChanList (&hr, lCountryCode, lIndexCable, TRUE, m_lTuningSpace);
m_pListBroadcast = new CChanList (&hr, lCountryCode, lIndexBroad, FALSE, m_lTuningSpace);
if (FAILED(hr) || !m_pListCable || !m_pListBroadcast)
{
m_pListCurrent = NULL;
return FAILED(hr) ? hr : E_OUTOFMEMORY;
}
m_pListCurrent = ((m_CurrentInputType == TunerInputCable) ?
m_pListCable : m_pListBroadcast);
//
// Tell all of the subtuners the country code has changed
//
for (int j = 0; j < TuningMode_Last; j++) {
if (m_pModeList[j]) {
hr = m_pModeList[j]->put_CountryCode (lCountryCode);
DbgLog(( LOG_ERROR, 0,
TEXT("FAILED: %0x=put_CountryCode(%d), TunerMode=%d"), hr, lCountryCode, j));
if (m_pModeList[j] == m_pMode_Current) {
hrFinal = hr;
}
}
}
return hrFinal;
}
STDMETHODIMP
CTVTuner::get_CountryCode(long *plCountryCode)
{
*plCountryCode = m_lCountryCode;
return NOERROR;
}
STDMETHODIMP
CTVTuner::put_TuningSpace(long lTuningSpace)
{
m_lTuningSpace = lTuningSpace;
ASSERT (m_pListCurrent);
m_pListCurrent->ReadListFromRegistry(lTuningSpace);
return NOERROR;
}
STDMETHODIMP
CTVTuner::get_TuningSpace(long *plTuningSpace)
{
*plTuningSpace = m_lTuningSpace;
return NOERROR;
}
//
// Possible plSignalStrength values for TV are:
// AMTUNER_HASNOSIGNALSTRENGTH = -1,
// AMTUNER_NOSIGNAL = 0,
// AMTUNER_SIGNALPRESENT = 1
// or (0 - 100) for AM/FM
//
STDMETHODIMP
CTVTuner::SignalPresent(
/* [out] */ long *plSignalStrength)
{
ASSERT (m_pMode_Current);
return m_pMode_Current->SignalPresent (plSignalStrength);
}
STDMETHODIMP
CTVTuner::put_Mode(
/* [in] */ AMTunerModeType lMode)
{
HRESULT hr;
long OriginalMode = (long) m_CurrentMode;
BOOL ModeChangeOk = FALSE;
static BOOL Recursion = FALSE;
// Check that the requested mode is theoretically supported by
// the device
if (!(lMode & m_TunerCaps.ModesSupported)) {
return E_INVALIDARG;
}
//
// Now tell all of the subtuners that the mode has changed
//
for (int j = 0; j < TuningMode_Last; j++) {
AMTunerModeType lModeOfTuner;
if (m_pModeList[j]) {
hr = m_pModeList[j]->get_Mode (&lModeOfTuner);
ASSERT (SUCCEEDED (hr));
hr = m_pModeList[j]->put_Mode (lMode);
ASSERT (SUCCEEDED (hr));
if (lModeOfTuner == lMode) {
ModeChangeOk = SUCCEEDED (hr);
m_pMode_Current = m_pModeList[j];
}
}
}
if (ModeChangeOk) {
m_CurrentMode = lMode;
}
else {
// Mode Change FAILED, try to put back the original mode!!!
if (!Recursion) {
ASSERT (FALSE);
Recursion = TRUE;
hr = put_Mode ((AMTunerModeType) OriginalMode);
Recursion = FALSE;
ASSERT (SUCCEEDED (hr));
}
}
return hr;
}
STDMETHODIMP
CTVTuner::get_Mode(
/* [out] */ AMTunerModeType *plMode)
{
BOOL fOk;
KSPROPERTY_TUNER_MODE_S TunerMode;
ULONG cbReturned;
if ( !m_hDevice )
return E_INVALIDARG;
*plMode = (AMTunerModeType) m_CurrentMode;
// Sanity check, confirm that the device mode matches our
// internal version
TunerMode.Property.Set = PROPSETID_TUNER;
TunerMode.Property.Id = KSPROPERTY_TUNER_MODE;
TunerMode.Property.Flags = KSPROPERTY_TYPE_GET;
fOk = KsControl(m_hDevice,
(DWORD) IOCTL_KS_PROPERTY,
&TunerMode, sizeof( KSPROPERTY_TUNER_MODE_S),
&TunerMode, sizeof( KSPROPERTY_TUNER_MODE_S),
&cbReturned);
if (!fOk || (m_CurrentMode != (AMTunerModeType) TunerMode.Mode)) {
DbgLog(( LOG_ERROR, 0,
TEXT("FAILED: KSPROPERTY_TUNER_MODE, KSPROPERTY_TYPE_GET, cbReturned = %d, Mode = %x"),
cbReturned, (AMTunerModeType) TunerMode.Mode));
}
return NOERROR;
}
STDMETHODIMP
CTVTuner::GetAvailableModes(
/* [out] */ long *plModes)
{
*plModes = m_TunerCaps.ModesSupported;
return NOERROR;
}
STDMETHODIMP
CTVTuner::get_AvailableTVFormats (long *pAnalogVideoStandard)
{
*pAnalogVideoStandard = 0;
ASSERT (m_pMode_Current);
return m_pMode_Current->get_AvailableTVFormats (pAnalogVideoStandard);
}
STDMETHODIMP
CTVTuner::get_TVFormat (long *plAnalogVideoStandard)
{
ASSERT (m_pMode_Current);
return m_pMode_Current->get_TVFormat (plAnalogVideoStandard);
}
STDMETHODIMP
CTVTuner::get_NumInputConnections (long * plNumInputConnections)
{
*plNumInputConnections = m_lTotalInputs;
return NOERROR;
}
STDMETHODIMP
CTVTuner::get_InputType (long lIndex, TunerInputType * pInputConnectionType)
{
if (lIndex < 0 || (lIndex >= m_lTotalInputs))
return HRESULT_FROM_WIN32 (ERROR_INVALID_PARAMETER);
*pInputConnectionType = m_pInputTypeArray[lIndex];
return NOERROR;
}
STDMETHODIMP
CTVTuner::put_InputType (long lIndex, TunerInputType InputConnectionType)
{
if (lIndex < 0 || lIndex >= m_lTotalInputs)
return HRESULT_FROM_WIN32 (ERROR_INVALID_PARAMETER);
m_pInputTypeArray[lIndex] = InputConnectionType;
// If we're changing the type of the currently selected input
if (lIndex == m_lInputIndex) {
m_CurrentInputType = m_pInputTypeArray[lIndex];
m_pListCurrent = ((m_CurrentInputType == TunerInputCable) ?
m_pListCable : m_pListBroadcast);
}
// Since we're changing the input type (cable vs broad), we need to retune
if (m_pMode_Current) {
long lChannel, lVideoSubChannel, AudioSubChannel;
get_Channel(&lChannel, &lVideoSubChannel, &AudioSubChannel);
put_Channel( lChannel, lVideoSubChannel, AudioSubChannel);
}
return NOERROR;
}
STDMETHODIMP
CTVTuner::put_ConnectInput (long lIndex)
{
if (lIndex < 0 || lIndex >= m_lTotalInputs)
return HRESULT_FROM_WIN32 (ERROR_INVALID_PARAMETER);
m_lInputIndex = lIndex;
m_CurrentInputType = m_pInputTypeArray[lIndex];
m_pListCurrent = ((m_CurrentInputType == TunerInputCable) ?
m_pListCable : m_pListBroadcast);
HW_SetInput (lIndex);
if (m_pMode_Current) {
long lChannel, lVideoSubChannel, AudioSubChannel;
get_Channel(&lChannel, &lVideoSubChannel, &AudioSubChannel);
put_Channel( lChannel, lVideoSubChannel, AudioSubChannel);
}
return NOERROR;
}
STDMETHODIMP
CTVTuner::get_ConnectInput (long *plIndex)
{
*plIndex = m_lInputIndex;
return NOERROR;
}
STDMETHODIMP
CTVTuner::get_VideoFrequency (long * plFreq)
{
ASSERT (m_pMode_Current);
return m_pMode_Current->get_VideoFrequency (plFreq);
}
STDMETHODIMP
CTVTuner::get_AudioFrequency (long * plFreq)
{
ASSERT (m_pMode_Current);
return m_pMode_Current->get_AudioFrequency (plFreq);
}
HRESULT
CTVTuner::DeliverChannelChangeInfo(KS_TVTUNER_CHANGE_INFO &ChangeInfo,
long Mode)
{
return m_pFilter->DeliverChannelChangeInfo(ChangeInfo, Mode);
}