Книга представляется в виде расширенного описания оглавления и фрагментов текста , что поможет Вам понять ee содержание. Все права на книгу принадлежат автору. Вы не имеете права распространять книгу в целом или по частям без разрешения автора.

Условия закачки книги в формате PDF находятся здесь http://www.plati.ru/asp/pay.asp?id_d=681750. Условия закачки исходных текстов здесь http://www.plati.ru/asp/pay.asp?id_d=681755.

Глава 3(фрагмент) Построение графа DirectShow до фильтра сбора

 

Принципы построения графа сбора
Определение конфигурации устройства сбора
Поиск фильтров с помощью метода FindInterface [переход...]

Принципы построения графа сбора

Построение графа сбора производится вокруг предварительно установленного в граф фильтра сбора, который является центральным фильтром графа. Сначала построение графа ведется от фильтра сбора вверх по потоку, при этом выявляются и устанавливаются все фильтры, которые входят в состав устройства. Затем производится определение возможностей фильтра сбора записывать и отображать данные. Для этого производится обнаружение выходных контактов фильтра сбора и определение их функциональных возможностей. Фильтры сбора могут иметь различный набор выходных контактов. В минимальном варианте фильтр сбора может иметь только один выходной контакт записи. После обнаружения выходных контактов фильтра сбора начинается построение графа вниз по потоку. Каждый выходной контакт фильтра сбора является началом ветви графа определенного назначения. На завершающей стадии построения графа сбора, может быть построено несколько ветвей графа в зависимости от решаемой задачи и наличия  выходных контактов. Например, строится одна ветвь графа для предварительного просмотра, вторая ветвь для записи сжатых или не сжатых файлов и может быть третья ветвь графа для просмотра телетекста и дополнительной информации.

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

Интерфейсы

Описание

IAMAnalogVideoDecoder

Управление аналого-цифровым преобразованием в фильтре сбора WDM.

IAMBufferNegotiation

Управление распределением буфера на контакте.

IAMCopyCaptureFileProgress

Интерфейс обратного вызова (callback) для приема сообщений при  операции копирования файлов.

IAMCrossbar

Интерфейс, управляющий переключением источников видео сигнала.

IAMDroppedFrames

Определение производительности фильтра сбора в процессе работы (run-time).

IAMStreamControl

Управление временем пуска и останова на индивидуальном потоке данных.

IAMStreamConfig

Управление  выходным форматом видео изображения и звука.

IAMTVTuner

Управление настройкой приемника.

IAMVfwCaptureDialogs

Отображение диалога для фильтра сбора VFW. Может предоставляться и другими  фильтрами для управления их свойствами.

IAMVideoControl

Управление конечным изображением, переключает изображение зеркально, вертикально, горизонтально. Управляет внешним переключателем потока данных. Устройство должно поддерживать, указанные возможности.

IAMVideoProcAmp

Регулировки видеосигнала, такие как яркость, контрастность и другие.

ICaptureGraphBuilder2

Построение и настройка фильтрового графа.

IFileSinkFilter2

Установка  имени и атрибутов записываемого  файла.

Таблица 1 Интерфейсы сбора видео данных

 

Определение конфигурации устройства сбора

На начальном этапе построения графа сбора следует определить конфигурацию устройства, то есть определить какие фильтры входят в его состав  и отыскать их интерфейсы. Как уже упоминалось ранее, типичное устройство аналогового телевидения содержит фильтр сбора, фильтр настройки ТВ приемника, фильтр переключателя видео входов. Многофункциональный телеприемник может дополнительно включать фильтр приемника звуковых программ .

         Отправной точкой поиска является фильтр сбора, который должен быть к этому моменту уже установленным в граф. Поиск может производиться двумя способами:

 

Поиск фильтров с помощью  метода FindInterface

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

 

HRESULT FindInterface(
const GUID    *pCategory,
const GUID    *pType,
IBaseFilter   *pf,
REFIID        riid,
void          **ppint
);
 
