Forcing Mpeg2Demultiplexer to use ffdshow to rende

2019-04-18 02:24发布

问题:

I spend a lot of time trying to make DTVViewer sample of DirectShow work unfortunately with no success. The video format of DVBT network is H264 and I found that the IntelliConnect behavior of IFilterGraph prefers to use Mpeg2 Video format.

For those who want to see the code, here it is. If you do not know anything about DirectShow I shared my experience with this code. And the most probably problem is described in step 5 and 6 of the tutorial.

  • The code for helper function which connects filters:

    public static void UnsafeConnectFilters(IFilterGraph2 graph, IBaseFilter source, IBaseFilter dest, Func<AMMediaType, bool> sourceMediaPredicate=null, Func<AMMediaType, bool> destMediaPredicate=null) {
        foreach(IPin spin in IteratePinsByDirection(source, PinDirection.Output)) {
            if(IsConnected(spin))
                continue;
            int fetched;
            AMMediaType[] sourceTypes=GetMajorType(spin, out fetched);
            if(fetched>0) {
                Guid sourceType=sourceTypes[0].majorType;
                try {
                    if(sourceMediaPredicate!=null&&!sourceMediaPredicate(sourceTypes[0]))
                        continue;
                    foreach(IPin pin in IteratePinsByDirection(dest, PinDirection.Input)) {
                        if(IsConnected(pin))
                            continue;
                        var types=GetMajorType(pin, out fetched);
                        try {
                            if(fetched>0) {
                                Guid destType=types[0].majorType;
                                if(destMediaPredicate!=null&&!destMediaPredicate(types[0]))
                                    continue;
                                if(sourceType==destType) {
                                    spin.Connect(pin, types[0]);
                                    return;
                                }
                            }
                            else {
                                spin.Connect(pin, sourceTypes[0]);
                                return;
                            }
                        }
                        finally {
                        }
                    }
                }
                finally {
                }
    
            }
        }
    }
    

