/// <summary> /// Initializes a new instance of the <see cref="AnalogSubChannel"/> class. /// </summary> internal AnalogSubChannel(TvCardAnalog card, int subchnnelId, TvAudio tvAudio, bool hasTeletext, IBaseFilter mpFileWriter) { _card = card; _hasTeletext = hasTeletext; _tvAudio = tvAudio; _mpFileWriter = mpFileWriter; _mpRecord = (IMPRecord)_mpFileWriter; _mpRecord.AddChannel(ref _subChannelId); _subChannelId = subchnnelId; }
/// <summary> /// Adds the tv capture to the graph and connects it to the crossbar. /// At the end of this method the graph looks like: /// [ ] ------------------------->[ ]------>[ ] /// [ tvtuner ] [ crossbar ] [ video capture ] /// [ ]----[ ]-------->[ ]------>[ filter ] /// [ tvaudio ] /// [ tuner ] /// </summary> /// <param name="tvAudio">The tvaudio component</param> /// <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> /// <param name="capBuilder">The Capture graph builder</param> /// <returns>true, if the graph building was successful</returns> public bool CreateFilterInstance(Graph graph, ICaptureGraphBuilder2 capBuilder, IFilterGraph2 graphBuilder, Tuner tuner, Crossbar crossbar, TvAudio tvAudio) { if (!string.IsNullOrEmpty(graph.Capture.Name)) { Log.Log.WriteFile("analog: Using Capture configuration from stored graph"); if (CreateConfigurationBasedFilterInstance(graph, capBuilder, graphBuilder, tuner, crossbar, tvAudio)) { Log.Log.WriteFile("analog: Using Capture configuration from stored graph succeeded"); return(true); } } Log.Log.WriteFile("analog: No stored or invalid graph for Capture component - Trying to detect"); return(CreateAutomaticFilterInstance(graph, capBuilder, graphBuilder, tuner, crossbar, tvAudio)); }
/// <summary> /// Creates the filter by trying to detect it /// </summary> /// <param name="tvAudio">The tvaudio component</param> /// <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> /// <param name="capBuilder">The Capture graph builder</param> /// <returns>true, if the graph building was successful</returns> private bool CreateAutomaticFilterInstance(Graph graph, ICaptureGraphBuilder2 capBuilder, IFilterGraph2 graphBuilder, Tuner tuner, Crossbar crossbar, TvAudio tvAudio) { DsDevice[] devices; bool videoConnected = false; bool audioConnected = false; //get a list of all video capture devices try { if (tuner.TunerName == "Adaptec USB TvTuner") { Log.Log.WriteFile("analog: Adaptec USB device detected!"); devices = DsDevice.GetDevicesOfCat(FilterCategory.VideoInputDevice); } else { devices = DsDevice.GetDevicesOfCat(FilterCategory.AMKSCapture); devices = DeviceSorter.Sort(devices, tuner.TunerName, tvAudio.TvAudioName, crossbar.CrossBarName); } } catch (Exception) { Log.Log.WriteFile("analog: AddTvCaptureFiler error in allocating devices collection"); return(false); } if (devices.Length == 0) { Log.Log.WriteFile("analog: AddTvCaptureFilter no tvcapture devices found"); return(false); } //try each video capture filter for (int i = 0; i < devices.Length; i++) { bool filterUsed = false; IBaseFilter tmp; if (_badCaptureDevices.Contains(devices[i].Name)) { Log.Log.WriteFile("analog: AddTvCaptureFilter bypassing: {0}", devices[i].Name); continue; } Log.Log.WriteFile("analog: AddTvCaptureFilter try:{0} {1}", devices[i].Name, i); // if video capture filter is in use, then we can skip it if (DevicesInUse.Instance.IsUsed(devices[i])) { continue; } int hr; try { // add video capture filter 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) { //cannot add video capture filter to graph, try next one if (tmp != null) { graphBuilder.RemoveFilter(tmp); Release.ComObject("TvCaptureFilter", tmp); } continue; } int destinationIndex; // connect crossbar->video capture filter if (!videoConnected && FilterGraphTools.ConnectFilter(graphBuilder, crossbar.VideoOut, tmp, out destinationIndex)) { _filterVideoCapture = tmp; _videoCaptureDevice = devices[i]; if (_audioCaptureDevice != _videoCaptureDevice) { DevicesInUse.Instance.Add(_videoCaptureDevice); } Log.Log.WriteFile("analog: AddTvCaptureFilter connected video to crossbar successfully"); graph.Capture.Name = _videoCaptureDevice.Name; graph.Capture.VideoIn = destinationIndex; videoConnected = true; filterUsed = true; } // crossbar->audio capture filter // Many video capture are also the audio capture filter, so we can always try it again if (videoConnected && FilterGraphTools.ConnectFilter(graphBuilder, crossbar.AudioOut, tmp, out destinationIndex)) { _filterAudioCapture = tmp; _audioCaptureDevice = devices[i]; if (_audioCaptureDevice != _videoCaptureDevice) { DevicesInUse.Instance.Add(_audioCaptureDevice); } Log.Log.WriteFile("analog: AddTvCaptureFilter connected audio to crossbar successfully"); graph.Capture.AudioCaptureName = devices[i].Name; graph.Capture.AudioIn = destinationIndex; audioConnected = true; filterUsed = true; } // _audioCaptureDevice should never be null - avoids null exception crashes with Encoder.cs else { _audioCaptureDevice = devices[i]; } if (!filterUsed) { // cannot connect crossbar->video capture filter, remove filter from graph // cand continue with the next vieo capture filter Log.Log.WriteFile("analog: AddTvCaptureFilter failed to connect to crossbar"); graphBuilder.RemoveFilter(tmp); Release.ComObject("capture filter", tmp); } else { i = -1; // Go through the devices again from the start... } if (videoConnected && audioConnected) { break; } } if (_filterVideoCapture != null) { FindVBIPin(graph); CheckCapabilities(graph, capBuilder); } return(_filterVideoCapture != null); }
/// <summary> /// Creates the filter based on the configuration file /// </summary> /// <param name="tvAudio">The tvaudio component</param> /// <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> /// <param name="capBuilder">The Capture graph builder</param> /// <returns>true, if the graph building was successful</returns> private bool CreateConfigurationBasedFilterInstance(Graph graph, ICaptureGraphBuilder2 capBuilder, IFilterGraph2 graphBuilder, Tuner tuner, Crossbar crossbar, TvAudio tvAudio) { string audioDeviceName = graph.Capture.AudioCaptureName; string videoDeviceName = graph.Capture.Name; DsDevice[] devices; bool videoConnected = false; bool audioConnected = false; //get a list of all video capture devices try { if (tuner.TunerName == "Adaptec USB TvTuner") { Log.Log.WriteFile("analog: Adaptec USB device detected!"); devices = DsDevice.GetDevicesOfCat(FilterCategory.VideoInputDevice); } else { devices = DsDevice.GetDevicesOfCat(FilterCategory.AMKSCapture); //shouldn't be VideoInputDevice devices = DeviceSorter.Sort(devices, tuner.TunerName, tvAudio.TvAudioName, crossbar.CrossBarName); } } catch (Exception) { Log.Log.WriteFile("analog: AddTvCaptureFiler error in allocating devices collection"); return(false); } if (devices.Length == 0) { Log.Log.WriteFile("analog: AddTvCaptureFilter no tvcapture devices found"); return(false); } //try each video capture filter for (int i = 0; i < devices.Length; i++) { bool filterUsed = false; IBaseFilter tmp; if (_badCaptureDevices.Contains(devices[i].Name)) { Log.Log.WriteFile("analog: AddTvCaptureFilter bypassing: {0}", devices[i].Name); continue; } Log.Log.WriteFile("analog: AddTvCaptureFilter try:{0} {1}", devices[i].Name, i); // if video capture filter is in use, then we can skip it if (DevicesInUse.Instance.IsUsed(devices[i])) { Log.Log.WriteFile("analog: Device: {0} in use?", devices[i].Name); continue; } if (!videoDeviceName.Equals(devices[i].Name) && (audioDeviceName == null || !audioDeviceName.Equals(devices[i].Name))) { continue; } int hr; try { // add video capture filter 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) { //cannot add video capture filter to graph, try next one if (tmp != null) { Log.Log.WriteFile("analog: cannot add filter: {0} to graph", devices[i].Name); graphBuilder.RemoveFilter(tmp); Release.ComObject("TvCaptureFilter", tmp); } continue; } // connect crossbar->video capture filter if (videoDeviceName.Equals(devices[i].Name) && FilterGraphTools.ConnectPin(graphBuilder, crossbar.VideoOut, tmp, graph.Capture.VideoIn)) { _filterVideoCapture = tmp; _videoCaptureDevice = devices[i]; if (_audioCaptureDevice != _videoCaptureDevice) { DevicesInUse.Instance.Add(_videoCaptureDevice); } Log.Log.WriteFile("analog: AddTvCaptureFilter connected video to crossbar successfully"); videoConnected = true; filterUsed = true; } // crossbar->audio capture filter // Many video capture are also the audio capture filter, so we can always try it again if (audioDeviceName.Equals(devices[i].Name) && FilterGraphTools.ConnectPin(graphBuilder, crossbar.AudioOut, tmp, graph.Capture.AudioIn)) { _filterAudioCapture = tmp; _audioCaptureDevice = devices[i]; if (_audioCaptureDevice != _videoCaptureDevice) { DevicesInUse.Instance.Add(_audioCaptureDevice); } Log.Log.WriteFile("analog: AddTvCaptureFilter connected audio to crossbar successfully"); audioConnected = true; filterUsed = true; } // _audioCaptureDevice should never be null - avoids null exception crashes with Encoder.cs else { _audioCaptureDevice = devices[i]; } if (!filterUsed) { // cannot connect crossbar->video capture filter, remove filter from graph // cand continue with the next vieo capture filter Log.Log.WriteFile("analog: AddTvCaptureFilter failed to connect to crossbar"); graphBuilder.RemoveFilter(tmp); Release.ComObject("capture filter", tmp); } else { i = -1; // Go through the devices again from the start... } if (videoConnected && audioConnected) { break; } } if (_filterVideoCapture != null) { if (graph.Capture.TeletextPin != -1) { _pinVBI = DsFindPin.ByDirection(_filterVideoCapture, PinDirection.Output, graph.Capture.TeletextPin); } _videoProcAmp = _filterVideoCapture as IAMVideoProcAmp; _analogVideoDecoder = _filterVideoCapture as IAMAnalogVideoDecoder; _streamConfig = _filterVideoCapture as IAMStreamConfig; _videoFormats = graph.Capture.AvailableVideoStandard; _defaultVideoProcAmpValues = graph.Capture.VideoProcAmpValues; _frameRate = graph.Capture.FrameRate; _imageWidth = graph.Capture.ImageWidth; _imageHeight = graph.Capture.ImageHeight; CheckCapabilitiesStreamConfig(graph, capBuilder); SetCaptureConfiguration(graph); } return(_filterVideoCapture != null); }
/// <summary> /// Creates the encoder component /// </summary> /// <param name="_graphBuilder">The graph builder</param> /// <param name="_tuner">The tuner component</param> /// <param name="_tvAudio">The tvaudio component</param> /// <param name="_crossbar">The crossbar component</param> /// <param name="_capture">The capture component</param> /// <returns>true, if the building was successful; false otherwise</returns> public bool CreateFilterInstance(IFilterGraph2 _graphBuilder, Tuner _tuner, TvAudio _tvAudio, Crossbar _crossbar, Capture _capture) { _layer = new TvBusinessLayer(); // now things get difficult. // Here we can have the following situations: // 1. we're done, the video capture filter has a mpeg-2 audio output pin // 2. we need to add 1 encoder filter which converts both the audio/video output pins // of the video capture filter to mpeg-2 // 3. we need to potentially mux the mpeg-2 video with audio. i.e. Nvidia NVTV Dual Tuner capture cards. // 4. we need to add 2 mpeg-2 encoder filters for software cards. One for audio and one for video // after the 2 encoder filters, a multiplexer will be added which takes the output of both // encoders and generates mpeg-2 //situation 1. we look if the video capture device has an mpeg-2 output pin (media type:stream) FindCapturePin(MediaType.Stream, MediaSubType.Null, _capture.VideoFilter); //specific workaround for the Plextor COnvertX devices if (_tuner.IsPlextorCard()) { Log.Log.Info("analog: Plextor ConvertX TV402U detected"); _isPlextorConvertX = true; //fake the capture pin to the Plextor media type & subtype FindCapturePin(MediaType.Video, MediaSubtype_Plextor, _capture.VideoFilter); //Find the audio pin FindAudioVideoPins(_capture); //Add the audio encoder AddAudioCompressor(_graphBuilder); //Add the Plextor specific InterVideo mux & gets the new capture pin. AddInterVideoMuxer(_graphBuilder, _capture); } if (_pinCapture == null) { // no it does not. So we have situation 2, 3 or 4 and first need to add 1 or more encoder filters // First we try only to add encoders where the encoder pin names are the same as the // output pins of the capture filters and we search only for filter which have an mpeg2-program stream output pin if (!AddTvEncoderFilter(true, true, _graphBuilder, _tuner, _tvAudio, _crossbar, _capture)) { //if that fails, we try any encoder filter with an mpeg2-program stream output pin if (!AddTvEncoderFilter(false, true, _graphBuilder, _tuner, _tvAudio, _crossbar, _capture)) { // If that fails, we try to add encoder where the encoder pin names are the same as the // output pins of the capture filters, and now except encoders except with mpeg2-ts output pin if (!AddTvEncoderFilter(true, false, _graphBuilder, _tuner, _tvAudio, _crossbar, _capture)) { // If that fails, we try every encoder except encoders except with mpeg2-ts output pin AddTvEncoderFilter(false, false, _graphBuilder, _tuner, _tvAudio, _crossbar, _capture); } } } // 1 or 2 encoder filters have been added. // check if the encoder filters supply a mpeg-2 output pin FindCapturePin(MediaType.Stream, MediaSubType.Null, _filterVideoEncoder); // not as a stream, but perhaps its supplied with another media type if (_pinCapture == null) FindCapturePin(MediaType.Video, MediaSubType.Mpeg2Program, _filterVideoEncoder); if (_pinCapture == null) { //still no mpeg output found, we move on to situation 3. We need to add a multiplexer // First we try only to add multiplexers where the multiplexer pin names are the same as the // output pins of the encoder filters //for the NVTV filter the pin names dont match .. so check first in bool eval and thus skips // trying AddTvMultiPlexer with matching pinnames when using NVTV if (_tuner.IsNvidiaCard() || !AddTvMultiPlexer(true, _graphBuilder, _tuner, _tvAudio, _crossbar, _capture)) { //if that fails, we try any multiplexer filter AddTvMultiPlexer(false, _graphBuilder, _tuner, _tvAudio, _crossbar, _capture); } } } // multiplexer filter now has been added. // check if the encoder multiplexer supply a mpeg-2 output pin if (_pinCapture == null) { FindCapturePin(MediaType.Stream, MediaSubType.Null, _filterMultiplexer); if (_pinCapture == null) FindCapturePin(MediaType.Video, MediaSubType.Mpeg2Program, _filterMultiplexer); } if (_pinCapture == null) { // Still no mpeg-2 output pin found // looks like this is a s/w encoding card if (!FindAudioVideoPins(_capture)) { Log.Log.WriteFile("analog: failed to find audio/video pins"); throw new Exception("No analog audio/video pins found"); } if (!AddAudioCompressor(_graphBuilder)) { Log.Log.WriteFile("analog: failed to add audio compressor. You must install a supported audio encoder!"); throw new TvExceptionSWEncoderMissing( "No audio compressor filter found. You must install a supported audio encoder!"); } if (!AddVideoCompressor(_graphBuilder)) { Log.Log.WriteFile("analog: failed to add video compressor. You must install a supported video encoder!"); throw new TvExceptionSWEncoderMissing( "No video compressor filter found. You must install a supported video encoder!"); } if (FilterGraphTools.GetFilterName(_filterAudioCompressor).Contains("InterVideo Audio Encoder")) { if (!AddInterVideoMuxer(_graphBuilder, _capture)) { Log.Log.WriteFile("analog: failed to add intervideo muxer"); throw new Exception("No intervideo muxer filter found"); } } else { if (!AddAnalogMuxer(_graphBuilder)) { Log.Log.WriteFile("analog: failed to add analog muxer"); throw new Exception("No analog muxer filter found"); } } } //Certain ATI cards have pin names which don't match etc. if (_capture.VideoCaptureName.Contains("ATI AVStream Analog Capture") || _capture.AudioCaptureName.Contains("ATI AVStream Analog Capture")) { Log.Log.WriteFile("analog: ATI AVStream Analog Capture card detected adding mux"); AddTvMultiPlexer(false, _graphBuilder, _tuner, _tvAudio, _crossbar, _capture); FindCapturePin(MediaType.Stream, MediaSubType.Mpeg2Program, _filterMultiplexer); } //add the mpeg-2 demultiplexer filter AddMpeg2Demultiplexer(_graphBuilder); if (!AddMpegMuxer(_graphBuilder, _capture)) { throw new TvException("Analog: unable to add mpeg muxer"); } return true; }
/// <summary> /// Adds one or 2 encoder filters to the graph /// several posibilities /// 1. no encoder filter needed /// 2. single encoder filter with seperate audio/video inputs and 1 (mpeg-2) output /// 3. single encoder filter with a mpeg2 program stream input (I2S) /// 4. two encoder filters. one for audio and one for video /// /// At the end of this method the graph looks like: /// /// option 2: one encoder filter, with 2 inputs /// [ ]----->[ ] /// [ capture filter ] [ encoder filter ] /// [ ]----->[ ] /// /// /// option 3: one encoder filter, with 1 input /// [ ] [ ] /// [ capture filter ]----->[ encoder filter ] /// [ ] [ ] /// /// /// option 4: 2 encoder filters one for audio and one for video /// [ ]----->[ video ] /// [ capture filter ] [ encoder filter ] /// [ ] [ ] /// [ ] /// [ ]----->[ audio ] /// [ encoder filter ] /// [ ] /// /// </summary> /// <param name="matchPinNames">if set to <c>true</c> the pin names of the encoder filter should match the pin names of the capture filter.</param> /// <param name="mpeg2ProgramFilter">if set to <c>true</c> than only encoders with an mpeg2 program output pins are accepted</param> /// <param name="_graphBuilder">GraphBuilder</param> /// <param name="_tuner">Tuner</param> /// <param name="_tvAudio">TvAudio</param> /// <param name="_crossbar">Crossbar</param> /// <param name="_capture">Capture</param> /// <returns>true if encoder filters are added, otherwise false</returns> private bool AddTvEncoderFilter(bool matchPinNames, bool mpeg2ProgramFilter, IFilterGraph2 _graphBuilder, Tuner _tuner, TvAudio _tvAudio, Crossbar _crossbar, Capture _capture) { Log.Log.WriteFile("analog: AddTvEncoderFilter - MatchPinNames: {0} - MPEG2ProgramFilter: {1}", matchPinNames, mpeg2ProgramFilter); bool finished = false; DsDevice[] devices; // first get all encoder filters available on this system try { devices = DsDevice.GetDevicesOfCat(FilterCategory.WDMStreamingEncoderDevices); devices = DeviceSorter.Sort(devices, _tuner.TunerName, _tvAudio.TvAudioName, _crossbar.CrossBarName, _capture.VideoCaptureName, _capture.AudioCaptureName, _videoEncoderDevice, _audioEncoderDevice, _multiplexerDevice); } catch (Exception) { Log.Log.WriteFile("analog: AddTvEncoderFilter no encoder devices found (Exception)"); return false; } if (devices == null) { Log.Log.WriteFile("analog: AddTvEncoderFilter no encoder devices found (devices == null)"); return false; } if (devices.Length == 0) { Log.Log.WriteFile("analog: AddTvEncoderFilter no encoder devices found"); return false; } //for each encoder Log.Log.WriteFile("analog: AddTvEncoderFilter found:{0} encoders", devices.Length); for (int i = 0; i < devices.Length; i++) { IBaseFilter tmp; //if encoder is in use, we can skip it if (DevicesInUse.Instance.IsUsed(devices[i])) { Log.Log.WriteFile("analog: skip :{0} (inuse)", devices[i].Name); continue; } Log.Log.WriteFile("analog: try encoder:{0} {1}", devices[i].Name, i); int hr; try { //add encoder filter to graph hr = _graphBuilder.AddSourceFilterForMoniker(devices[i].Mon, null, devices[i].Name, out tmp); } catch (Exception) { Log.Log.WriteFile("analog: cannot add filter {0} to graph", devices[i].Name); continue; } if (hr != 0) { //failed to add filter to graph, continue with the next one if (tmp != null) { _graphBuilder.RemoveFilter(tmp); Release.ComObject("TvEncoderFilter", tmp); } continue; } if (tmp == null) continue; // Encoder has been added to the graph // Now some cards have 2 encoder types, one for mpeg-2 transport stream and one for // mpeg-2 program stream. We dont want the mpeg-2 transport stream ! // So first we check the output pins... // and dont accept filters which have a mpeg-ts output pin.. // get the output pin bool isTsFilter = mpeg2ProgramFilter; IPin pinOut = DsFindPin.ByDirection(tmp, PinDirection.Output, 0); if (pinOut != null) { //check which media types it support IEnumMediaTypes enumMediaTypes; pinOut.EnumMediaTypes(out enumMediaTypes); if (enumMediaTypes != null) { int fetched; AMMediaType[] mediaTypes = new AMMediaType[20]; enumMediaTypes.Next(20, mediaTypes, out fetched); if (fetched > 0) { for (int media = 0; media < fetched; ++media) { //check if media is mpeg-2 transport if (mediaTypes[media].majorType == MediaType.Stream && mediaTypes[media].subType == MediaSubType.Mpeg2Transport) { isTsFilter = true; } //check if media is mpeg-2 program if (mediaTypes[media].majorType == MediaType.Stream && mediaTypes[media].subType == MediaSubType.Mpeg2Program) { isTsFilter = false; break; } // NVTV dual tuner needs this one to make it work so dont skip it if (mediaTypes[media].majorType == MediaType.Video && mediaTypes[media].subType == new Guid("be626472-fe7c-4a21-9f0b-d8f18b5ab441")) /*MediaSubType.?? */ { isTsFilter = false; break; } } } } Release.ComObject("pinout", pinOut); } //if encoder has mpeg-2 ts output pin, then we skip it and continue with the next one if (isTsFilter) { Log.Log.WriteFile("analog: filter {0} does not have mpeg-2 ps output or is a mpeg-2 ts filters", devices[i].Name); _graphBuilder.RemoveFilter(tmp); Release.ComObject("TvEncoderFilter", tmp); continue; } // get the input pins of the encoder (can be 1 or 2 inputs) IPin pin1 = DsFindPin.ByDirection(tmp, PinDirection.Input, 0); IPin pin2 = DsFindPin.ByDirection(tmp, PinDirection.Input, 1); if (pin1 != null) Log.Log.WriteFile("analog: encoder in-pin1:{0}", FilterGraphTools.LogPinInfo(pin1)); if (pin2 != null) Log.Log.WriteFile("analog: encoder in-pin2:{0}", FilterGraphTools.LogPinInfo(pin2)); // if the encoder has 2 input pins then this means it has seperate inputs for audio and video if (pin1 != null && pin2 != null) { // try to connect the capture device -> encoder filters.. if (ConnectEncoderFilter(tmp, true, true, matchPinNames, _graphBuilder, _capture)) { //succeeded, encoder has been added and we are done _filterVideoEncoder = tmp; _videoEncoderDevice = devices[i]; DevicesInUse.Instance.Add(_videoEncoderDevice); Log.Log.WriteFile("analog: AddTvEncoderFilter succeeded (encoder with 2 inputs)"); // success = true; finished = true; tmp = null; } } else if (pin1 != null) { //encoder filter only has 1 input pin. //First we get the media type of this pin to determine if its audio of video IEnumMediaTypes enumMedia; AMMediaType[] media = new AMMediaType[20]; int fetched; pin1.EnumMediaTypes(out enumMedia); enumMedia.Next(1, media, out fetched); if (fetched == 1) { //media type found Log.Log.WriteFile("analog: AddTvEncoderFilter encoder output major:{0} sub:{1}", media[0].majorType, media[0].subType); //is it audio? if (media[0].majorType == MediaType.Audio) { //yes, pin is audio //then connect the encoder to the audio output pin of the capture filter if (ConnectEncoderFilter(tmp, false, true, matchPinNames, _graphBuilder, _capture)) { //this worked. but we're not done yet. We probably need to add a video encoder also _filterAudioEncoder = tmp; _audioEncoderDevice = devices[i]; DevicesInUse.Instance.Add(_audioEncoderDevice); // success = true; Log.Log.WriteFile("analog: AddTvEncoderFilter succeeded (audio encoder)"); // if video encoder was already added, then we're done. if (_filterVideoEncoder != null) finished = true; tmp = null; } } else { //pin is video //then connect the encoder to the video output pin of the capture filter if (ConnectEncoderFilter(tmp, true, false, matchPinNames, _graphBuilder, _capture)) { //this worked. but we're not done yet. We probably need to add a audio encoder also _filterVideoEncoder = tmp; _videoEncoderDevice = devices[i]; DevicesInUse.Instance.Add(_videoEncoderDevice); // success = true; Log.Log.WriteFile("analog: AddTvEncoderFilter succeeded (video encoder)"); // if audio encoder was already added, then we're done. if (_filterAudioEncoder != null) finished = true; tmp = null; } } DsUtils.FreeAMMediaType(media[0]); } else { // filter does not report any media type (which is strange) // we must do something, so we treat it as a video input pin Log.Log.WriteFile("analog: AddTvEncoderFilter no media types for pin1"); //?? if (ConnectEncoderFilter(tmp, true, false, matchPinNames, _graphBuilder, _capture)) { _filterVideoEncoder = tmp; _videoEncoderDevice = devices[i]; DevicesInUse.Instance.Add(_videoEncoderDevice); // success = true; Log.Log.WriteFile("analog: AddTvEncoderFilter succeeded"); finished = true; tmp = null; } } } else { Log.Log.WriteFile("analog: AddTvEncoderFilter no pin1"); } if (pin1 != null) Release.ComObject("encoder pin0", pin1); if (pin2 != null) Release.ComObject("encoder pin1", pin2); if (tmp != null) { _graphBuilder.RemoveFilter(tmp); Release.ComObject("encoder filter", tmp); } if (finished) { Log.Log.WriteFile("analog: AddTvEncoderFilter succeeded 3"); return true; } } //for (int i = 0; i < devices.Length; i++) Log.Log.WriteFile("analog: AddTvEncoderFilter no encoder found"); return false; }
/// <summary> /// Adds the multiplexer filter to the graph. /// several posibilities /// 1. no tv multiplexer needed /// 2. tv multiplexer filter which is connected to a single encoder filter /// 3. tv multiplexer filter which is connected to two encoder filter (audio/video) /// 4. tv multiplexer filter which is connected to the capture filter /// at the end this method the graph looks like this: /// /// option 2: single encoder filter /// [ ]----->[ ] [ ] /// [ capture filter ] [ encoder filter ]----->[ multiplexer ] /// [ ]----->[ ] [ ] /// /// /// option 3: dual encoder filters /// [ ]----->[ video ] /// [ capture filter ] [ encoder filter ]------>[ ] /// [ ] [ ] [ ] /// [ ] [ multiplexer ] /// [ ]----->[ audio ]------>[ ] /// [ encoder filter ] /// [ ] /// /// option 4: no encoder filter /// [ ]----->[ ] /// [ capture filter ] [ multiplexer ] /// [ ]----->[ ] /// </summary> /// <param name="matchPinNames">if set to <c>true</c> the pin names of the multiplexer filter should match the pin names of the encoder filter.</param> /// <param name="_graphBuilder">GraphBuilder</param> /// <param name="_tuner">Tuner</param> /// <param name="_tvAudio">TvAudio</param> /// <param name="_crossbar">Crossbar</param> /// <param name="_capture">Capture</param> /// <returns>true if encoder filters are added, otherwise false</returns> private bool AddTvMultiPlexer(bool matchPinNames, IFilterGraph2 _graphBuilder, Tuner _tuner, TvAudio _tvAudio, Crossbar _crossbar, Capture _capture) { //Log.Log.WriteFile("analog: AddTvMultiPlexer"); DsDevice[] devicesHW; DsDevice[] devicesSW; DsDevice[] devices; //get a list of all multiplexers available on this system try { devicesHW = DsDevice.GetDevicesOfCat(FilterCategory.WDMStreamingMultiplexerDevices); devicesHW = DeviceSorter.Sort(devicesHW, _tuner.TunerName, _tvAudio.TvAudioName, _crossbar.CrossBarName, _capture.VideoCaptureName, _capture.AudioCaptureName, _videoEncoderDevice, _audioEncoderDevice, _multiplexerDevice); // also add the SoftWare Multiplexers in case no compatible HardWare multiplexer is found (NVTV cards) devicesSW = _tuner.IsNvidiaCard() ? DsDevice.GetDevicesOfCat(FilterCategory.MediaMultiplexerCategory) : new DsDevice[0]; devices = new DsDevice[devicesHW.Length + devicesSW.Length]; int nr = 0; for (int i = 0; i < devicesHW.Length; ++i) devices[nr++] = devicesHW[i]; for (int i = 0; i < devicesSW.Length; ++i) devices[nr++] = devicesSW[i]; } catch (Exception ex) { Log.Log.WriteFile("analog: AddTvMultiPlexer no multiplexer devices found (Exception) " + ex.Message); return false; } if (devices.Length == 0) { Log.Log.WriteFile("analog: AddTvMultiPlexer no multiplexer devices found"); return false; } //for each multiplexer for (int i = 0; i < devices.Length; i++) { IBaseFilter tmp; Log.Log.WriteFile("analog: AddTvMultiPlexer try:{0} {1}", devices[i].Name, i); // if multiplexer is in use, we can skip it if (DevicesInUse.Instance.IsUsed(devices[i])) continue; int hr; try { //add multiplexer 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 it to graph, continue with the next multiplexer if (tmp != null) { _graphBuilder.RemoveFilter(tmp); Release.ComObject("multiplexer filter", tmp); } continue; } // try to connect the multiplexer to encoders/capture devices if (ConnectMultiplexer(tmp, matchPinNames, _graphBuilder, _tuner, _capture)) { // succeeded, we're done _filterMultiplexer = tmp; _multiplexerDevice = devices[i]; DevicesInUse.Instance.Add(_multiplexerDevice); Log.Log.WriteFile("analog: AddTvMultiPlexer succeeded"); break; } // unable to connect it, remove the filter and continue with the next one _graphBuilder.RemoveFilter(tmp); Release.ComObject("multiplexer filter", tmp); } if (_filterMultiplexer == null) { Log.Log.WriteFile("analog: no TvMultiPlexer found"); return false; } return true; }
/// <summary> /// Builds the directshow graph for this analog tvcard /// </summary> public override void BuildGraph() { if (_cardId == 0) { GetPreloadBitAndCardId(); _configuration = Configuration.readConfiguration(_cardId, _name, _devicePath); Configuration.writeConfiguration(_configuration); } _lastSignalUpdate = DateTime.MinValue; _tunerLocked = false; Log.Log.WriteFile("analog: build graph"); try { if (_graphState != GraphState.Idle) { Log.Log.WriteFile("analog: Graph already build"); throw new TvException("Graph already build"); } //create a new filter graph _graphBuilder = (IFilterGraph2) new FilterGraph(); _rotEntry = new DsROTEntry(_graphBuilder); _capBuilder = (ICaptureGraphBuilder2) new CaptureGraphBuilder2(); _capBuilder.SetFiltergraph(_graphBuilder); Graph graph = _configuration.Graph; _tuner = new Tuner(_device); if (!_tuner.CreateFilterInstance(graph, _graphBuilder)) { Log.Log.Error("analog: unable to add tv tuner filter"); throw new TvException("Analog: unable to add tv tuner filter"); } _minChannel = _tuner.MinChannel; _maxChannel = _tuner.MaxChannel; //add the wdm crossbar device and connect tvtuner->crossbar _crossbar = new Crossbar(); if (!_crossbar.CreateFilterInstance(graph, _graphBuilder, _tuner)) { Log.Log.Error("analog: unable to add tv crossbar filter"); throw new TvException("Analog: unable to add tv crossbar filter"); } //add the tv audio tuner device and connect it to the crossbar _tvAudio = new TvAudio(); if (!_tvAudio.CreateFilterInstance(graph, _graphBuilder, _tuner, _crossbar)) { Log.Log.Error("analog: unable to add tv audio tuner filter"); throw new TvException("Analog: unable to add tv audio tuner filter"); } //add the tv capture device and connect it to the crossbar _capture = new Capture(); if (!_capture.CreateFilterInstance(graph, _capBuilder, _graphBuilder, _tuner, _crossbar, _tvAudio)) { Log.Log.Error("analog: unable to add capture filter"); throw new TvException("Analog: unable to add capture filter"); } Configuration.writeConfiguration(_configuration); _teletext = new TeletextComponent(); if (_capture.SupportsTeletext) { if (!_teletext.CreateFilterInstance(graph, _graphBuilder, _capture)) { Log.Log.Error("analog: unable to setup teletext filters"); throw new TvException("Analog: unable to setup teletext filters"); } } Configuration.writeConfiguration(_configuration); _encoder = new Encoder(); if (!_encoder.CreateFilterInstance(_graphBuilder, _tuner, _tvAudio, _crossbar, _capture)) { Log.Log.Error("analog: unable to add encoding filter"); throw new TvException("Analog: unable to add capture filter"); } Log.Log.WriteFile("analog: Check quality control"); _qualityControl = QualityControlFactory.createQualityControl(_configuration, _encoder.VideoEncoderFilter, _capture.VideoFilter, _encoder.MultiplexerFilter, _encoder.VideoCompressorFilter); if (_qualityControl == null) { Log.Log.WriteFile("analog: No quality control support found"); //If a hauppauge analog card, set bitrate to default //As the graph is stopped, we don't need to pass in the deviceID //However, if we wish to change quality for a live graph, the deviceID must be passed in if (_tunerDevice != null && _capture.VideoFilter != null) { if (_capture.VideoCaptureName.Contains("Hauppauge")) { Hauppauge _hauppauge = new Hauppauge(_capture.VideoFilter, string.Empty); _hauppauge.SetStream(103); _hauppauge.SetAudioBitRate(384); _hauppauge.SetVideoBitRate(6000, 8000, true); int min, max; bool vbr; _hauppauge.GetVideoBitRate(out min, out max, out vbr); Log.Log.Write("Hauppauge set video parameters - Max kbps: {0}, Min kbps: {1}, VBR {2}", max, min, vbr); _hauppauge.Dispose(); _hauppauge = null; } } } if (!AddTsFileSink()) { throw new TvException("Analog: unable to add mpfilewriter"); } Log.Log.WriteFile("analog: Graph is built"); FilterGraphTools.SaveGraphFile(_graphBuilder, "analog.grf"); ReloadCardConfiguration(); _graphState = GraphState.Created; } catch (TvExceptionSWEncoderMissing ex) { Log.Log.Write(ex); Dispose(); _graphState = GraphState.Idle; throw; } catch (Exception ex) { Log.Log.Write(ex); Dispose(); _graphState = GraphState.Idle; throw new TvExceptionGraphBuildingFailed("Graph building failed", ex); } }
/// <summary> /// Disposes this instance. /// </summary> public virtual void Dispose() { if (_graphBuilder == null) { return; } Log.Log.WriteFile("analog:Dispose()"); if (!CheckThreadId()) { return; } if (_graphState == GraphState.TimeShifting || _graphState == GraphState.Recording) { // Stop the graph first. To ensure that the timeshift files are no longer blocked StopGraph(); } FreeAllSubChannels(); IMediaControl mediaCtl = (_graphBuilder as IMediaControl); if (mediaCtl == null) { throw new TvException("Can not convert graphBuilder to IMediaControl"); } // Decompose the graph mediaCtl.Stop(); FilterGraphTools.RemoveAllFilters(_graphBuilder); Log.Log.WriteFile("analog:All filters removed"); if (_tuner != null) { _tuner.Dispose(); _tuner = null; _tunerDevice = null; } if (_crossbar != null) { _crossbar.Dispose(); _crossbar = null; } if (_tvAudio != null) { _tvAudio.Dispose(); _tvAudio = null; } if (_capture != null) { _capture.Dispose(); _capture = null; } if (_encoder != null) { _encoder.Dispose(); _encoder = null; } if (_teletext != null) { _teletext.Dispose(); _teletext = null; } if (_tsFileSink != null) { Release.ComObject("tsFileSink filter", _tsFileSink); _tsFileSink = null; } _rotEntry.Dispose(); _rotEntry = null; Release.ComObject("Graphbuilder", _graphBuilder); _graphBuilder = null; _graphState = GraphState.Idle; Log.Log.WriteFile("analog: dispose completed"); }
/// <summary> /// Builds the directshow graph for this analog tvcard /// </summary> public override void BuildGraph() { if (_cardId == 0) { GetPreloadBitAndCardId(); _configuration = Configuration.readConfiguration(_cardId, _name, _devicePath); Configuration.writeConfiguration(_configuration); } _lastSignalUpdate = DateTime.MinValue; _tunerLocked = false; Log.Log.WriteFile("analog: build graph"); try { if (_graphState != GraphState.Idle) { Log.Log.WriteFile("analog: Graph already build"); throw new TvException("Graph already build"); } //create a new filter graph _graphBuilder = (IFilterGraph2)new FilterGraph(); _rotEntry = new DsROTEntry(_graphBuilder); _capBuilder = (ICaptureGraphBuilder2)new CaptureGraphBuilder2(); _capBuilder.SetFiltergraph(_graphBuilder); Graph graph = _configuration.Graph; _tuner = new Tuner(_device); if (!_tuner.CreateFilterInstance(graph, _graphBuilder)) { Log.Log.Error("analog: unable to add tv tuner filter"); throw new TvException("Analog: unable to add tv tuner filter"); } _minChannel = _tuner.MinChannel; _maxChannel = _tuner.MaxChannel; //add the wdm crossbar device and connect tvtuner->crossbar _crossbar = new Crossbar(); if (!_crossbar.CreateFilterInstance(graph, _graphBuilder, _tuner)) { Log.Log.Error("analog: unable to add tv crossbar filter"); throw new TvException("Analog: unable to add tv crossbar filter"); } //add the tv audio tuner device and connect it to the crossbar _tvAudio = new TvAudio(); if (!_tvAudio.CreateFilterInstance(graph, _graphBuilder, _tuner, _crossbar)) { Log.Log.Error("analog: unable to add tv audio tuner filter"); throw new TvException("Analog: unable to add tv audio tuner filter"); } //add the tv capture device and connect it to the crossbar _capture = new Capture(); if (!_capture.CreateFilterInstance(graph, _capBuilder, _graphBuilder, _tuner, _crossbar, _tvAudio)) { Log.Log.Error("analog: unable to add capture filter"); throw new TvException("Analog: unable to add capture filter"); } Configuration.writeConfiguration(_configuration); _teletext = new TeletextComponent(); if (_capture.SupportsTeletext) { if (!_teletext.CreateFilterInstance(graph, _graphBuilder, _capture)) { Log.Log.Error("analog: unable to setup teletext filters"); throw new TvException("Analog: unable to setup teletext filters"); } } Configuration.writeConfiguration(_configuration); _encoder = new Encoder(); if (!_encoder.CreateFilterInstance(_graphBuilder, _tuner, _tvAudio, _crossbar, _capture)) { Log.Log.Error("analog: unable to add encoding filter"); throw new TvException("Analog: unable to add capture filter"); } Log.Log.WriteFile("analog: Check quality control"); _qualityControl = QualityControlFactory.createQualityControl(_configuration, _encoder.VideoEncoderFilter, _capture.VideoFilter, _encoder.MultiplexerFilter, _encoder.VideoCompressorFilter); if (_qualityControl == null) { Log.Log.WriteFile("analog: No quality control support found"); //If a hauppauge analog card, set bitrate to default //As the graph is stopped, we don't need to pass in the deviceID //However, if we wish to change quality for a live graph, the deviceID must be passed in if (_tunerDevice != null && _capture.VideoFilter != null) { if (_capture.VideoCaptureName.Contains("Hauppauge")) { Hauppauge _hauppauge = new Hauppauge(_capture.VideoFilter, string.Empty); _hauppauge.SetStream(103); _hauppauge.SetAudioBitRate(384); _hauppauge.SetVideoBitRate(6000, 8000, true); int min, max; bool vbr; _hauppauge.GetVideoBitRate(out min, out max, out vbr); Log.Log.Write("Hauppauge set video parameters - Max kbps: {0}, Min kbps: {1}, VBR {2}", max, min, vbr); _hauppauge.Dispose(); _hauppauge = null; } } } if (!AddTsFileSink()) { throw new TvException("Analog: unable to add mpfilewriter"); } Log.Log.WriteFile("analog: Graph is built"); FilterGraphTools.SaveGraphFile(_graphBuilder, "analog.grf"); ReloadCardConfiguration(); _graphState = GraphState.Created; } catch (TvExceptionSWEncoderMissing ex) { Log.Log.Write(ex); Dispose(); _graphState = GraphState.Idle; throw; } catch (Exception ex) { Log.Log.Write(ex); Dispose(); _graphState = GraphState.Idle; throw new TvExceptionGraphBuildingFailed("Graph building failed", ex); } }
/// <summary> /// Disposes this instance. /// </summary> public virtual void Dispose() { if (_graphBuilder == null) return; Log.Log.WriteFile("analog:Dispose()"); if (!CheckThreadId()) return; if (_graphState == GraphState.TimeShifting || _graphState == GraphState.Recording) { // Stop the graph first. To ensure that the timeshift files are no longer blocked StopGraph(); } FreeAllSubChannels(); IMediaControl mediaCtl = (_graphBuilder as IMediaControl); if (mediaCtl == null) { throw new TvException("Can not convert graphBuilder to IMediaControl"); } // Decompose the graph mediaCtl.Stop(); FilterGraphTools.RemoveAllFilters(_graphBuilder); Log.Log.WriteFile("analog:All filters removed"); if (_tuner != null) { _tuner.Dispose(); _tuner = null; _tunerDevice = null; } if (_crossbar != null) { _crossbar.Dispose(); _crossbar = null; } if (_tvAudio != null) { _tvAudio.Dispose(); _tvAudio = null; } if (_capture != null) { _capture.Dispose(); _capture = null; } if (_encoder != null) { _encoder.Dispose(); _encoder = null; } if (_teletext != null) { _teletext.Dispose(); _teletext = null; } if (_tsFileSink != null) { Release.ComObject("tsFileSink filter", _tsFileSink); _tsFileSink = null; } _rotEntry.Dispose(); _rotEntry = null; Release.ComObject("Graphbuilder", _graphBuilder); _graphBuilder = null; _graphState = GraphState.Idle; Log.Log.WriteFile("analog: dispose completed"); }