Параметры.
pCategory
(in) Указатель на GUID критерия поиска. Может иметь четыре значения.

&LOOK_UPSTREAM_ONLY   - поиск вверх по потоку.
&LOOK_DOWNSTREAM_ONLY - поиск вниз по потоку.
Член структуры AMPROPERTY_PIN_CATEGORY -указывает категорию (GUID) контакта для поиска. Например, категория контакта &PIN_CATEGORY_CAPTURE. Категории контактов указаны в разделе справки.
NULL.  В этом случае поиск производится в таком порядке:
·        На фильтре.
·        На контактах.
·        Вниз по потоку.
·        Вверх по потоку.

pType
(in) Указатель на GUID главного медиа типа выходного контакта или NULL. Список главных типов приводится в разделе справки.
 
pf
(in) Указатель на фильтр, с которого начинается поиск.
 
riid
(in) Идентификатор интерфейса (IID), который требуется найти.
 
ppint
(out) Адрес переменной для приема указателя на найденный интерфейс. После использования интерфейса его следует освободить.
 
Коды возврата
Возвращается величина типа HRESULT. Возможные значения показаны ниже.
 

Возвращаемая величина

Описание

S_OK

Завершение без ошибок.

E_FAIL

Ошибка.

E_NOINTERFACE

Интерфейс не поддерживается.

E_POINTER

Указатель на интерфейс NULL.

 

 

 

 

 

 

 

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

В процессе поиска происходит опрос объектов в графе в следующем порядке:

  1. Фильтр.

  2. Контакты фильтра.

Поиск можно ограничить установкой параметров pCategory и pType.

Если параметр pCategory имеет значение LOOK_DOWNSTREAM_ONLY или LOOK_UPSTREAM_ONLY, то поиск происходит вверх или вниз по потоку. Параметр pType игнорируется.

Если параметр pCategory указывает категорию контакта, то тогда учитывается параметр pType и он также должен быть указан.  В этом случае метод ищет интерфейс на контактах фильтров, что находятся выше по потоку, начиная с указанного фильтра. Поиск ниже по потоку ограничен выходными контактами фильтра. Категория контакта указывается при поиске интерфейса на контактах фильтра сбора.  Следует учесть, что фильтр сбора может иметь раздельные контакты для сбора и предварительного просмотра. Некоторые фильтры имеют контакт видео порта (PIN_CATEGORY_VIDEOPORT) вместо контакта предварительного просмотра. В этом случае, если производить поиск только для  категории  контакта предварительного просмотра (PIN_CATEGORY_PREVIEW) и медиа типа MEDIATYPE_Video, метод определит видео порт как контакт предварительного просмотра и приложение не сможет различить видео порт.

Если устройство сбора использует WDM драйвер, то при поиске интерфейсов для фильтра настройки (TV Tuner) и (или) фильтра переключателя видео входов (Analog Video Crossbar) они автоматически устанавливаются в граф. Метод также производит соединение установленных фильтров.

         Для дальнейшего построения графа вокруг уже установленного фильтра сбора создадим метод CCapture::InitCaptureVideo(HWND hwndView), где параметр hwndView является дескриптором окна отображения приложения, в котором будет отображаться видео. Он понадобится нам в дальнейшем для инициализации фильтра отображения. В методе напишем код для поиска интерфейсов IAMTVTuner для фильтра настройки телеприемника и  IAMCrossbar для фильтра переключателя видео входов. На первом этапе закомментируем часть кода, которая создает объекты классов переключателя и настройки приемника. Сейчас важно определить, как метод будет производить поиск и установку фильтров в граф. Если метод найдет интерфейс фильтра настройки IAMTVTuner , то он должен будет найти и установить в граф  фильтр переключателя видео входов, поскольку переключатель входов всегда находится ниже по потоку. Возможен вариант существования специального устройства сбора без фильтра настройки или без переключателя. Для нестандартного варианта устройства, метод все равно должен найти все фильтры выше по потоку и установить их. Так предполагается из описания метода, но как в действительности он работает в этом случае, можно определить только экспериментально, документация не посвящает нас в такие тонкости. Поэтому для страховки, следует поискать интерфейс переключателя IAMCrossbar, после неудачного поиска интерфейса фильтра настройки. Можно поступить и другим способом. После окончания поиска фильтра настройки перечислить фильтры в графе и найти фильтр переключателя. Это несколько ускорит поиск нужных фильтров.

        

