/// <summary> /// Creates the Graph instance which represents an analog graph /// </summary> /// <param name="xmlNode">The graph xml node</param> /// <returns>Graph instance</returns> public static Graph CreateInstance(XmlNode xmlNode) { Graph graph = new Graph(); XmlNode tunerNode = null; XmlNode tvAudioNode = null; XmlNode crossbarNode = null; XmlNode captureNode = null; XmlNode teletextNode = null; if (xmlNode != null) { tunerNode = xmlNode.SelectSingleNode("tuner"); tvAudioNode = xmlNode.SelectSingleNode("tvAudio"); crossbarNode = xmlNode.SelectSingleNode("crossbar"); captureNode = xmlNode.SelectSingleNode("capture"); teletextNode = xmlNode.SelectSingleNode("teletext"); } graph.Tuner = Tuner.CreateInstance(tunerNode); graph.TvAudio = TvAudio.CreateInstance(tvAudioNode); graph.Crossbar = Crossbar.CreateInstance(crossbarNode); graph.Capture = Capture.CreateInstance(captureNode); graph.Teletext = Teletext.CreateInstance(teletextNode); return graph; }
/// <summary> /// Detects the capabilities of the tv audio device /// </summary> private void CheckCapabilities(Graph graph) { TVAudioMode availableAudioModes; _tvAudioTunerInterface.GetHardwareSupportedTVAudioModes(out availableAudioModes); graph.TvAudio.AudioModes = availableAudioModes; if ((availableAudioModes & (TVAudioMode.Stereo)) != 0) { AnalogAudioStream stream = new AnalogAudioStream(); stream.AudioMode = TVAudioMode.Stereo; stream.Language = "Stereo"; streams.Add(stream); } if ((availableAudioModes & (TVAudioMode.Mono)) != 0) { AnalogAudioStream stream = new AnalogAudioStream(); stream.AudioMode = TVAudioMode.Mono; stream.Language = "Mono"; streams.Add(stream); } if ((availableAudioModes & (TVAudioMode.LangA)) != 0) { AnalogAudioStream stream = new AnalogAudioStream(); stream.AudioMode = TVAudioMode.LangA; stream.Language = "LangA"; streams.Add(stream); } if ((availableAudioModes & (TVAudioMode.LangB)) != 0) { AnalogAudioStream stream = new AnalogAudioStream(); stream.AudioMode = TVAudioMode.LangB; stream.Language = "LangB"; streams.Add(stream); } if ((availableAudioModes & (TVAudioMode.LangC)) != 0) { AnalogAudioStream stream = new AnalogAudioStream(); stream.AudioMode = TVAudioMode.LangC; stream.Language = "LangC"; streams.Add(stream); } }
/// <summary> /// Creates the filter by trying to detect it /// </summary> /// <param name="crossbar">The crossbar componen</param> /// <param name="tuner">The tuner component</param> /// <param name="graph">The stored graph</param> /// <param name="graphBuilder">The graphBuilder</param> /// <returns>true, if the graph building was successful</returns> private bool CreateAutomaticFilterInstance(Graph graph, Tuner tuner, Crossbar crossbar, IFilterGraph2 graphBuilder) { //get all tv audio tuner devices on this system DsDevice[] devices = null; try { devices = DsDevice.GetDevicesOfCat(FilterCategory.AMKSTVAudio); devices = DeviceSorter.Sort(devices, tuner.TunerName, crossbar.CrossBarName); } catch (Exception) { Log.Log.WriteFile("analog: AddTvAudioFilter no tv audio devices found - Trying TvTuner filter"); } if (devices != null && devices.Length > 0) { // try each tv audio tuner for (int i = 0; i < devices.Length; i++) { IBaseFilter tmp; Log.Log.WriteFile("analog: AddTvAudioFilter try:{0} {1}", devices[i].Name, i); //if tv audio tuner is currently in use we can skip it if (DevicesInUse.Instance.IsUsed(devices[i])) continue; int hr; try { //add tv audio tuner to graph hr = graphBuilder.AddSourceFilterForMoniker(devices[i].Mon, null, devices[i].Name, out tmp); } catch (Exception) { Log.Log.WriteFile("analog: cannot add filter to graph"); continue; } if (hr != 0) { //failed to add tv audio tuner to graph, continue with the next one if (tmp != null) { graphBuilder.RemoveFilter(tmp); Release.ComObject("tvAudioFilter filter", tmp); } continue; } // try connecting the tv tuner-> tv audio tuner if (FilterGraphTools.ConnectPin(graphBuilder, tuner.AudioPin, tmp, 0)) { // Got it ! // Connect tv audio tuner to the crossbar IPin pin = DsFindPin.ByDirection(tmp, PinDirection.Output, 0); hr = graphBuilder.Connect(pin, crossbar.AudioTunerIn); if (hr < 0) { //failed graphBuilder.RemoveFilter(tmp); Release.ComObject("audiotuner pinin", pin); Release.ComObject("audiotuner filter", tmp); } else { //succeeded. we're done Log.Log.WriteFile("analog: AddTvAudioFilter succeeded:{0}", devices[i].Name); Release.ComObject("audiotuner pinin", pin); _filterTvAudioTuner = tmp; _audioDevice = devices[i]; DevicesInUse.Instance.Add(_audioDevice); _tvAudioTunerInterface = tuner.Filter as IAMTVAudio; break; } } else { // cannot connect tv tuner-> tv audio tuner, try next one... graphBuilder.RemoveFilter(tmp); Release.ComObject("audiotuner filter", tmp); } } } if (_filterTvAudioTuner == null) { Log.Log.WriteFile("analog: AddTvAudioFilter no tv audio devices found - Trying TvTuner filter"); int hr = graphBuilder.Connect(tuner.AudioPin, crossbar.AudioTunerIn); if (hr != 0) { Log.Log.Error("analog: unable to add TvAudioTuner to graph - even TvTuner as TvAudio fails"); mode = TvAudioVariant.Unavailable; } else { Log.Log.WriteFile("analog: AddTvAudioFilter connected TvTuner with Crossbar directly succeeded!"); mode = TvAudioVariant.TvTunerConnection; _tvAudioTunerInterface = tuner.Filter as IAMTVAudio; if (_tvAudioTunerInterface != null) { Log.Log.WriteFile("analog: AddTvAudioFilter succeeded - TvTuner is also TvAudio"); _filterTvAudioTuner = tuner.Filter; mode = TvAudioVariant.TvTuner; } } graph.TvAudio.Mode = mode; } else { mode = TvAudioVariant.Normal; graph.TvAudio.Name = _audioDevice.Name; } if (mode != TvAudioVariant.Unavailable && mode != TvAudioVariant.TvTunerConnection && _tvAudioTunerInterface != null) { CheckCapabilities(graph); } return true; }
/// <summary> /// Adds the tv audio tuner to the graph and connects it to the crossbar. /// At the end of this method the graph looks like: /// [ ] ------------------------->[ ] /// [ tvtuner ] [ crossbar ] /// [ ]----[ ]-------->[ ] /// [ tvaudio ] /// [ tuner ] /// </summary> /// <param name="crossbar">The crossbar componen</param> /// <param name="tuner">The tuner component</param> /// <param name="graph">The stored graph</param> /// <param name="graphBuilder">The graphBuilder</param> /// <returns>true, if the graph building was successful</returns> public bool CreateFilterInstance(Graph graph, IFilterGraph2 graphBuilder, Tuner tuner, Crossbar crossbar) { streams = new List<IAudioStream>(); if (!string.IsNullOrEmpty(graph.TvAudio.Name) && graph.TvAudio.Mode != TvAudioVariant.Unavailable) { Log.Log.WriteFile("analog: Using TvAudio configuration from stored graph"); if (CreateConfigurationBasedFilterInstance(graph, tuner, crossbar, graphBuilder)) { Log.Log.WriteFile("analog: Using TvAudio configuration from stored graph succeeded"); return true; } } if (tuner.AudioPin == null) { Log.Log.WriteFile("analog: AddTvAudioFilter no tv audio device needed!"); mode = TvAudioVariant.Unavailable; return true; } Log.Log.WriteFile("analog: No stored graph for TvAudio component - Trying to detect"); return CreateAutomaticFilterInstance(graph, tuner, crossbar, graphBuilder); }
/// <summary> /// Creates the filter by trying to detect it /// </summary> /// <param name="tuner">The tuner component</param> /// <param name="graph">The stored graph</param> /// <param name="graphBuilder">The graphBuilder</param> /// <returns>true, if the graph building was successful</returns> private bool CreateAutomaticFilterInstance(Graph graph, IFilterGraph2 graphBuilder, Tuner tuner) { _audioTunerIn = null; DsDevice[] devices; //get list of all crossbar devices installed on this system try { devices = DsDevice.GetDevicesOfCat(FilterCategory.AMKSCrossbar); devices = DeviceSorter.Sort(devices, graph.Tuner.Name); } catch (Exception) { Log.Log.WriteFile("analog: AddCrossBarFilter no crossbar devices found"); return false; } if (devices == null || devices.Length == 0) { Log.Log.WriteFile("analog: AddCrossBarFilter no crossbar devices found"); return false; } //try each crossbar for (int i = 0; i < devices.Length; i++) { IBaseFilter tmp; Log.Log.WriteFile("analog: AddCrossBarFilter try:{0} {1}", devices[i].Name, i); //if crossbar is already in use then we can skip it if (DevicesInUse.Instance.IsUsed(devices[i])) continue; int hr; try { //add the crossbar to the graph hr = graphBuilder.AddSourceFilterForMoniker(devices[i].Mon, null, devices[i].Name, out tmp); } catch (Exception) { Log.Log.WriteFile("analog: cannot add filter to graph"); continue; } if (hr != 0) { //failed. try next crossbar if (tmp != null) { graphBuilder.RemoveFilter(tmp); Release.ComObject("CrossBarFilter", tmp); } continue; } _crossBarFilter = (IAMCrossbar)tmp; CheckCapabilities(); if (_videoOutPinIndex == -1) { Log.Log.WriteFile("analog: AddCrossbarFilter no video output found"); graphBuilder.RemoveFilter(tmp); _crossBarFilter = null; Release.ComObject("CrossBarFilter", tmp); continue; } // Check that the crossbar has a tuner video input pin. IPin pinIn = null; if (_videoPinMap.ContainsKey(AnalogChannel.VideoInputType.Tuner)) { pinIn = DsFindPin.ByDirection(tmp, PinDirection.Input, _videoPinMap[AnalogChannel.VideoInputType.Tuner]); } if (pinIn == null) { // no pin found, continue with next crossbar Log.Log.WriteFile("analog: AddCrossBarFilter no video tuner input pin detected"); if (tmp != null) { graphBuilder.RemoveFilter(tmp); _crossBarFilter = null; Release.ComObject("CrossBarFilter", tmp); } continue; } //connect tv tuner->crossbar int tempVideoPinIndex; if (FilterGraphTools.ConnectFilter(graphBuilder, tuner.Filter, pinIn, out tempVideoPinIndex)) { // Got it, we're done _filterCrossBar = tmp; _crossBarDevice = devices[i]; DevicesInUse.Instance.Add(_crossBarDevice); if (_audioTunerIn == null) { _audioTunerIn = DsFindPin.ByDirection(_filterCrossBar, PinDirection.Input, _audioPinMap[AnalogChannel.AudioInputType.Tuner]); } Release.ComObject("crossbar videotuner pin", pinIn); _videoOut = DsFindPin.ByDirection(_filterCrossBar, PinDirection.Output, _videoOutPinIndex); if (_audioOutPinIndex != -1) { _audioOut = DsFindPin.ByDirection(_filterCrossBar, PinDirection.Output, _audioOutPinIndex); } Log.Log.WriteFile("analog: AddCrossBarFilter succeeded"); graph.Crossbar.AudioOut = _audioOutPinIndex; graph.Crossbar.AudioPinMap = _audioPinMap; graph.Crossbar.Name = _crossBarDevice.Name; graph.Crossbar.VideoOut = _videoOutPinIndex; graph.Crossbar.VideoPinMap = _videoPinMap; graph.Crossbar.VideoPinRelatedAudioMap = _videoPinRelatedAudioMap; graph.Tuner.VideoPin = tempVideoPinIndex; break; } // cannot connect tv tuner to crossbar, try next crossbar device graphBuilder.RemoveFilter(tmp); Release.ComObject("crossbar videotuner pin", pinIn); Release.ComObject("crossbar filter", tmp); } return _filterCrossBar != null; }
/// <summary> /// Creates the filter based on the configuration file /// </summary> /// <param name="tuner">The tuner component</param> /// <param name="graph">The stored graph</param> /// <param name="graphBuilder">The graphBuilder</param> /// <returns>true, if the graph building was successful</returns> private bool CreateConfigurationBasedFilterInstance(Graph graph, IFilterGraph2 graphBuilder, Tuner tuner) { string deviceName = graph.Crossbar.Name; _audioTunerIn = null; DsDevice[] devices; //get list of all crossbar devices installed on this system try { devices = DsDevice.GetDevicesOfCat(FilterCategory.AMKSCrossbar); devices = DeviceSorter.Sort(devices, graph.Tuner.Name); } catch (Exception) { Log.Log.WriteFile("analog: AddCrossBarFilter no crossbar devices found"); return false; } if (devices == null || devices.Length == 0) { Log.Log.WriteFile("analog: AddCrossBarFilter no crossbar devices found"); return false; } //try each crossbar for (int i = 0; i < devices.Length; i++) { IBaseFilter tmp; //if crossbar is already in use then we can skip it if (DevicesInUse.Instance.IsUsed(devices[i])) continue; if (!deviceName.Equals(devices[i].Name)) continue; Log.Log.WriteFile("analog: AddCrossBarFilter use:{0} {1}", devices[i].Name, i); int hr; try { //add the crossbar to the graph hr = graphBuilder.AddSourceFilterForMoniker(devices[i].Mon, null, devices[i].Name, out tmp); } catch (Exception) { Log.Log.WriteFile("analog: cannot add filter to graph"); continue; } if (hr != 0) { //failed. try next crossbar if (tmp != null) { graphBuilder.RemoveFilter(tmp); Release.ComObject("CrossBarFilter", tmp); } continue; } _crossBarFilter = (IAMCrossbar)tmp; _videoPinMap = graph.Crossbar.VideoPinMap; _audioPinMap = graph.Crossbar.AudioPinMap; _videoPinRelatedAudioMap = graph.Crossbar.VideoPinRelatedAudioMap; _videoOutPinIndex = graph.Crossbar.VideoOut; _audioOutPinIndex = graph.Crossbar.AudioOut; if (_videoOutPinIndex == -1) { Log.Log.WriteFile("analog: AddCrossbarFilter no video output found"); graphBuilder.RemoveFilter(tmp); _crossBarFilter = null; Release.ComObject("CrossBarFilter", tmp); continue; } //connect tv tuner->crossbar IPin tunerOut = DsFindPin.ByDirection(tuner.Filter, PinDirection.Output, graph.Tuner.VideoPin); if (tunerOut != null && _videoPinMap.ContainsKey(AnalogChannel.VideoInputType.Tuner) && FilterGraphTools.ConnectPin(graphBuilder, tunerOut, tmp, _videoPinMap[AnalogChannel.VideoInputType.Tuner])) { // Got it, we're done _filterCrossBar = tmp; _crossBarDevice = devices[i]; DevicesInUse.Instance.Add(_crossBarDevice); if (_audioTunerIn == null) { _audioTunerIn = DsFindPin.ByDirection(_filterCrossBar, PinDirection.Input, _audioPinMap[AnalogChannel.AudioInputType.Tuner]); } Release.ComObject("tuner video out", tunerOut); _videoOut = DsFindPin.ByDirection(_filterCrossBar, PinDirection.Output, _videoOutPinIndex); if (_audioOutPinIndex != -1) { _audioOut = DsFindPin.ByDirection(_filterCrossBar, PinDirection.Output, _audioOutPinIndex); } Log.Log.WriteFile("analog: AddCrossBarFilter succeeded"); break; } // cannot connect tv tuner to crossbar, try next crossbar device if (tmp != null) { graphBuilder.RemoveFilter(tmp); Release.ComObject("crossbarFilter filter", tmp); } if (tunerOut != null) { Release.ComObject("tuner video out", tunerOut); } } return _filterCrossBar != null; }
/// <summary> /// Adds the cross bar filter to the graph and connects the tv tuner to the crossbar. /// at the end of this method the graph looks like: /// [tv tuner]----->[crossbar] /// </summary> /// <param name="tuner">The tuner component</param> /// <param name="graph">The stored graph</param> /// <param name="graphBuilder">The graphBuilder</param> /// <returns>true, if the graph building was successful</returns> public bool CreateFilterInstance(Graph graph, IFilterGraph2 graphBuilder, Tuner tuner) { if (!string.IsNullOrEmpty(graph.Crossbar.Name)) { Log.Log.WriteFile("analog: Using Crossbar configuration from stored graph"); if (CreateConfigurationBasedFilterInstance(graph, graphBuilder, tuner)) { Log.Log.WriteFile("analog: Using Crossbar configuration from stored graph succeeded"); return true; } } Log.Log.WriteFile("analog: No stored or invalid graph for Crossbar component - Trying to detect"); return CreateAutomaticFilterInstance(graph, graphBuilder, tuner); }
/// <summary> /// Checks the capabilities of the tuner device /// </summary> /// <returns>true, if the checks were successful</returns> private bool CheckCapabilities(Graph graph) { if (_tuner == null) { Log.Log.WriteFile(""); return false; } UpdateMinMaxChannel(); AMTunerModeType tunerModes; _tuner.GetAvailableModes(out tunerModes); _supportsFMRadio = (AMTunerModeType.FMRadio & tunerModes) != 0; _supportsAMRadio = (AMTunerModeType.AMRadio & tunerModes) != 0; if (_supportsFMRadio && !_supportsAMRadio) { graph.Tuner.RadioMode = RadioMode.FM; } if (!_supportsFMRadio && _supportsAMRadio) { graph.Tuner.RadioMode = RadioMode.AM; } if (_supportsFMRadio && _supportsAMRadio) { graph.Tuner.RadioMode = RadioMode.FM | RadioMode.AM; } return true; }
/// <summary> /// Creates the tuner filter instance /// </summary> /// <param name="graph">The stored graph</param> /// <param name="graphBuilder">The graphbuilder</param> /// <returns>true, if the graph building was successful</returns> public bool CreateFilterInstance(Graph graph, IFilterGraph2 graphBuilder) { Log.Log.WriteFile("analog: AddTvTunerFilter {0}", _tunerDevice.Name); if (DevicesInUse.Instance.IsUsed(_tunerDevice)) return false; IBaseFilter tmp; int hr; try { hr = graphBuilder.AddSourceFilterForMoniker(_tunerDevice.Mon, null, _tunerDevice.Name, out tmp); } catch (Exception) { Log.Log.WriteFile("analog: cannot add filter to graph"); return false; } if (hr != 0) { Log.Log.Error("analog: AddTvTunerFilter failed:0x{0:X}", hr); throw new TvException("Unable to add tvtuner to graph"); } _filterTvTuner = tmp; DevicesInUse.Instance.Add(_tunerDevice); _tuner = _filterTvTuner as IAMTVTuner; if (string.IsNullOrEmpty(graph.Tuner.Name) || !_tunerDevice.Name.Equals( graph.Tuner.Name)) { Log.Log.WriteFile("analog: Detecting capabilities of the tuner"); graph.Tuner.Name = _tunerDevice.Name; int index; _audioPin = FilterGraphTools.FindMediaPin(_filterTvTuner, MediaType.AnalogAudio, MediaSubType.Null, PinDirection.Output, out index); graph.Tuner.AudioPin = index; return CheckCapabilities(graph); } Log.Log.WriteFile("analog: Using stored capabilities of the tuner"); _audioPin = DsFindPin.ByDirection(_filterTvTuner, PinDirection.Output, graph.Tuner.AudioPin); _supportsFMRadio = (graph.Tuner.RadioMode & RadioMode.FM) != 0; _supportsAMRadio = (graph.Tuner.RadioMode & RadioMode.AM) != 0; return true; }
/// <summary> /// Creates the teletext component in the graph. First we try to use the stored informations in the graph /// </summary> /// <param name="graph">The stored graph</param> /// <param name="graphBuilder">The graphbuilder</param> /// <param name="capture">The capture component</param> /// <returns>true, if the building was successful; false otherwise</returns> public bool CreateFilterInstance(Graph graph, IFilterGraph2 graphBuilder, Capture capture) { Log.Log.WriteFile("analog: SetupTeletext()"); Guid guidBaseFilter = typeof (IBaseFilter).GUID; object obj; //find and add tee/sink to sink filter DsDevice[] devices = DsDevice.GetDevicesOfCat(FilterCategory.AMKSSplitter); devices[0].Mon.BindToObject(null, null, ref guidBaseFilter, out obj); _teeSink = (IBaseFilter)obj; int hr = graphBuilder.AddFilter(_teeSink, devices[0].Name); if (hr != 0) { Log.Log.Error("analog:SinkGraphEx.SetupTeletext(): Unable to add tee/sink filter"); return false; } //connect capture filter -> tee sink filter IPin pin = DsFindPin.ByDirection(_teeSink, PinDirection.Input, 0); hr = graphBuilder.Connect(capture.VBIPin, pin); Release.ComObject(pin); if (hr != 0) { //failed... Log.Log.Error("analog: unable to connect capture->tee/sink"); graphBuilder.RemoveFilter(_teeSink); Release.ComObject(_teeSink); _teeSink = _filterWstDecoder = null; return false; } if (!string.IsNullOrEmpty(graph.Teletext.Name)) { Log.Log.WriteFile("analog: Using Teletext-Component configuration from stored graph"); devices = DsDevice.GetDevicesOfCat(graph.Teletext.Category); foreach (DsDevice device in devices) { if (device.Name != null && device.Name.Equals(graph.Teletext.Name)) { //found it, add it to the graph Log.Log.Info("analog:Using teletext component - {0}", graph.Teletext.Name); device.Mon.BindToObject(null, null, ref guidBaseFilter, out obj); _filterWstDecoder = (IBaseFilter)obj; hr = graphBuilder.AddFilter(_filterWstDecoder, device.Name); if (hr != 0) { //failed... Log.Log.Error("analog:SinkGraphEx.SetupTeletext(): Unable to add WST Codec filter"); graphBuilder.RemoveFilter(_filterWstDecoder); _filterWstDecoder = null; } break; } } } if (_filterWstDecoder == null) { Log.Log.WriteFile("analog: No stored or invalid graph for Teletext component - Trying to detect"); //find the WST codec filter devices = DsDevice.GetDevicesOfCat(FilterCategory.AMKSVBICodec); foreach (DsDevice device in devices) { if (device.Name != null && device.Name.IndexOf("WST") >= 0) { //found it, add it to the graph Log.Log.Info("analog:Found WST Codec filter"); device.Mon.BindToObject(null, null, ref guidBaseFilter, out obj); _filterWstDecoder = (IBaseFilter)obj; hr = graphBuilder.AddFilter(_filterWstDecoder, device.Name); if (hr != 0) { //failed... Log.Log.Error("analog:Unable to add WST Codec filter"); graphBuilder.RemoveFilter(_teeSink); Release.ComObject(_teeSink); _teeSink = _filterWstDecoder = null; return false; } graph.Teletext.Name = device.Name; graph.Teletext.Category = FilterCategory.AMKSVBICodec; break; } } //Look for VBI Codec for Vista users as Vista doesn't use WST Codec anymore if (_filterWstDecoder == null) { devices = DsDevice.GetDevicesOfCat(FilterCategory.AMKSMULTIVBICodec); foreach (DsDevice device in devices) if (device.Name != null && device.Name.IndexOf("VBI") >= 0) { //found it, add it to the graph Log.Log.Info("analog:Found VBI Codec filter"); device.Mon.BindToObject(null, null, ref guidBaseFilter, out obj); _filterWstDecoder = (IBaseFilter)obj; hr = graphBuilder.AddFilter(_filterWstDecoder, device.Name); if (hr != 0) { //failed... Log.Log.Error("analog:Unable to add VBI Codec filter"); graphBuilder.RemoveFilter(_teeSink); Release.ComObject(_teeSink); _teeSink = _filterWstDecoder = null; return false; } graph.Teletext.Name = device.Name; graph.Teletext.Category = FilterCategory.AMKSMULTIVBICodec; break; } } } if (_filterWstDecoder == null) { Log.Log.Error("analog: unable to find WST Codec or VBI Codec filter"); graphBuilder.RemoveFilter(_teeSink); Release.ComObject(_teeSink); _teeSink = _filterWstDecoder = null; return false; } //connect tee sink filter-> wst codec filter IPin pinOut = DsFindPin.ByDirection(_teeSink, PinDirection.Output, 0); pin = DsFindPin.ByDirection(_filterWstDecoder, PinDirection.Input, 0); hr = graphBuilder.Connect(pinOut, pin); Release.ComObject(pin); Release.ComObject(pinOut); if (hr != 0) { //failed Log.Log.Error("analog: unable to tee/sink->wst codec"); graphBuilder.RemoveFilter(_filterWstDecoder); graphBuilder.RemoveFilter(_teeSink); Release.ComObject(_filterWstDecoder); Release.ComObject(_teeSink); _teeSink = _filterWstDecoder = null; _teeSink = null; graph.Teletext.Name = null; graph.Teletext.Category = new Guid(); return false; } //done Log.Log.WriteFile("analog: teletext setup"); if (_filterWstDecoder != null) { Log.Log.WriteFile("analog:connect wst/vbi codec->tsfilesink"); _pinWST_VBI = DsFindPin.ByDirection(_filterWstDecoder, PinDirection.Output, 0); } return true; }