Does anyone know about:

  1. How should I connect the h264 pin to ffdshow?
  2. How should I recommend the graph to use h264 video decoding?

  • Tutorial and details

    1. Create the graph

      _graph = (IFilterGraph2)new FilterGraph();
      
    2. We are using DVBT network

      IBaseFilter networkProvider = (IBaseFilter) new DVBTNetworkProvider();
      

      ... which must be tuned to 602000KHz@8MHz ONID=1 TSID=1 SID=6

      ITuner tuner = (ITuner) networkProvider;
      IDVBTuningSpace tuningspace = (IDVBTuningSpace) new DVBTuningSpace();
      tuningspace.put_UniqueName("DVBT TuningSpace");
      tuningspace.put_FriendlyName("DVBT TuningSpace");
      tuningspace.put__NetworkType(typeof (DVBTNetworkProvider).GUID);
      tuningspace.put_SystemType(DVBSystemType.Terrestrial);
      ITuneRequest request;
      tuningspace.CreateTuneRequest(out request);
      ILocator locator = (ILocator) new DVBTLocator();
      locator.put_CarrierFrequency(602000);
      ((IDVBTLocator) locator).put_Bandwidth(8);
      request.put_Locator(locator);
      IDVBTuneRequest dvbrequest = (IDVBTuneRequest) request;
      dvbrequest.put_TSID(1);
      dvbrequest.put_ONID(1);
      dvbrequest.put_SID(6);
      _graph.AddFilter(networkProvider, "Network Provider");
      
    3. Create a mpeg2 demux to get separate EPG/Vidoe/Audio/Text streams out of single TV stream

      _mpeg2Demultiplexer = (IBaseFilter) new MPEG2Demultiplexer();
      _graph.AddFilter(_mpeg2Demultiplexer, "MPEG-2 Demultiplexer");
      

      Now we search local filters for BDA Source Filter which in my case is IT9135 BDA Fitler

      DsDevice[] devicesOfCat = 
          DsDevice.GetDevicesOfCat(FilterCategory.BDASourceFiltersCategory);
      
      IBaseFilter iteDeviceFilter;
      
      _graph.AddSourceFilterForMoniker(
          devicesOfCat[0].Mon, null, devicesOfCat[0].Name, out iteDeviceFilter);
      
    4. Now connect filters: [DVBT Net. Provider]->[BDA Src Filter]->[MPEG2Demux]-> ...

      UnsafeConnectFilters(_graph, networkProvider, iteDeviceFilter);
      UnsafeConnectFilters(_graph, iteDeviceFilter, _mpeg2Demultiplexer);
      

      Two filters must be connected to demux, to provide epg (program guide data). sorry I do not know what they specifically are doig :P. They are located under BDATransportInformationRenderersCategory category. We try to find them by name and connect them to demux

      DsDevice[] dsDevices = 
          DsDevice.GetDevicesOfCat(FilterCategory.BDATransportInformationRenderersCategory);
      
      foreach (DsDevice dsDevice in dsDevices)
      {
          IBaseFilter filter;
      
          _graph.AddSourceFilterForMoniker(
              dsDevice.Mon, null, dsDevice.Name, out filter);
      
          if(dsDevice.Name == "BDA MPEG2 Transport Information Filter")
              _bdaTIF = filter;
          else if(dsDevice.Name == "MPEG-2 Sections and Tables")
          {
              _mpeg2SectionsAndTables = filter;
          }
          UnsafeConnectFilters(_graph, _mpeg2Demultiplexer, filter);
      }
      

      Now demux is connected to both MPEG-2 Sections and Tables and BDA MPEG2 Transport Information Filter.

    5. Now create h264 video type and add the output an output pin to demux for this type

      AMMediaType h264 = new AMMediaType();
      h264.formatType = FormatType.VideoInfo2;
      h264.subType = MediaSubType.H264;
      h264.majorType = MediaType.Video;
      IPin h264pin;
      ((IMpeg2Demultiplexer) _mpeg2Demultiplexer).CreateOutputPin(h264, "h264", out h264pin);
      

      Below, I tried to search for ffdshow Video Decoder which is capable of processing H264 video and is located under DirectShow Filters category(as in GraphStudio).

      DsDevice[] directshowfilters = 
          DsDevice.GetDevicesOfCat(FilterCategory.LegacyAmFilterCategory);
      
      IBaseFilter ffdshow = null;
      foreach (DsDevice directshowfilter in directshowfilters)
      {
          if(directshowfilter.Name == "ffdshow Video Decoder")
          {
              _graph.AddSourceFilterForMoniker(
                  directshowfilter.Mon, null, directshowfilter.Name, 
                  out ffdshow);
      
              break;
          }
      }
      
    6. Create a video renderer for video output ...

      _videoRenderer = new VideoRendererDefault();
      _graph.AddFilter((IBaseFilter)_videoRenderer, "Video Renderer");
      

      ... and audio ...

      DsDevice defaultDirectSound = 
          DsDevice.GetDevicesOfCat(FilterCategory.AudioRendererCategory)[0];
      
      _graph.AddSourceFilterForMoniker(
          defaultDirectSound.Mon, null, defaultDirectSound.Name, 
          out _audioRender);
      

      Here I tried to connect h264 output pin of demux to ffdshow. This method call fails with AccessViolationException. I'm not sure how to connect these two together :(.

      Commenting this line will result in a graph which starts running, although there is an disconnected ffdshowVideoDecoder filter in the graph, will not show anything. IntelliConnect connects Mpeg2 video output to a locally available video decoder and as I said it will not display anything.

      // UnsafeConnectFilters(_graph, _mpeg2Demultiplexer, ffdshow, type => type.majorType == MediaType.Video && type.subType == MediaSubType.H264);
      
    7. ConnectFilters is borrowed from DTVViewer sample of directshowlib

      ConnectFilters();
      

      I moved actual tuning here

      tuner.put_TuningSpace(tuningspace);
      tuner.put_TuneRequest(request);
      
    8. start the graph and wish for some sound or video to be displayed

      int hr = (_graph as IMediaControl).Run();
      DsError.ThrowExceptionForHR(hr);
      
    9. check that the graph is running ...

      FilterState pfs;
      hr = (_graph as IMediaControl).GetState(1000, out pfs);
      DsError.ThrowExceptionForHR(hr);
      

      and it says that the graph is running.

回答1:

Did you check that your ffdshow is enabled for H264/AVC? Open the filter properties and In "Codecs" section, H264/AVC format should be enabled (you can also disable the Mpeg2 decoder just to make sure it won't prefer this format).

Another thing, you can try using another Mpeg2 demultiplexer. The default "MPEG-2 Demultiplexer" is not behaving the same on different environments. There are many other filters that can demux TS and if you can invest some money, I'd recommend using MainConcept or Elecard.