HRESULT CCapture::InitCaptureVideo(HWND hwndView)
{
     //Нет фильтра сбора в графе
     if(!m_pCapV)
        return E_FAIL;
     HRESULT hr=S_OK;
     string sErr;
     //Указатель на первый переключатель видео входов
     CComPtr<IBaseFilter> pCross1;
     //Указатель на фильтр настройки приемника видео
     CComPtr<IBaseFilter> pTunerV;
     //Указатель на фильтр звуковых программ приемника
     CComPtr<IBaseFilter> pTunerA;

 
     try
     {
         //Построения графа сбора  через поиск интерфейса IAMTVTuner
         CComPtr<IAMTVTuner> pTuner;
         hr = m_pBuilder->FindInterface(&LOOK_UPSTREAM_ONLY,&MEDIATYPE_Video,m_pCapV,IID_IAMTVTuner                                                ,reinterpret_cast<void **>(&pTuner));

    //Если интерфейс найден, реализуем класс для фильтра настройки приемника              
         if(hr == S_OK && pTuner)
         {
    //Создаем объект класса CTuner
              //m_pAMTunerV = CTuner::MakeTuner(pAMTVTuner);
 
    //Перечисление фильтров в графе для поиска фильтра переключателя
                   CComPtr<IEnumFilters> pEnum;
                   hr = m_pGraph2->EnumFilters(&pEnum);
                   if (S_OK == hr)
                   {
                        //запрашиваем интерфейс на каждом фильтре
                        CComPtr<IBaseFilter> pFilter;
                        while (S_OK == pEnum->Next(1, &pFilter, NULL))
                        {
                            CComQIPtr<IAMCrossbar> pCross(pFilter);
                            if(pCross)
                            {
                                 //Фильтр переключателя был установлен
                                 //создать объект переключателя
                                 //   m_pCrossBar1 =
                                 //CCross::MakeCrossbar(pCross);
                                 break;
                            }
                            pFilter = NULL;
                        }
                        pEnum = NULL;
                   }
                   if(!m_pCrossBar1)
                        return FALSE;     
}
else
{
     //поиск фильтра переключателя, если не найден фильтр  настройки
     CComPtr<IAMTVTuner> pCross;
     //hr = m_pBuilder->FindInterface(
                         //&LOOK_UPSTREAM_ONLY
                         //,&MEDIATYPE_Video,m_pCapV,IID_IAMCrossbar
                         //,reinterpret_cast<void **>(&pCross));
 
         //если интерфейс найден, реализуем класс для фильтра  настройки                
         if(hr == S_OK && pCross)
         {
              //m_pCrossBar1 = CCross::MakeCrossbar(pCross);
         }
}
}
     catch(HRESULT)
     {
         if(hr != S_OK)
         {
              DestroyGraph();
         }
     }
     return hr;
}

 

Проверим работу метода в режиме поиска только фильтра настройки приемника. При удачном завершении поиска построитель графа должен автоматически установить фильтр настройки приемника и соединить его контакты с другими фильтрами. Для вызова метода в обработчике CTVshowView::OnNMClickListVideo добавим следующие строки.

 
if(S_OK != pCap->InitCaptureVideo(hwnd))
{
     string sErr = "Ошибка инициализации фильтра сбора";
     CTVshowApp::myMessageError(E_FAIL,sErr);
     return;
}

 
Полностью код обработчика можно посмотреть в папке проекта, версия ProjectState3.

 Запускаем программу в режиме отладки и с помощью утилиты GraphEdit проверим результат. В окне утилиты видим картинку, показанную на рисунке 3.1.

 

 

