/// <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");
    }