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

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

Глава 7(фрагмент). Запись файлов

Запись_файлов_в_DirectShow_
Простой_способ_записи_файлов_в_формате_AVI_и_ASF_ [переход...]
Расширенный_способ_записи_файлов_в_формате_AVI_и_ASF_ [переход...]
 

Запись файлов в DirectShow

DirectShow позволяет производить запись видеофайлов двух форматов, определяемых подтипами:

Для этих двух форматов файлов DirectShow предоставляет фильтры записи файлов. Основные данные фильтров записи файлов проведены в справочном разделе. Запись файлов AVI производится с участием двух фильтров, фильтра мультиплексора (AVI Mux) и собственно самого фильтра записи файлов (File Writer). Фильтр мультиплексора многовходовый и производит формирование потока данных в формате AVI. Фильтр записи файлов принимает поток данных от мультиплексора  и производит запись на диск, как простой поток байтов без дополнительного форматирования.

Фильтр записи файлов ASF содержит внутренний мультиплексор и принимает на входные контакты поток данных непосредственно от фильтров захвата (capture) видео и звука. Он не требует промежуточных фильтров. Фильтр записи файлов (WM ASF Writer) в формате ASF является фильтром оберткой для записывающего объекта платформы Windows Media Format SDK. Фильтр обрабатывает любое число входных потоков и создает файлы в формате Advanced Systems Format (ASF). Файл формата ASF является форматом файла контейнера для хранения как сжатых так и не сжатых звуковых и видео данных. Спецификация файлов формата ASF здесь не рассматривается и при необходимости с ней можно ознакомиться в папке Doc на компакт-диске. Файл ASF может содержать практически любые типы  данных. Фильтр записи ASF файлов может использоваться для различных видов обработки потоков данных, цифровое видео (DV), сжатие и восстановление звука, преобразование файлов AVI или MPEG для передачи по сетевым каналам связи. Данный фильтр является единственным способом создания файлов ASF в DirectShow.

Фильтр ASF использует Windows Media Format SDK для преобразования несжатых видео и звуковых данных в Windows Media контент, с применением кодеков Windows Media. Если контент содержит только звуковые данные, файл имеет расширение ".wma", если файл содержит видео данные или видео и звуковые данные он имеет расширение ".wmv".  Также файл может иметь обобщенное расширение ".asf".  Любой тип файла может включать и метаданные. Список интерфейсов фильтра и другие данные приведены в справочном разделе.

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

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

Для записи файлов также можно использовать фильтры сторонних производителей, которые обеспечивают запись файлов других форматов, например MPEG2. Запись файлов в формате MPEG2 не поддерживается DirectShow, Microsoft не предоставляет  фильтров для записи  файлов MPEG2. Для воспроизведения DirectShow предоставляет фильтры для файлов формата MPEG2.

 

Простой способ записи файлов в формате AVI и ASF

Для записи файлов необходимо построить ветку графа, начиная с контактов записи (capture) фильтра захвата изображения и фильтра захвата звука. Запись файлов AVI и ASF в простом варианте не представляет особой сложности. Рассмотрим на примере вариант записи файлов двух форматов. Допустим, что требуется производить запись отдельного файла одного из двух форматов. Начало записи файла будет происходить при нажатии кнопки  "Запись", остановка и освобождение файла при нажатии кнопки "Стоп". Анализ и выбор формата записываемого файла производится по расширению имени файла. Повторная процедура производит запись файла с новым именем.

Построение ветки графа для реализации записи файлов форматов AVI и ASF можно произвести полуавтоматически с использованием вспомогательного объекта построителя, который предоставляет интерфейс ICaptureGraphBuilder2. Некоторые  методы этого интерфейса рассматривались ранее, сейчас рассмотрим метод SetOutputFileName, который формирует участок графа для записи файла. Метод устанавливает в граф фильтры для записи либо файла типа AVI, либо файла ASF.  Для простейшего способа записи вполне достаточно только этого метода.