Рис.3.1 Результат поиска интерфейса фильтра настройки приемника

Как видно на рисунке метод установил все фильтры и соединил их, в том числе и фильтр приемника звуковых программ , но не полностью.  Нет подключения контакта Analog Video фильтра TvTuner к входному контакту переключателя Video Tuner In. Это ошибка, суть которой попытаемся выяснить с помощью утилиты  GraphEdit . Как упоминалось ранее, для фильтров WDM потоки данных передаются в режиме ядра, а соединение фильтров отображает только управление фильтром со стороны DirectShow.  Проверить работу графа, когда он подключен к приложению невозможно, но его можно сохранить на диске, закрыть GraphEdit, затем опять открыть в GraphEdit и произвести необходимые проверки. Проделав необходимые манипуляции, достроим граф автоматически с контакта фильтра сбора Preview. Для этого следует сделать правый щелчок на контакте Preview и в открывшемся меню выбрать пункт "Render Pin". В граф будет установлен отображающий фильтр и появляется возможность проверить его функционирование в режиме предварительного просмотра. Для управления фильтрами в графе следует на фильтре сделать правый щелчок открывшемся меню выбрать пункт "Filter Properties". Откроется диалог со свойствами фильтра, позволяющий произвести его настройку. Диалог свойств фильтра настройки и фильтра сбора показаны на рисунке ниже. Первый позволяет произвести настройку на каналы, а второй выбрать стандарт телевидения. После настройки фильтров можно запустить граф и увидеть телевизионную картинку. Для ответа, почему  не происходит соединения, и в тоже время граф работает, немного поэкспериментируем с графом.

Учтите, что описанные результаты могут отличаться от результатов ваших экспериментов. На результат эксперимента влияет много факторов, от модели устройства до версии его драйвера и того насколько разработчики устройства следовали условиям DirectShow.

Проверим, действительно ли контакт остался неподключенным или GraphEdit нам не показал его подключение. Маловероятно, что утилита GraphEdit  виновата в этом, но проверка позволит исключить ее влияние. Сначала сохраним граф  в файле и закроем GraphEdit. Напишем вспомогательный метод, определяющий неподключенные контакты на фильтре и возвращающий их имена. Он поможет в процессе экспериментов вывести сообщение с именами неподключенных контактов на фильтре.

 

HRESULT CCapture::GetUnconnectedPin(IBaseFilter *pFilter ,PIN_DIRECTION pinDir )
{
     CComPtr<IEnumPins> pEnum ;
     CComPtr<IPin> pPinl,pTmp ;
     PIN_INFO info ={0};
     vector<string> namePin;
     //Создать перечислитель контактов
     HRESULT hr = pFilter->EnumPins(&pEnum);
     if (FAILED(hr))
            return hr;
 
     while (pEnum->Next(1, &pPinl, NULL) == S_OK)
     {
         PIN_DIRECTION pinDirl;
         //Проверяем направление контакта
         pPinl->QueryDirection(&pinDirl);
         if (pinDirl == pinDir)
         {
              if(pPinl->ConnectedTo(&pTmp) == VFW_E_NOT_CONNECTED)
              {
                   //Контакт не подключен,получить информацию о  контакте
                   pPinl->QueryPinInfo(&info);
                   //Преобразование в простую строку
                   string stmp = COLE2T(info.achName);
                   //Запись строк в вектор
                   namePin.push_back(stmp);
              }
              pTmp = NULL;
         }
         pPinl = NULL;
     }
 
     //Вывод
     string str;
     size_t count = namePin.size();
     for(size_t i=0; i< count; ++i)
     {
         str += namePin[i] + "\n\r";
     }
     str.insert(0,"Неподключенные контакты\n\r");
     AfxMessageBox(str.c_str());
     return hr;
}

 

[в начало]

Hosted by uCoz