|
|
//==========================================================================;
//
// Copyright (c) Microsoft Corporation 1999-2000.
//
//--------------------------------------------------------------------------;
//
// MSVidTVTuner.cpp : Implementation of CMSVidTVTuner
//
#include "stdafx.h"
#ifndef TUNING_MODEL_ONLY
#include "perfcntr.h"
#include "MSVidCtl.h"
#include "MSVidTVTuner.h"
#include <bdamedia.h>
#include "segimpl.h"
#include "segimpl.h"
#include "devices.h"
const ULONG t_SVIDEO = 0; const ULONG t_COMPOSITE = 1; const ULONG t_TUNER = 2; DEFINE_EXTERN_OBJECT_ENTRY(CLSID_MSVidAnalogTunerDevice, CMSVidTVTuner)
const int DEFAULT_ANALOG_CHANNEL = 4;
typedef CComQIPtr<IMSVidCtl> PQMSVidCtl; typedef CComQIPtr<IMSVidVideoRenderer> PQMSVidVideoRenderer;
/////////////////////////////////////////////////////////////////////////////
// CMSVidTVTuner
STDMETHODIMP CMSVidTVTuner::Decompose(){ m_bRouted = false; return S_OK; }
STDMETHODIMP CMSVidTVTuner::ChannelAvailable(LONG nChannel, LONG * SignalStrength, VARIANT_BOOL * fSignalPresent){ VIDPERF_FUNC; if(!SignalStrength || !fSignalPresent){ return E_POINTER; } CComQIPtr<IAMAnalogVideoDecoder> qi_VidDec(m_Filters[m_iCapture]); if(qi_VidDec){ long signal = FALSE; HRESULT hr = qi_VidDec->get_HorizontalLocked(&signal); if(FAILED(hr)){ return hr; } *fSignalPresent = signal ? VARIANT_TRUE:VARIANT_FALSE; return NOERROR; } return E_NOINTERFACE; }
STDMETHODIMP CMSVidTVTuner::InterfaceSupportsErrorInfo(REFIID riid) { static const IID* arr[] = { &IID_IMSVidAnalogTuner }; for (int i=0; i < sizeof(arr) / sizeof(arr[0]); i++) { if (InlineIsEqualGUID(*arr[i],riid)) return S_OK; } return S_FALSE; } STDMETHODIMP CMSVidTVTuner::put_Tune(ITuneRequest *pTR) { VIDPERF_FUNC; TRACELM(TRACE_DETAIL, "CMSVidTVTuner<>::put_Tune()"); if (!m_fInit) { return Error(IDS_OBJ_NO_INIT, __uuidof(IMSVidTuner), CO_E_NOTINITIALIZED); } if (!pTR) { return E_POINTER; } try { TNTuneRequest req(pTR); ASSERT(req); // This whole next section would be nice to check, but due to aux in
// the Tuning Space may change
/*if (m_TS) {
// if this tuner has been initialized propertly it will have a tuning space
// that it handles already specified. in that case, we should only
// handle tune requests for our ts
TNTuningSpace ts(req.TuningSpace()); if (ts != m_TS) { return ImplReportError(__uuidof(T), IDS_INVALID_TS, __uuidof(IMSVidTuner), E_INVALIDARG); } } else { // undone: if dev init is correct this case should never occur
// return E_UNEXPECTED;
} */ HRESULT hr = S_OK; PQVidCtl pqCtl; if(!!m_pContainer){ hr = m_pContainer->QueryInterface(IID_IMSVidCtl, reinterpret_cast<void**>(&pqCtl)); if(FAILED(hr)){ return hr; } MSVidCtlStateList curState = STATE_UNBUILT; hr = pqCtl->get_State(&curState); if(SUCCEEDED(hr) && curState > STATE_STOP){ hr = DoTune(req); } else{ m_bRouted = false; hr = NOERROR; } } if (SUCCEEDED(hr)) { m_pCurrentTR = req; m_pCurrentTR.Clone(); if (!m_TS) { // undone: this is bad. temporary hack until dev init is correct.
m_TS = req.TuningSpace(); m_TS.Clone(); } } return hr; } catch(...) { return E_INVALIDARG; } } HRESULT CMSVidTVTuner::UpdateTR(TNTuneRequest &tr) { TNChannelTuneRequest ctr(tr);
// If we have not been routed yet, check the current tr first to make sure it is not set
// if we don't get_Tune wacks the tr currently set
if(!m_bRouted){ if(m_pCurrentTR){ TNChannelTuneRequest curTR(m_pCurrentTR); HRESULT hr = ctr->put_Channel(curTR.Channel()); if (FAILED(hr)) { return E_UNEXPECTED; } return NOERROR; } }
long channel; PQTVTuner ptv(m_Filters[m_iTuner]); long vs, as; HRESULT hr = ptv->get_Channel(&channel, &vs, &as); if (FAILED(hr)) { return E_UNEXPECTED; } hr = ctr->put_Channel(channel); if (FAILED(hr)) { return E_UNEXPECTED; } // undone: update the components
return NOERROR; } HRESULT CMSVidTVTuner::TwiddleXBar(ULONG dwInput){ // For Support for Aux Inputs
VIDPERF_FUNC; if(dwInput < 0 || dwInput > 2){ return E_INVALIDARG; } // Set up lists of audio and video types for use in routing data
int m_iDeMux = -1; MediaMajorTypeList VideoTypes; MediaMajorTypeList AudioTypes; if (!VideoTypes.size()) { VideoTypes.push_back(MEDIATYPE_Video); VideoTypes.push_back(MEDIATYPE_AnalogVideo);
}
if (!AudioTypes.size()) { AudioTypes.push_back(MEDIATYPE_Audio); AudioTypes.push_back(MEDIATYPE_AnalogAudio); }
// See how far we have to route the audio/video
PQVidCtl pqCtl; if(!!m_pContainer){ HRESULT hr = m_pContainer->QueryInterface(IID_IMSVidCtl, reinterpret_cast<void**>(&pqCtl)); if(FAILED(hr)){ return hr; }
PQFeatures fa; hr = pqCtl->get_FeaturesActive(&fa); if(FAILED(hr)){ return hr; }
CFeatures* pC = static_cast<CFeatures *>(fa.p); DeviceCollection::iterator i; for(i = pC->m_Devices.begin(); i != pC->m_Devices.end(); ++i){ if(VWGraphSegment(*i).ClassID() == CLSID_MSVidEncoder){ break; } }
if(i != pC->m_Devices.end()){ m_iDeMux = 1; } }
// Find the Capture Filter
DSFilter capFilter (m_Filters[m_iCapture]); if(!capFilter){ return E_FAIL; }
// Get the Crossbar
DSFilterList::iterator i; for(i = m_Filters.begin(); i != m_Filters.end(); ++i){ if((*i).IsXBar()){ break; } } if(i == m_Filters.end()){ return E_FAIL; }
// DSextend helper class
PQCrossbarSwitch qiXBar((*i)); if(!qiXBar){ return E_FAIL; }
// DSExtend does not have all the functions so get the filter as well
DSFilter bar(qiXBar); if(!bar){ return E_FAIL; }
// Variables for routing audio and video
DSFilter startFilter; DSPin audioStartPin, videoStartPin; VWStream vpath; VWStream apath;
// Setup startFilter and startPins if needed
if(dwInput == t_TUNER){ PQVidCtl pqCtl; HRESULT hr = m_pContainer->QueryInterface(IID_IMSVidCtl, reinterpret_cast<void**>(&pqCtl)); if(FAILED(hr)){ return hr; }
if(!pqCtl){ return E_FAIL; }
DSFilter tunerFilter(m_Filters[m_iTuner]); if(!tunerFilter){ return E_FAIL; }
startFilter = tunerFilter; if(!tunerFilter){ _ASSERT(false); return E_UNEXPECTED; }
} if(dwInput == t_SVIDEO || dwInput == t_COMPOSITE){ // Route Audio from Audio Line In
DSPin inAudio; DSPin inVideo; long inputs, outputs;
HRESULT hr = qiXBar->get_PinCounts(&outputs, &inputs); if(FAILED(hr)){ return E_FAIL; }
long physConn, audioConn; // set up the physical connnecter we are looking for
if(dwInput == t_SVIDEO){ physConn = PhysConn_Video_SVideo; } else if(dwInput == t_COMPOSITE){ physConn = PhysConn_Video_Composite; }
// always want line in
audioConn = PhysConn_Audio_Line; long audioIdx = -1; long videoIdx = -1;
// Look through all of the input pins looking for the audio and video input we need
for(long n = 0; n <= inputs; ++n){ long inRelate, inType; hr = qiXBar->get_CrossbarPinInfo(TRUE, n, &inRelate, &inType); if(FAILED(hr)){ continue; }
if(inType == physConn){ videoIdx = n; }
if(inType == audioConn){ audioIdx = n; } } if(videoIdx == audioIdx || videoIdx == -1 || audioIdx == -1){ return E_FAIL; }
long idx = -1;
// Crossbars are wank and dont return pins instead they return indexes so we need to find the pin
for(DSFilter::iterator foo = bar.begin(); foo != bar.end(); ++foo){ if((*foo).GetDirection() == PINDIR_INPUT){ ++idx; if(idx == videoIdx){ inVideo = (*foo); }
if(idx == audioIdx){ inAudio = (*foo); } } } if(!inAudio || !inVideo){ return E_FAIL; } startFilter = bar; audioStartPin = inAudio; videoStartPin = inVideo; if(!startFilter || !audioStartPin || !videoStartPin){ _ASSERT(false); return E_UNEXPECTED; } }
m_pGraph.BuildGraphPath(startFilter, capFilter, vpath, VideoTypes, DOWNSTREAM, videoStartPin); // undone: in win64 size() is really __int64. fix output operator for
// that type and remove cast
TRACELSM(TRACE_DETAIL, (dbgDump << "CVidCtl::RouteStreams routing video path of size " << (long)vpath.size()), ""); vpath.Route();
TRACELM(TRACE_DETAIL, "CVidCtl::RouteStreams finding audio path");
if(m_iDeMux > 0){ m_pGraph.BuildGraphPath(startFilter, capFilter, apath, AudioTypes, DOWNSTREAM, audioStartPin); apath.Route(); } else { VWGraphSegment::iterator i; // there's an analog filter and a digital filter in every audio renderer segment, try both until
// we find one that's connected.
CComQIPtr<IMSVidAudioRenderer> audioR; pqCtl->get_AudioRendererActive(&audioR); VWGraphSegment ar(audioR); if(!!ar){ for (i = ar.begin(); i != ar.end(); ++i) { m_pGraph.BuildGraphPath(startFilter, (*i), apath, AudioTypes, DOWNSTREAM, audioStartPin); if (apath.size()) { TRACELSM(TRACE_DETAIL, (dbgDump << "Analog tuner Twiddling for audio path of size " << (long)apath.size()), ""); apath.Route(); break; } } }
}
m_bRouted = true; return NOERROR; }
HRESULT CMSVidTVTuner::DoTune(TNTuneRequest &tr) { VIDPERF_FUNC; TRACELM(TRACE_DETAIL, "CMSVidTVTuner()::DoTune()"); // validate that this tuning request is one we can handle
TNChannelTuneRequest newTR(tr); if (!newTR) { return Error(IDS_INVALID_TR, __uuidof(IMSVidAnalogTuner), DISP_E_TYPEMISMATCH); }
TNChannelTuneRequest curTR(m_pCurrentTR); TNAnalogTVTuningSpace ats; ats = newTR.TuningSpace(); if (!ats) { //return Error(IDS_INVALID_TR, __uuidof(IMSVidAnalogTuner), E_INVALIDARG);
//********************************************************************//
// MOGUL "FIX": //
// Support for Analog Tuners that output mpeg //
//********************************************************************//
TNAuxInTuningSpace auxts; auxts = newTR.TuningSpace(); if(!auxts){ return Error(IDS_INVALID_TR, __uuidof(IMSVidAnalogTuner), E_INVALIDARG); }
// if the graph isn't built don't do any more.
if (m_iTuner == -1) { return S_FALSE; //Error(IDS_NP_NOT_INIT, __uuidof(IMSVidAnalogTuner), S_FALSE);
}
long channel = newTR.Channel(); // Default is SVideo
if (channel == -1) { channel = t_SVIDEO; }
// Check to see if the m_pCurrentTR is the same type as the one we are tuning to
TNAuxInTuningSpace curTS(m_pCurrentTR.TuningSpace());
if(!m_bRouted || !curTS || !curTR || curTR.Channel() != channel){ if(channel == t_SVIDEO){ HRESULT hr = TwiddleXBar(t_SVIDEO); } else if(channel == t_COMPOSITE){ HRESULT hr = TwiddleXBar(t_COMPOSITE); } else{ return Error(IDS_INVALID_TR, __uuidof(IMSVidAnalogTuner), E_INVALIDARG); } } //********************************************************************//
// END "FIX" //
//********************************************************************//
} else{ // if the graph isn't built don't do any more.
if (m_iTuner == -1) { return S_FALSE; //Error(IDS_NP_NOT_INIT, __uuidof(IMSVidAnalogTuner), S_FALSE);
}
PQTVTuner ptv(m_Filters[m_iTuner]); if(!ptv){ return E_NOINTERFACE; }
long channel = newTR.Channel(); if (channel == -1) { channel = DEFAULT_ANALOG_CHANNEL; }
long curChannel = -1; if(curTR){ curChannel = curTR.Channel(); }
long curInputType = ats.InputType(); long curCountryCode = ats.CountryCode(); TNAnalogTVTuningSpace curTS; if(curTR){ curTS = curTR.TuningSpace(); if(curTS){ curInputType = curTS.InputType(); curCountryCode = curTS.CountryCode(); } } bool bXbarTwiddled = false; if(!m_bRouted || !curTR || curInputType != ats.InputType() || curCountryCode != ats.CountryCode() || !curTS || curTS != ats){ HRESULT hr = TwiddleXBar(t_TUNER); if(FAILED(hr)){ return hr; } TunerInputType ti = ats.InputType(); hr = ptv->put_InputType(0, ti); if (FAILED(hr)) { return Error(IDS_CANT_SET_INPUTTYPE, __uuidof(IMSVidAnalogTuner), E_UNEXPECTED); }
long countrycode = ats.CountryCode(); hr = ptv->put_CountryCode(countrycode); if (FAILED(hr)) { return Error(IDS_CANT_SET_COUNTRYCODE, __uuidof(IMSVidAnalogTuner), E_UNEXPECTED); } bXbarTwiddled = true; }
if(channel != curChannel || bXbarTwiddled){ // undone: use components to determine subchannel stuff
HRESULT hr = ptv->put_Channel(channel, AMTUNER_SUBCHAN_DEFAULT, AMTUNER_SUBCHAN_DEFAULT); if (FAILED(hr)) { return Error(IDS_CANT_SET_CHANNEL, __uuidof(IMSVidAnalogTuner), hr); } }
} if (!m_pBcast) { PQServiceProvider sp(m_pGraph); if (!sp) { TRACELM(TRACE_ERROR, "CMSVidTVTuner::DoTune() can't get service provider i/f"); return Error(IDS_CANT_NOTIFY_CHANNEL_CHANGE, __uuidof(IMSVidAnalogTuner), E_UNEXPECTED); }
HRESULT hr = sp->QueryService(SID_SBroadcastEventService, IID_IBroadcastEvent, reinterpret_cast<LPVOID*>(&m_pBcast)); if (FAILED(hr) || !m_pBcast) { hr = m_pBcast.CoCreateInstance(CLSID_BroadcastEventService, 0, CLSCTX_INPROC_SERVER); if (FAILED(hr)) { TRACELM(TRACE_ERROR, "CMSVidTVTuner::DoTune() can't create bcast service"); return Error(IDS_CANT_NOTIFY_CHANNEL_CHANGE, __uuidof(IMSVidAnalogTuner), E_UNEXPECTED); }
PQRegisterServiceProvider rsp(m_pGraph); if (!rsp) { TRACELM(TRACE_ERROR, "CMSVidTVTuner::DoTune() can't get get register service provider i/f"); return Error(IDS_CANT_NOTIFY_CHANNEL_CHANGE, __uuidof(IMSVidAnalogTuner), E_UNEXPECTED); }
hr = rsp->RegisterService(SID_SBroadcastEventService, m_pBcast); if (FAILED(hr)) { TRACELSM(TRACE_ERROR, (dbgDump << "CMSVidTVTuner::DoTune() can't get register service provider. hr = " << hexdump(hr)), ""); return Error(IDS_CANT_NOTIFY_CHANNEL_CHANGE, __uuidof(IMSVidAnalogTuner), E_UNEXPECTED); } } }
ASSERT(m_pBcast); m_pBcast->Fire(EVENTID_TuningChanged); return NOERROR; }
HRESULT CMSVidTVTuner::put_Container(IMSVidGraphSegmentContainer *pCtl) { if (!m_fInit) { return Error(IDS_OBJ_NO_INIT, __uuidof(IMSVidAnalogTuner), CO_E_NOTINITIALIZED); } try { CPerfCounter pCounterTuner; pCounterTuner.Reset(); if (!pCtl) { return Unload(); } if (m_pContainer) { if (!m_pContainer.IsEqualObject(VWSegmentContainer(pCtl))) { return Error(IDS_OBJ_ALREADY_INIT, __uuidof(IMSVidAnalogTuner), CO_E_ALREADYINITIALIZED); } else { return NO_ERROR; } } // DON'T addref the container. we're guaranteed nested lifetimes
// and an addref creates circular refcounts so we never unload.
m_pContainer.p = pCtl; m_pGraph = m_pContainer.GetGraph(); DSFilter pTuner(m_pGraph.AddMoniker(m_pDev)); if (!pTuner) { return E_UNEXPECTED; } m_Filters.push_back(pTuner); m_iTuner = 0; TRACELM(TRACE_DETAIL, "CMSVidTVTuner::put_Container() tuner added"); pCounterTuner.Stop(); TRACELSM(TRACE_ERROR, (dbgDump << " CVidCtl:: PutContainer TVTuner Filter: " << (unsigned long)(pCounterTuner.GetLastTime() / _100NS_IN_MS) << "." << (unsigned long)(pCounterTuner.GetLastTime() % _100NS_IN_MS) << " ms"), ""); pCounterTuner.Reset(); if (!m_pSystemEnum) { m_pSystemEnum = PQCreateDevEnum(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER); if (!m_pSystemEnum) { return E_UNEXPECTED; } } pCounterTuner.Stop(); TRACELSM(TRACE_ERROR, (dbgDump << " CVidCtl:: PutContainer TVTuner SysEnum: " << (unsigned long)(pCounterTuner.GetLastTime() / _100NS_IN_MS) << "." << (unsigned long)(pCounterTuner.GetLastTime() % _100NS_IN_MS) << " ms"), ""); pCounterTuner.Reset(); DSDevices CaptureList(m_pSystemEnum, KSCATEGORY_CAPTURE); DSDevices::iterator i; DSFilter Capture; DSFilterList intermediates; try { ASSERT(m_iTuner > -1); for (i = CaptureList.begin(); i != CaptureList.end(); ++i) { CString csName; Capture = m_pGraph.LoadFilter(*i, csName); if (!Capture) { continue; } TRACELSM(TRACE_DETAIL, (dbgDump << "CMSVidTVTuner::put_Container() found not video capture filter = " << csName), ""); if (!IsVideoFilter(Capture)) { continue; } TRACELSM(TRACE_DETAIL, (dbgDump << "CMSVidTVTuner::put_Container() found video capture filter = " << csName), ""); HRESULT hr = m_pGraph.AddFilter(Capture, csName); if (FAILED(hr)) { continue; } hr = m_pGraph.Connect(m_Filters[m_iTuner], Capture, intermediates); pCounterTuner.Stop(); TRACELSM(TRACE_ERROR, (dbgDump << " CVidCtl:: PutContainer Capture Filter: " << (unsigned long)(pCounterTuner.GetLastTime() / _100NS_IN_MS) << "." << (unsigned long)(pCounterTuner.GetLastTime() % _100NS_IN_MS) << " ms"), ""); pCounterTuner.Reset(); if (SUCCEEDED(hr)) { break; } TRACELM(TRACE_DETAIL, "CMSVidTVTuner::put_Container() removing unconnectable capture filter"); m_pGraph.RemoveFilter(Capture); } if (i == CaptureList.end()) { TRACELM(TRACE_ERROR, "CMSVidTVTuner::put_Container() can't find valid capture"); return Error(IDS_NO_CAPTURE, __uuidof(IMSVidAnalogTuner), E_NOINTERFACE); } m_Filters.insert(m_Filters.end(), intermediates.begin(), intermediates.end()); m_iTuner = 0; ASSERT(m_iTuner > -1); } catch(ComException &e) { return e; } m_Filters.push_back(Capture); m_iCapture = m_Filters.size() - 1; m_iTuner = 0; ASSERT(m_iTuner > -1 && m_iCapture > 0 && m_iCapture != m_iTuner); TRACELM(TRACE_DETAIL, "CMSVidTVTuner::put_Container() tuner connected"); pCounterTuner.Stop(); TRACELSM(TRACE_ERROR, (dbgDump << " CVidCtl:: PutContainer TVTuner added to list: " << (unsigned long)(pCounterTuner.GetLastTime() / _100NS_IN_MS) << "." << (unsigned long)(pCounterTuner.GetLastTime() % _100NS_IN_MS) << " ms"), "");
pCounterTuner.Reset(); HRESULT hr = BroadcastAdvise(); if (FAILED(hr)) { TRACELM(TRACE_ERROR, "CMSVidTVTuner::put_Container() can't advise for broadcast events"); return E_UNEXPECTED; } TRACELM(TRACE_DETAIL, "CMSVidTVTuner::put_Container() registered for tuning changed events"); pCounterTuner.Stop(); TRACELSM(TRACE_ERROR, (dbgDump << " CVidCtl:: PutContainer Rest : " << (unsigned long)(pCounterTuner.GetLastTime() / _100NS_IN_MS) << "." << (unsigned long)(pCounterTuner.GetLastTime() % _100NS_IN_MS) << " ms"), "");
} catch (ComException &e) { return e; } catch(...) { return E_UNEXPECTED; } return NOERROR; }
HRESULT CMSVidTVTuner::Build() { HRESULT hr = put_SAP(VARIANT_FALSE); if(FAILED(hr)){ TRACELM(TRACE_ERROR, "CVidCtl put_sap failed"); //ASSERT(false);
}
PQMSVidCtl pv(m_pContainer); if (!pv) { return E_UNEXPECTED; }
PQMSVidVideoRenderer pvr; hr = pv->get_VideoRendererActive(&pvr); if (FAILED(hr) || !pvr) { return NOERROR; // video disabled, no vr present
}
hr = pvr->put_SourceSize(sslClipByOverScan); if (FAILED(hr)) { return hr; }
return pvr->put_OverScan(DEFAULT_OVERSCAN_PCT); }
#endif //TUNING_MODEL_ONLY
// end of file - msvidtvtuner.cpp
|