Второй  интерфейс, который может быть понадобиться при записи файлов это IFileSinkFilter, и производный от него IFileSinkFilter2. Эти интерфейсы предоставляются на фильтрах записи файлов. Интерфейс IFileSinkFilter2 полностью заменяет IFileSinkFilter и добавляет два метода, управляющие перезаписью файлов.

Если производится запись файла AVI, метод SetOutputFileName устанавливает два соединенных фильтра, фильтр мультиплексора AVI Mux и фильтр записи файла. При записи файлов ASF устанавливается только фильтр записи файла. После вызова этого метода остается только подключить фильтр записи к фильтрам захвата изображения и звука. Это можно сделать различными способами, как ручным соединением фильтров, так и автоматически с возможностью установки промежуточного фильтра компрессора. Фильтр компрессора может понадобиться для записи сжатых файлов AVI. При записи файлов ASF использовать компрессор не нужно, поскольку файл сжимается в соответствии с установленным по умолчанию профилем. Другое дело, что профиль по умолчанию может не удовлетворить пользователя и для полноценного использования возможностей  фильтра потребуется управление системными профилями, что невозможно при простейшем варианте записи.

Запись начинается сразу после пуска графа. Для остановки записи следует остановить граф и удалить фильтр (или фильтры) записи. Для записи нового файла вся процедура повторяется.

Все опыты по записи файлов производятся с размером изображения 352х288 пикселей для стандарта SECAM.  При установке другого видео формата (размера изображения) запись файла становиться невозможной. Подробней об установке формата на контакте Capture фильтра захвата будет рассказано далее.

Рассмотрим  метод ICaptureGraphBuilder2::SetOutputFileName и методы интерфейса IFileSinkFilter, используемые для реализации простого способа записи файлов.

HRESULT SetOutputFileName(
  const GUID      *pType,
  LPCOLESTR   lpwstrFile,
  IBaseFilter           **ppf,
  IFileSinkFilter **pSink
);
 
Параметры.
pType
[in]
Указатель на GUID, определяющий выходной медиа подтип или идентификатор (CLSID) фильтра мультиплексора или фильтра записи файла. Медиа подтип может иметь одно из следующих значений.   
 

Value

Описание

MEDIASUBTYPE_Avi

Файл формата AVI.

MEDIASUBTYPE_Asf

Файл формата ASF.

 

lpwstrFile
[in]
Указатель на строку типа WCHAR, содержащую имя файла.
 
ppf
[out]
Адрес указателя, принимающего интерфейс IBaseFilter мультиплексора.
 
pSink
[out]
Адрес указателя, принимающего интерфейс IFileSinkFilter фильтра записи файла. Может быть NULL.

Коды возврата.

Код

Описание

S_OK

Без ошибок.

E_FAIL

Ошибка.

E_POINTER

Нулевой указатель.

 

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

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

Интерфейс IFileSinkFilter имеет два метода SetFileName и GetCurFile. Метод SetFileName  создает файл, если он не существует, в противном случае файл переписывается без удаления.

HRESULT SetFileName(
LPCOLESTR pszFileName,
const AM_MEDIA_TYPE *pmt
);
 
Параметры.
pszFileName
[in]
Указатель на строку с именем файла.
 
pmt
[in]
Указатель на структуру, описывающую медиа формат выборки записываемой в файл и  медиа тип для входного контакта фильтра записи. В документации отсутствуют указания на какие-либо особенности его использования. Нулевое значение указателя не препятствует нормальной записи файла.
 
Возвращает величины HRESULT.
 
Метод GetCurFile возвращает имя файла и медиа тип текущего файла.
 
HRESULT GetCurFile(
LPOLESTR *ppszFileName,
AM_MEDIA_TYPE *pmt
);
 
Параметры.
ppszFileName
[out]
Адрес указателя на строку типа OLESTR, которая принимает имя файла. Строка должна быть освобождена, вызовом CoTaskMemFree после ее использования.

pmt
[out]
Указатель на структуру AM_MEDIA_TYPE, которая принимает медиа тип. Может быть равным NULL, если тип не нужен. Если медиа тип используется, то структура должна освобождаться после использования.
 

