Яндекс.Метрика

    Песочница

    Приложения для приема цифрового телевещания средствами DirectShow

    Приложение написано под ОС Windows7, DirectX 9, модель тюнера – AverTV Duo Hybrid PCI-E2, язык C#

    Microsoft TV Technologies Internals. В этой статье описан граф для приема цифрового телевещания, а также фильтры, которые используются при постороении графа.

    Для создания приложения необходимо:
    1. Построить граф.
    В своем графе я использую следующие фильтры:
    • Microsoft DVB-T Network Provider
    • AVerMedia 716x BDA DVBT Tuner (в зависимости от вашей модели Тюнера)
    • AVerMedia 716x BDA Digital Capture (в зависимости от вашей модели Тюнера)
    • MPEG2 Demultiplexer
    • BDA MPEG2 Transport Information Filter
    • MPEG-2 Sections and Tables
    • Microsoft DTV-DVD Video Decoder
    • Video Mixing Renderer 9
    2. Настроить Network Provider:
    В настройку провайдера входит создание экземпляра класса DVBTuningSpace, настройка этого объекта в соответствии со стандартом DVB-T. Через экземпляр класса DVBTuningSpace создается объект TuneRequest, который необходимо передать провайдеру.

    3. Настроить локатор на нужную частоту.
    Под локатором понимается экземпляр класса DVBTLocator, который используется для задания
    несущей частоты. (Некоторые тюнеры способны самостоятельно сканировать частоты и при задании неверной несущей частоты граф будет принимать и отображать каналы).
    Теперь разберемся с каждым фильтром в отдельности:
    1. BDA Network Provider – это фильтр-источник для графов, работающих с цифровым телевидением. Доступно несколько фильтров этой категории:
    o Для стандарта DVB-S (Спутниковое ТВ): Microsoft DVB-S Network Provider
    o Для стандарта DVB-T (Наземное ТВ): Microsoft DVB-T Network Provider
    o Для стандарта DVB-C (Кабельное ТВ): Microsoft DVB-C Network Provider
    o Для стандарта ATSC (Американский стандарт цифрового ТВ): Microsoft ATSC Network Provider
    В нашем случае используем фильтр Microsoft DVB-T Network Provider

    Для получения каналов, соответствующих несущей частоте используется фильтр MPEG-2 Sections and Tables. Через него можно получить таблицу PAT, которая содержит в себе SID-ы доступных каналов.
    Для отображения определенного канала используется интерфейс IDVBTuneRequest.С помощью метода put_SID вы можете передать SID нужного Вам канала.
    Примечание: После обновления провайдера, например, задания новой частоты, необходимо запускать граф, если он еще не запущен. Если граф будет остановлен, то при попытке получения данных из фильтров, например, получения таблицы PAT из фильтра MPEG-2 Sections and Tables, Вы получите несоответствующий новым настройкам провайдера результат.

    Граф DVB-T в GraphEditPlus:
    image

    Добавляем фильтры в Граф:

     #region add filters methods
            int IGraphDVBT.AddNetworkProvider()
            {
                pNetworkProvider = (IBaseFilter)Activator.CreateInstance(Type.GetTypeFromCLSID(typeof(DVBTNetworkProvider).GUID));
                return pGraph.AddFilter(pNetworkProvider, "Network Provider");
            }
    
            int IGraphDVBT.AddTuner()
            {
                pDVBTTuner = DirectShowTools.CreateFilter(FilterCategory.BDASourceFiltersCategory, "AVerMedia 716x BDA DVBT Tuner");
                return pGraph.AddFilter(pDVBTTuner, "DVBT Tuner");
            }
    
            int IGraphDVBT.AddCapture()
            {
                pDigitalCapture = DirectShowTools.CreateFilter(FilterCategory.BDAReceiverComponentsCategory, "AVerMedia 716x BDA Digital Capture");
                return pGraph.AddFilter(pDigitalCapture, "Digital Capture");
            }
    
            int IGraphDVBT.AddMPEG2Demultiplexer()
            {
                pMPEGDemux = (IBaseFilter)new MPEG2Demultiplexer();
                return pGraph.AddFilter(pMPEGDemux, "MPEG2 Demultiplexer");
            }
    
            int IGraphDVBT.AddBDAMPEG2TIF()
            {
                pBDAMPEGTIF = DirectShowTools.CreateFilter(FilterCategory.LegacyAmFilterCategory, "BDA MPEG2 Transport Information Filter");
                return pGraph.AddFilter(pBDAMPEGTIF, "BDA MPEG2 TIF");
            }
    
            int IGraphDVBT.AddMPEG2SectAndTables()
            {
                pMPEGSectAndTables = DirectShowTools.CreateFilter(FilterCategory.LegacyAmFilterCategory, "MPEG-2 Sections and Tables");
                return pGraph.AddFilter(pMPEGSectAndTables, "MPEG2 Section and Tables");
            }
    
            int IGraphDVBT.AddVideoDecoder()
            {
                pVideoDecoder = DirectShowTools.CreateFilter(FilterCategory.LegacyAmFilterCategory, "Microsoft DTV-DVD Video Decoder");
                return pGraph.AddFilter(pVideoDecoder, "DTV-DVD Video Decode");
            }
    
            int IGraphDVBT.AddVideoRenderer()
            {
                pVideoRenderer = (IBaseFilter)new VideoMixingRenderer9();
                return pGraph.AddFilter(pVideoRenderer, "Video Renderer");
            }
    
            int IGraphDVBT.AddAudioDecoder()
            {
                pAudioDecoder = DirectShowTools.CreateFilter(FilterCategory.LegacyAmFilterCategory, "Microsoft DTV-DVD Audio Decoder");
                return pGraph.AddFilter(pAudioDecoder, "DTV-DVD Audio Decode");
            }
    
            int IGraphDVBT.AddAudioRenderer()
            {
                pAudioRenderer = DirectShowTools.CreateFilter(FilterCategory.AudioRendererCategory, "Default DirectSound Device");
                return pGraph.AddFilter(pAudioRenderer, "Audio Renderer");
            }
            #endregion




    Пины фильтров нужно соединить между собой (Обратите внимание на то, что имена пинов в Вашем случае могут быть совершенно другими) :

    int IGraphDVBT.ConnectFilters()
            {
                int hr;
                IPin outPin;
                IPin inPin;
    
                outPin = DirectShowTools.FindPin(pNetworkProvider, new string[] { "Antenna Out" });
                inPin = DirectShowTools.FindPin(pDVBTTuner, new string[] { "0" });
                hr = pGraph.Connect(outPin, inPin); if (hr < 0) return hr;
    
                outPin = DirectShowTools.FindPin(pDVBTTuner, new string[] { "1" });
                inPin = DirectShowTools.FindPin(pDigitalCapture, new string[] { "0" });
                hr = pGraph.Connect(outPin, inPin); if (hr < 0) return hr;
    
                outPin = DirectShowTools.FindPin(pDigitalCapture, new string[] { "1" });
                inPin = DirectShowTools.FindPin(pMPEGDemux, new string[] { "MPEG-2 Stream" });
                hr = pGraph.Connect(outPin, inPin); if (hr < 0) return hr;
    
                outPin = DirectShowTools.FindPin(pMPEGDemux, new string[] { "001" });
                inPin = DirectShowTools.FindPin(pBDAMPEGTIF, new string[] { "IB Input" });
                hr = pGraph.Connect(outPin, inPin); if (hr < 0) return hr;
    
                outPin = DirectShowTools.FindPin(pMPEGDemux, new string[] { "002" });
                inPin = DirectShowTools.FindPin(pMPEGSectAndTables, new string[] { "In" });
                hr = pGraph.Connect(outPin, inPin); if (hr < 0) return hr;
    
                outPin = DirectShowTools.FindPin(pMPEGDemux, new string[] { "003" });
                inPin = DirectShowTools.FindPin(pVideoDecoder, new string[] { "Video Input" });
                hr = pGraph.Connect(outPin, inPin); if (hr < 0) return hr;
    
                outPin = DirectShowTools.FindPin(pMPEGDemux, new string[] { "007" });
                inPin = DirectShowTools.FindPin(pAudioDecoder, new string[] { "In" });
                hr = pGraph.Connect(outPin, inPin); if (hr < 0) return hr;
    
                outPin = DirectShowTools.FindPin(pVideoDecoder, new string[] { "Video Output 1" });
                inPin = DirectShowTools.FindPin(pVideoRenderer, new string[] { "VMR Input0" });
                hr = pGraph.Connect(outPin, inPin); if (hr < 0) return hr;
    
                outPin = DirectShowTools.FindPin(pAudioDecoder, new string[] { "Out" });
                inPin = DirectShowTools.FindPin(pAudioRenderer, new string[] { "Audio Input pin (rendered)" });
                hr = pGraph.Connect(outPin, inPin); if (hr < 0) return hr;
    
                return 0;
    
            }





    Теперь настраиваем DVB-T Network Provider. Для этого необходимо задать соответствующие DVB-T типы для экземпляра класса DVBTuningSpace. Методом put_TuneRequest мы активируем настроенный объект в провайдере.

    int IGraphDVBT.SetNetworkProvider()
            {
                int hr;
    
                IDVBTuningSpace tuningSpace = (IDVBTuningSpace)new DVBTuningSpace();
    
                hr = tuningSpace.put_UniqueName("DVBT TuningSpace");                   
                               if (hr < 0) return hr;
                hr = tuningSpace.put_FriendlyName("DVBT TuningSpace");                  
                               if (hr < 0) return hr;
                hr = tuningSpace.put_NetworkType("{" +
                    typeof(DVBTNetworkProvider).GUID.ToString() + "}");                    
                               if (hr < 0) return hr;
                hr = tuningSpace.put_SystemType(DVBSystemType.Terrestrial);          
                               if (hr < 0) return hr;
    
                ITuneRequest tr;
    
                hr = tuningSpace.CreateTuneRequest(out tr); if (hr < 0) return hr;
    
                IDVBTuneRequest tuneRequest = (IDVBTuneRequest)tr;
    
                hr = (pNetworkProvider as ITuner).put_TuneRequest(tuneRequest); if (hr < 0) return hr;
    
                return 0;
            }





    Для того, чтобы соединенный граф заставить работать его необходимо настроить на нужную частоту. Для этого существует интерфейс IDVBTLocator, с помощью которого мы и зададим несущую частоту. Все остальные параметры нам не понадобятся, поэтому задаем им соответствующие значения:

    private int SetDVBTLocator(int frequency, out IDVBTLocator locator)
            {
                int hr = 0;
                // -- LOCATOR -- 
                locator = (IDVBTLocator)new DVBTLocator();
                //set frequency
                hr = locator.put_CarrierFrequency(frequency);  /* kGz */   
                               if (hr < 0) return hr;
                //Not set parameters
                hr = locator.put_Bandwidth(-1);     /* in Mgz */              
                                if (hr < 0) return hr;
                hr = locator.put_SymbolRate(-1);                                  
                               if (hr < 0) return hr;
                hr = locator.put_OtherFrequencyInUse(false);                  
                               if (hr < 0) return hr;
                hr = locator.put_LPInnerFEC(FECMethod.MethodNotSet);   
                               if (hr < 0) return hr;
                hr = locator.put_LPInnerFECRate(BinaryConvolutionCodeRate.RateNotSet); 
                               if (hr < 0) return hr;
                hr = locator.put_HAlpha(HierarchyAlpha.HAlphaNotSet);      
                               if (hr < 0) return hr;
                hr = locator.put_Mode(TransmissionMode.ModeNotSet);      
                               if (hr < 0) return hr;
                hr = locator.put_InnerFEC(FECMethod.MethodNotSet);      
                                if (hr < 0) return hr;
                hr = locator.put_InnerFECRate(BinaryConvolutionCodeRate.RateNotSet);   
                               if (hr < 0) return hr;
                hr = locator.put_OuterFEC(FECMethod.MethodNotSet);      
                                if (hr < 0) return hr;
                hr = locator.put_OuterFECRate(BinaryConvolutionCodeRate.RateNotSet);   
                               if (hr < 0) return hr;
                hr = locator.put_Modulation(ModulationType.ModNotSet);  
                                if (hr < 0) return hr;
    
                return 0;
            }





    Теперь можно задать несущую частоту и посмотреть какие каналы на ней находятся. Для этого мы достаем tuneRequest из провайдера, настраиваем наш локатор с указанной частотой, обновляем наш провайдер новыми настройками, и, через интерфейс IMpeg2Data достаем из фильтра ‘MPEG-2 Sections and Tables’ таблицу PAT, в которой и будут содержаться каналы, соответствующий несущей частоте. Если частоту вы выбрали неправильно, то метод GetPAT вернет Вам отрицательный HRESULT.

    public string[] SetFrequency(int frequency)
            {
                if (((IGraphDVBT)ConcreteGraph).NetworkProvider == null ||
                    ((IGraphDVBT)ConcreteGraph).TablesAndSections == null) { return null; }
    
                int hr = 0;
    
                IPAT PAT;              //Table with SID
                IDVB_SDT SDT;
                IDVB_SIT SIT;
                int channelCount = 0;
                short pwVal = 0;
                ITuneRequest tuneRequest = null;
    
                //get tune request
                hr = (((IGraphDVBT)ConcreteGraph).NetworkProvider as ITuner).get_TuneRequest(out tuneRequest); 
                if (hr < 0 || tuneRequest == null) { return null; }
    
                IDVBTuneRequest DVBtuneRequest = tuneRequest as IDVBTuneRequest;
                IDVBTLocator locator;
    
                //set locator with necessary frequency
                hr = SetDVBTLocator(frequency, out locator);
                if (hr < 0 || locator == null) { return null; }
    
                hr = DVBtuneRequest.put_Locator(locator);
                if (hr < 0) { return null; }
    
                //mediaControl.Stop();
    
                hr = (((IGraphDVBT)ConcreteGraph).NetworkProvider as ITuner).put_TuneRequest(DVBtuneRequest);
                if (hr < 0) { return null; }
    
                //mediaControl.Run();
    
                //value is channel SID
                IDvbSiParser parser = (IDvbSiParser)new DvbSiParser();
                hr = parser.Initialize(((IGraphDVBT)ConcreteGraph).TablesAndSections as IMpeg2Data);
                if (hr < 0) { return null; }
     
                 //get table with channels
                hr = parser.GetPAT(out PAT);
                if (hr < 0)
                {
                    //DsError.ThrowExceptionForHR(hr);               
                    return null; 
                }
     
                //get count of channels
                hr = PAT.GetCountOfRecords(out channelCount);
                if (hr < 0) { return null; }
    
                string[] channelsDVBT = new string[channelCount];
                //put channels number in the list
                for (int i = 0; i < channelCount; i++)
                {
                     PAT.GetRecordProgramNumber(i,out pwVal);
                     channelsDVBT[i] = "Channel_" + pwVal.ToString();
                }
    
                return channelsDVBT;
            }





    Замечание: После того как Вы обновите провайдер, обязательно запустите граф (если он уже не запущен). Это делается с помощью интерфейса IMediaControl.
    Для настройки провайдера на определенный канал используется интерфейс IDVBTuneRequest.С помощью метода put_SID вы можете передать SID нужного Вам канала.

            /// <summary>
            /// Take tune request from Network Provider
            /// Set New SID and put tune request on the Network Provider
            /// </summary>
            /// <param name="SID">channel SID</param>
            public void SetDVBTChannels(int SID)
            {
                int hr = 0;
    
                ITuneRequest tuneRequest = null;
    
                //get tune request
                hr = (((IGraphDVBT)ConcreteGraph).NetworkProvider as ITuner).get_TuneRequest(out tuneRequest);
                if (tuneRequest == null) { return; }
    
                IDVBTuneRequest DVBtuneRequest = tuneRequest as IDVBTuneRequest;
                //set new channel
                hr = DVBtuneRequest.put_SID(SID);
    
                //put tune request
                hr = (((IGraphDVBT)ConcreteGraph).NetworkProvider as ITuner).put_TuneRequest(DVBtuneRequest);
    
            }




    image

    Полный код программы доступен по следующей ссылке: Код