Угадайте кто? Да, это опять я со своим захватом видео, все еще в надеждах что мне кто-то поможет.
Итак, задачка старая — нужно сделать захват и encode видео в MPEG4 и звука в MP3. Все это нужно делать в риалтайме. Как это например сделано во FlyCap. Но теперь задача сделать это все через DirectShow.
Кто расскажет как и куда копать получит большое спасибо.
А если кто примером кода бросит, то и 12 баллов впридачу
Здравствуйте Slov, Вы писали:
S>Угадайте кто? Да, это опять я со своим захватом видео, все еще в надеждах что мне кто-то поможет. S>Итак, задачка старая — нужно сделать захват и encode видео в MPEG4 и звука в MP3. Все это нужно делать в риалтайме. Как это например сделано во FlyCap. Но теперь задача сделать это все через DirectShow. S>Кто расскажет как и куда копать получит большое спасибо. S>А если кто примером кода бросит, то и 12 баллов впридачу
А в чем проблема? В SDK римеры же есть этого захвата.
Здравствуйте Slov, Вы писали:
S>С записью этого дела в MPEG4 формате? Подскажи как называется и где искать.
Нужно построить граф и вставить в него mpeg4encoder, ну DivX, например.
Что бы говорить о конкретных деталях реализации, нужно увидеть как ты сейчас строишь граф, поэтому приведи код или скажи какой из примеров DS SDK ты используешь?
Здравствуйте scs, Вы писали:
scs>Нужно построить граф и вставить в него mpeg4encoder, ну DivX, например.
scs>Что бы говорить о конкретных деталях реализации, нужно увидеть как ты сейчас строишь граф, поэтому приведи код или скажи какой из примеров DS SDK ты используешь?
По правде сказать я бы не хотел использовать имеющиеся примеры, потому как не люблю реализации интерфейсов на чистом API, да и вообще организация кода примеров PSDK мне не нравится. Я скорее воспользуюсь WTL, но это не важно, так что будем считать что я использую PlayCap sample из DS SDK как самый простой. Теперь мне нужно кодировать видео через Microsoft MPEG4 V1/2 Codec, а аудио соотв MPEG3 и писать это в файл.
В любом случае большое спасибо за помощь.
Здравствуйте Slov, Вы писали:
S>По правде сказать я бы не хотел использовать имеющиеся примеры, потому как не люблю реализации интерфейсов на чистом API, да и вообще организация кода примеров PSDK мне не нравится. Я скорее воспользуюсь WTL, но это не важно, так что будем считать что я использую PlayCap sample из DS SDK как самый простой. Теперь мне нужно кодировать видео через Microsoft MPEG4 V1/2 Codec, а аудио соотв MPEG3 и писать это в файл. S>В любом случае большое спасибо за помощь.
S>Итак, задачка старая — нужно сделать захват и encode видео в MPEG4 и звука в MP3. Все это нужно делать в риалтайме. Как это например сделано во FlyCap. Но теперь задача сделать это все через DirectShow.
Нужно построить граф и вставить в него mpeg4encoder, ну DivX, например.
Что бы говорить о конкретных деталях реализации, нужно увидеть как ты сейчас строишь граф, поэтому приведи код или скажи какой из примеров DS SDK ты используешь?
S>По правде сказать я бы не хотел использовать имеющиеся примеры, потому как не люблю реализации интерфейсов на чистом API, да и вообще организация кода примеров PSDK мне не нравится. Я скорее воспользуюсь WTL, но это не важно, так что будем считать что я использую PlayCap sample из DS SDK как самый простой. Теперь мне нужно кодировать видео через Microsoft MPEG4 V1/2 Codec, а аудио соотв MPEG3 и писать это в файл. S>Нет, правда покажи как добавить этот самый граф и на звук тоже, а?
Ну ладно, сам напросился
Ниже будет приведен код создания графа в общем виде, он содержат вспомогательные функции:
// Функция находит фильтр по имени в system device enumerator и возвращает его.
// Параметры: [in] CComBSTR& bstrFilterName - имя фильтра
// [out] CComPtr<IBaseFilter>& pFilter - найденный фильтр
// [in] REFCLSID clsidDeviceClass - CLSID категории фильтра (см. DS SDK)
HRESULT GetFilter(CComBSTR& bstrFilterName, CComPtr<IBaseFilter>& pFilter, REFCLSID clsidDeviceClass)
{
HRESULT hr;
CComQIPtr<ICreateDevEnum, &IID_ICreateDevEnum> spSysDevEnum;
hr = spSysDevEnum.CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC);
if (SUCCEEDED(hr))
{
CComPtr<IEnumMoniker> pEnum;
hr = spSysDevEnum->CreateClassEnumerator(clsidDeviceClass, &pEnum, 0);
if (S_OK == hr)) // было неправильно - if ( SUCCEEDED(hr) ), см. примечание Snax
{
CComPtr<IMoniker> pMoniker;
ULONG cFetched;
// Enumerate over every category
ATLASSERT(pEnum);
while (SUCCEEDED(pEnum->Next(1, &pMoniker, &cFetched)))
{
if (!pMoniker)
{
hr = E_FAIL;
break;
}
CComQIPtr<IPropertyBag> pPropBag;
// Associate moniker with a file
hr = pMoniker->BindToStorage(0, 0, IID_IPropertyBag, (void **)&pPropBag);
if SUCCEEDED(hr)
{
CComVariant varCurFilterName;
varCurFilterName.vt = VT_BSTR;
ATLASSERT(pPropBag);
// Read FriendlyName from property bag
pPropBag->Read(L"FriendlyName", &varCurFilterName, 0);
if (bstrFilterName == varCurFilterName.bstrVal) // is this our filter?
{
// Create filter using IMoniker
pMoniker->BindToObject(0, 0, IID_IBaseFilter, (void**) &pFilter);
#ifdef DEBUG
FILTER_INFO fiRetrieved;
pFilter->QueryFilterInfo(&fiRetrieved);
ATLTRACE("SUCCEED: (GetFilter) The %s filter have been retreived\n", bstrFilterName);
#endif// DEBUGreturn S_OK;
}
}
pMoniker = NULL;
}
ATLTRACE("FAILED: (GetFilter) The %s filter have not been retreived\n", bstrFilterName);
}
}
return hr;
}
// Функция находит неподключенный пин у фильтра и возвращает его.
// Параметры: [in] PIN_INFO* pPinInfo - информация по которой ищется пин (см. DS SDK)
// [out] CComPtr<IPin>& pPin - найденный пин
HRESULT GetPin(PIN_INFO* pPinInfo, CComPtr<IPin>& pPin)
{
HRESULT hr;
if (!pPinInfo) return E_POINTER;
if (pPinInfo->pFilter)
{
CComPtr<IEnumPins> pEnumPins;
ULONG cFetched;
hr = pPinInfo->pFilter->EnumPins(&pEnumPins);
ATLASSERT(pEnumPins);
// Enumerate all pins of the filterwhile(pEnumPins->Next(1, &pPin, &cFetched) == S_OK)
{
PIN_INFO piPinInfo;
// We need to get pin information to compare it with our PinInfo
hr = pPin->QueryPinInfo(&piPinInfo);
if (SUCCEEDED(hr))
{
// We compare direction of the pin if it is the same it is our
// pin.
// We do not need a pin which have '~' first symbol because
// GraphBuilder do not connect pins which have this.if (piPinInfo.dir == pPinInfo->dir && piPinInfo.achName[0] != L'~')
{
// We are interesting in disconnected pins therefore we
// need to check the connection using ConnectedTo().
CComPtr<IPin> pConnectedPin;
hr = pPin->ConnectedTo(&pConnectedPin);
if (hr == VFW_E_NOT_CONNECTED || !pConnectedPin)
{
hr = S_OK;
break;
}
}
}
pPin = NULL; // release this pin to get next
}
}
else
{
hr = E_INVALIDARG;
}
return hr;
}
// Функция соединяет два фильтра черз их пины
// Параметры: [in] IBaseFilter* pSourceFilter - это вышестоящий фильтр
// [in] IBaseFilter* pReceiveFilter - это нижестоящий фильтр
// [in] IGraphBuilder* pGraphBuilder - это GraphBuilder
HRESULT ConnectFilters(IBaseFilter* pSourceFilter, IBaseFilter* pReceiveFilter, IGraphBuilder* pGraphBuilder)
{
HRESULT hr;
CComPtr<IPin> pOutputPin;
CComPtr<IPin> pInputPin;
PIN_INFO piPinInfo = {pSourceFilter, PINDIR_OUTPUT, {0}};
// Get output pin
hr = GetPin(&piPinInfo, pOutputPin);
if (SUCCEEDED(hr))
{
ATLASSERT(pOutputPin);
piPinInfo.pFilter = pReceiveFilter;
piPinInfo.dir = PINDIR_INPUT;
// Get input pin
hr = GetPin(&piPinInfo, pInputPin);
// Connect two pinsif (SUCCEEDED(hr))
hr = pGraphBuilder->Connect(pOutputPin, pInputPin);
}
return hr;
}
Теперь что касается создания самого графа:
HRESULT hr;
CComPtr<IGraphBuilder> pGraphBuilder;
CComPtr<IBaseFilter> pVideoEncoderFilter;
CComPtr<IBaseFilter> pAudioEncoderFilter;
CComPtr<IBaseFilter> pAVIMuxFilter;
CComPtr<IBaseFilter> pFileWriterFilter;
CComPtr<IMediaControl> pMediaControl;
hr = pGraphBuilder.CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC);
// Далее находим видео-кодировщик, ну DivX, напримерif (SUCCEEDED(hr))
{
hr = GetFilter(CComBSTR("Microsoft MPEG-4 Video Codec V3"), pVideoEncoderFilter, CLSID_VideoCompressorCategory);
if (SUCCEEDED(hr))
{
// Далее находим аудио-кодировщик, ну MP3, например
hr = GetFilter(CComBSTR("MPEG Layer-3"), pAudioEncoderFilter, CLSID_AudioCompressorCategory);
if (SUCCEEDED(hr))
{
// Далее находим AVI-Mux
hr = GetFilter(CComBSTR("AVI Mux"), pAVIMuxFilter, CLSID_LegacyAmFilterCategory);
if (SUCCEEDED(hr))
{
// Далее находим FileWriter
hr = GetFilter(CComBSTR("File writer"), pFileWriterFilter, CLSID_LegacyAmFilterCategory);
// потом устанавливаем имя файла, предварительно получив интерфейс IFileSinkFilter
CComPtr<IFileSinkFilter> pFileSinkFilter;
hr = pFilter.QueryInterface(&pFileSinkFilter);
ATLASSERT(pFileSinkFilter);
hr = pFileSinkFilter->SetFileName(L"test.avi", NULL);
}
}
}
}
if (SUCCEEDED(hr))
{
// теперь добавляем найденные фильтры в граф
pGraphBuilder->AddFilter(pVideoEncoderFilter, L"Filter 1");
pGraphBuilder->AddFilter(pAudioEncoderFilter, L"Filter 2");
pGraphBuilder->AddFilter(pAVIMuxFilter, L"Filter 3");
pGraphBuilder->AddFilter(pFileWriterFilter, L"Filter 4");
// теперь все эти фильтры соединяем, попутно находя соответствующие пины
ConnectFilters(pVideoEncoderFilter, pAVIMuxFilter, pGraphBuilder);
ConnectFilters(pAudioEncoderFilter, pAVIMuxFilter, pGraphBuilder);
ConnectFilters(pAVIMuxFilter, pFileWriterFilter, pGraphBuilder);
// теперь запрашиваем интерфейс IMediaControl
hr = pGraphBuilder.QueryInterface(&pMediaControl);
if (SUCCEEDED(hr))
hr = pMediaControl->Run(); // и начинаем писать наш фильм
}
Ну в общем примерно так нужно делать. Что нехватает в вышеприведенном коде: надо добавить фильтр источник аудио и видео данных. Если ты используешь CaptureDevice, то можно использовать ICaptureGraphBuilder2 интерфейс. Ну как его пользовать очень хорошо на примерах показано в DS SDK.
Да, вместо ICaptureGraphBuilder2, можно и простой граф использовать, просто для WDM capture device он удобнее. А можно с помощью SetFiltergraph упомянутого интерфейса передать ему уже готовый построенный граф.
Здравствуйте scs, Вы писали:
scs>Ниже будет приведен код создания графа в общем виде, он содержат вспомогательные функции:
scs>
scs>// Функция находит фильтр по имени в system device enumerator и возвращает его.
scs>// Параметры: [in] CComBSTR& bstrFilterName - имя фильтра
scs>// [out] CComPtr<IBaseFilter>& pFilter - найденный фильтр
scs>// [in] REFCLSID clsidDeviceClass - CLSID категории фильтра (см. DS SDK)
scs>HRESULT GetFilter(CComBSTR& bstrFilterName, CComPtr<IBaseFilter>& pFilter, REFCLSID clsidDeviceClass)
scs>{
scs> HRESULT hr;
scs> CComQIPtr<ICreateDevEnum, &IID_ICreateDevEnum> spSysDevEnum;
scs> hr = spSysDevEnum.CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC);
scs> if (SUCCEEDED(hr))
scs> {
scs> CComPtr<IEnumMoniker> pEnum;
scs> hr = spSysDevEnum->CreateClassEnumerator(clsidDeviceClass, &pEnum, 0);
scs> if (SUCCEEDED(hr))
scs> {
// почикано
scs> }
scs> }
scs> return hr;
scs>}
scs>
Внимание, вопрос! Что вернет ICreateDevEnum::CreateClassEnumerator() если в конкретной
системе нет ни одного фильтра этой категории? Правильно, S_FALSE. И NULL в ppEnumMoniker!!!
Здравствуйте Snax, Вы писали:
S>Внимание, вопрос! Что вернет ICreateDevEnum::CreateClassEnumerator() если в конкретной S>системе нет ни одного фильтра этой категории? Правильно, S_FALSE. И NULL в ppEnumMoniker!!!
S>Так что выделенный выше код нужно бы заменить на
S>
S>Иначе застрелится при первом же вызове pEnum->Next().
S>Кроме того, в документации по IEnumXXXX рекомендуют вызывать IEnumXXXX::Reset() до первого S>IEnumXXXX::Next(). Хотя в данном случае это не актуально.
S>Павел. S>P.S. Люди! Читайте документацию по используемым компонентам! Сэкономите время и здоровье.
Согласен по всем пунктам, это мой старый код (хотя он тоже работает), я потом только заметил, когда отправил, поэтому это место действительно лучше подправить.