Коды возврата.

Код

Описание

S_OK

Без ошибок.

E_FAIL

Нет открытого файла. Если файл не открыт, метод может возвращаться без ошибок, но возвращает NULL в первом  параметре. Он должен быть проверен перед использованием имени файла и медиа типа.

E_OUTOFMEMORY

Недостаточно памяти.

E_POINTER

Нулевой указатель в параметре ppszFileName.

 

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

HRESULT SetMode(DWORD dwFlags);
HRESULT GetMode(DWORD *dwFlags);

Флаг имеет только одно значение AM_FILE_OVERWRITE, который указывает на то, что старый файл должен быть удален.

 

Расширенный способ записи файлов в формате AVI и ASF

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

Первое и самое простое решение установить в граф две ветви графа для записи файлов двух типов и производить их оперативное переключение для записи файлов определенного формата. Со стороны DirectShow, каких либо ограничений и предупреждений на установку и использование в графе двух разнотипных фильтров записи файла найдено не было.  Проверка этого решения обнаружила много препятствий, которые  исключают возможность такого применения фильтров записи. Первое и, пожалуй, самое главное препятствие состоит в том, что фильтр записи файла ASF после установки в граф блокирует управление графом до момента его полного подключения к фильтрам захвата.  Таким образом, не удается организовать режим предварительного просмотра  с установленным и неподключенным фильтром ASF.  Блокировка управления графом не полная, в некоторых случаях удается установить фильтр в граф, сохранив предварительный просмотр. Например, фильтр ASF устанавливается в запущенный граф, но дальнейшее управление графом происходит с нарушениями, которые вызывают сомнения в управляемости графом. При такой установке отмечается невозможность определения текущего состояния графа. Метод IMediaControl::GetState возвращается с ошибкой. Фрагмент кода показан ниже.

 OAFilterState state; //состояние фильтрового графа    
   CComQIPtr<IMediaControl> pCtrl(m_pGraph2);
     ASSERT(pCtrl);

     HRESULT hr;
     if(FAILED(pCtrl->GetState(30,&state)))
     {
         return E_FAIL;
     }

 

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

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

 

Метод

Описание

Reconnect

Выполняет динамическую реконфигурацию двух контактов, выходного и входного. Входной контакт фильтра должен поддерживать интерфейс динамической реконфигурации IPinConnection.

Reconfigure

Закрывает граф и вызывает функцию обратного вызова (callback) в приложении или фильтре для выполнения  динамической реконфигурации.

AddFilterToCache

Переводит фильтр из графа к кэш.

RemoveFilterFromCache

Удаляет фильтр из кеша.

EnumCacheFilter

Перечисляет фильтры в кеше.

GetStartTime

Возвращает значение времени (reference time)  для последнего запуска графа.  

PushThroughData

Проталкивает данные через граф на указанный контакт.

SetFilterFlags

Устанавливает на фильтре флаг конфигурации. Флаг имеет одно значение, которое говорит, что фильтр удаляется в процессе реконфигурации.

GetFilterFlags

Возвращает флаг конфигурации.

RemoveFilterEx

Удаляет фильтр из графа.

Таблица  1. Интерфейс IGraphConfig.

В списке методов интерфейса IGraphConfig отсутствует метод для  явного извлечения фильтра из кеша. В документации DirectShow указывается, что методы IGraphBuilder::Render, IGraphBuilder::RenderFile, и IGraphBuilder::Connect пытаются использовать фильтры из кеша до поиска фильтров в регистре. В действительности метод IGraphBuilder::Connect не работает с кешем. Если во втором параметре метода указан входной контакт  фильтра, находящегося в кеше, то метод возвращается с ошибкой.  В этом можно убедиться, проверив его на практике с помощью такого кода.

         CComPtr<IPin> pP, pPCapV, pPCnt;
     pPCapV = GetPinCapture(m_pCapV);

