windows-nt/Source/XPSP1/NT/multimedia/dshow/vidctl/msvidctl/msvidtvtuner.cpp
2020-09-26 16:20:57 +08:00

669 lines
22 KiB
C++

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