//Находим контакт фильтра, который находится в кеше
     hr = m_pBuilder->FindPin(m_pFSinkASF, PINDIR_INPUT,NULL,    NULL,TRUE,0,&pP);
                                                                      
     if(pP && pPCapV)
     {
         hr = m_pGraph2->Connect(pPCapV,pP);
         if(hr != S_OK)
              CTVshowApp::myMessageError(hr,"");

         hr = pP->ConnectedTo(&pPCnt);
         if(hr != S_OK)
              CTVshowApp::myMessageError(hr,"");
    }

 

Метод Connect  возвращает код VFW_E_NOT_IN_GRAPH, если фильтр с указанным контактом не находится в графе и не производит никаких действий по подключению фильтра. Неоднократные проверки в различных условиях не привели к положительному результату. 

Метод RenderFile предназначен для работы с файлами и в данной ситуации не применим. Только метод Render, действительно автоматически извлекает фильтр из кеша в граф и подключает его. Остается определить, каким образом метод различит два или более фильтров в кеше предназначенных для решения одинаковой задачи записи файлов. А он никак их не различает, кеш оказался стековым, о чем  документация умалчивает. Метод Render устанавливает в граф и подключает фильтр, находящийся на верхушке стека. Дополнительным препятствием при работе с кешем является то, что при помещении в кеш имя фильтра, назначенное при установке в граф, очищается и при возврате из кеша назначается имя, генерируемое автоматически. Возможности интерфейса IGraphConfig очень ограничены в части работы с кешем из-за неработающего с кешем метода Connect. Другого метода  для простого извлечения фильтра из кеша нет. В примерах DirectShow также нет кода, работающего с кешем.

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

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

//Добавляем в граф фильтр, который находится в кеше.
//Фильтр при этом не удаляется автоматически из кеша и
//таким образом, указатель m_pFSinkASF указывает на
//фильтр в кеше и на фильтр в графе одновременно, в чем
//можно удостовериться при перечислении фильтров в кеше.

     HRESULT hr = m_pGraph2->AddFilter(m_pFSinkASF,L"ASF");
    
     CComQIPtr<IGraphConfig> pCnf(m_pGraph2);

//Теперь удаляем фильтр из кеша. В графе фильтр
//остается.
     if(pCnf)
     {
         hr = pCnf->RemoveFilterFromCache(m_pFSinkASF);
     }


 

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

 

HRESULT CCapture::AddFilterFromCache(IBaseFilter* pF,wstring nameFilter)                                                                                        
{
     HRESULT hr = S_OK;
     CComPtr<IEnumFilters> pEnum;
    
     CComQIPtr<IGraphConfig> pCnf(m_pGraph2);
     if(pCnf)
     {
         hr = pCnf->EnumCacheFilter(&pEnum);
         if(FAILED(hr))
              return hr;
     }

     //найти фильтр в кеше
     CComPtr<IBaseFilter> pFCache;
     while (S_OK == pEnum->Next(1, &pFCache, NULL))
     {
         if(pFCache == pF)
         {
              //добавляем фильтр в граф
              hr = m_pGraph2->AddFilter(pF,nameFilter.c_str());            
              if(FAILED(hr))
                   return hr;

              //удаляем фильтр из кеша
              hr = pCnf->RemoveFilterFromCache(pF);
              if(FAILED(hr))
                   return hr;

              //фильтр найден
              break;
         }
         pFCache = NULL;
     }

     return hr;
}

 

Для проверки всех решений напишем несколько методов, которые будут реализовывать следующие задачи:

 

Реализация перечисленных задач позволит выполнять запись файлов в двух режимах, последовательная запись нескольких файлов, или перезапись текущего файла.

При обращении к записанному файлу следует учесть, что фильтр ASF освобождает файл для доступа сразу после останова графа, в отличие от фильтра AVI, который требует смены имени файла и только после этого освобождает файл. Перед началом работы необходимо модернизировать проект и установить Windows Media Format SDK. Модернизированный проект находится в папке под именем ProjectState7. Установочный пакет Windows Media Format SDK следует загрузить с сайта Microsoft.

[в начало]

Hosted by uCoz