Войти

Показать полную графическую версию : [решено] *DirectShow* | не работает пример из SDK


pva
01-10-2009, 08:33
Здравствуйте мною глубокоуважаемые обитатели форума, да восхвалится имя ваше (во веки веков). У меня почему-то пример из MSDN перечисления устройств DirectShow захвата видео работает не так, как там описано:

// есть шаблон com_object<typename Interface>, который выполняет IUnknown::Release(),
// функции stop_error и log_error, которые сообщают результат HRESULT,
// stop* выдаёт исключение в случае ошибки, log* возвращает булевскую удачу-неудачу.

IEnumMoniker *dev_enum_val = 0;

// получаем перечислитель устройств
// com_object<Interface>::com_object<Interface>(Interface** intf, HRESULT hr) проверяет stop_error(hr) и инициализирует
// указатель на интерфейс значением *intf
com_object<IEnumMoniker> dev_enum(&dev_enum_val,
// com_object<Interface>::com_object<Interface>(const GUID&) использует CoCreateInstance для создания объекта
com_object<ICreateDevEnum>(CLSID_SystemDeviceEnum)->CreateClassEnumerator(
CLSID_VideoInputDeviceCategory, &dev_enum_val, 0));

// перебираем устройства внутри категории
IMoniker* dev_item_val=0;

while(!log_error(dev_enum->Next(1, &dev_item_val, 0))
{
com_object<IMoniker> dev_item(&dev_item_val, 0);
// я проверял, dev_item->GetDisplayName(0, 0, &name) даёт правильный результат,
// но дальше всё не по сценарию:

// получаем набор свойств устройства
IPropertyBag* prop_bag = 0;

if(log_error(dev_item->BindToStorage(0, 0, IID_IPropertyBag, (void **)&prop_bag_val)))
{
com_object<IPropertyBag> prop_bag(&prop_bag_val, 0);

// меня устраивает любое из свойств
variant<wstring> friendly_name;
if (
log_error(prop_bag->Read(L"Description", friendly_name.arg(), 0)) || // ошибка: неверный доступ к памяти
log_error(prop_bag->Read(L"FriendlyName", friendly_name.arg(), 0)) || // ошибка: неверный доступ к памяти
log_error(prop_bag->Read(L"Name", friendly_name.arg(), 0))) // ошибка: неверный доступ к памяти
{
// здесь, судя по примеру из MSDN, должно отображаться человеческое имя устройства
// но до сюда не доходит :(
MessageBoxW(Handle, friendly_name.value().c_str(), L"Enum", MB_OK);
}
}
}

вспомогательный код (если понадобится):

// шаблон com_object

void stop_error(HRESULT hr);
bool log_error(HRESULT hr);

template<typename Interface>
class com_object
{
public:
static const GUID& id();

~com_object() {_ptr->Release();}
com_object(const GUID& type_id, IUnknown* parent=0);
com_object(Interface** outer_interface, HRESULT hr);
com_object(com_object const& other) throw() : _ptr(other._ptr) {_ptr->AddRef();}
template<typename Interface2> com_object(com_object<Interface2> const&);
Interface* operator->() throw() {return _ptr;}
Interface* get() const throw() {return _ptr;}

private:
Interface* _ptr;
};

template<typename Interface>
com_object<Interface>::com_object(const GUID& type_id, IUnknown* parent) : _ptr()
{
stop_error(CoCreateInstance(type_id, parent, CLSCTX_INPROC_SERVER, id(), reinterpret_cast<void**>(&_ptr)));
}

template<typename Interface> com_object<Interface>::com_object(Interface** outer_interface, HRESULT hr) : _ptr(*outer_interface)
{
stop_error(hr);
if (!*outer_interface) throw runtime_error("got null interface");
}

template<typename Interface> template<typename Interface2>
com_object<Interface>::com_object(com_object<Interface2> const& other) : _ptr()
{
stop_error(other.get()->QueryInterface(id(), reinterpret_cast<void**>(_ptr)));
}


// шаблон variant

template<typename DataT>
class variant
{
public:
static const VARTYPE type_id();
~variant() {VariantClear(&_arg);}
variant() {VariantInit(&_arg);}
variant(const variant& v2) {VariantCopy(&_arg, &v2._arg);}
template<typename Data2>
variant(const variant<Data2>& v2) {VariantChangeType(&_arg, &v2._arg, 0, type_id());}
void reset() {VariantClear(&_arg);}
bool is_clear() const {return _arg.vt == VT_EMPTY;}
bool is_null() const {return _arg.vt == VT_NULL;}
bool is_error() const {return _arg.vt == VT_ERROR;}
DataT value() const;

// для вызовов API
VARIANT* arg() {return &_arg;}

private:
VARIANT _arg;

const VARIANT& _assert_value() const {
// проверка что именно нужного типа (а не VT_EMPTY например)
if (_arg.vt!=type_id()) throw logic_error("variant::_assert_value of different type");
return _arg;
}
};

template<> const VARTYPE variant<unsigned>::type_id(){return VT_UI4;}
template<> unsigned variant<unsigned>::value() const {return _assert_value().lVal;}
template<> const VARTYPE variant<int>::type_id() {return VT_I4;}
template<> int variant<int>::value() const {return _assert_value().ulVal;}
template<> const VARTYPE variant<float>::type_id() {return VT_R4;}
template<> float variant<float>::value() const {return _assert_value().fltVal;}
template<> const VARTYPE variant<double>::type_id() {return VT_R8;}
template<> double variant<double>::value() const {return _assert_value().dblVal;}
template<> const VARTYPE variant<wstring>::type_id() {return VT_BSTR;}
template<> wstring variant<wstring>::value() const {return wstring(_assert_value().bstrVal);}

Теперь отдельный вопрос про получение текстового описания сообщения об ошибке: У меня возничает ситуация, когда система не может выдать текстовое сообщение (FormatMessage выдаёт пустую строку). Как с этим бороться? Мне же надо знать, чем он недоволен

bool log_error(HRESULT hr)
{
if (hr)
{
hr &= 0xffff;

char *ddstr;
if (FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,
0, hr, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (char*) &ddstr, 0, 0))
{
MessageBoxA(0, ddstr, 0, MB_OK|MB_ICONERROR);
LocalFree(ddstr);
}
else
{
ostringstream ss;
ss << "Undefined error 0x" << hex << hr;
MessageBoxA(0, ss.str().c_str(), 0, MB_OK|MB_ICONERROR);
}
};

return hr==0;
}

void stop_error(HRESULT hr)
{
if (!log_error(hr))
{
throw runtime_error("check_error");
};
}

Специализация шаблона com_object для идентификации интрефейсов (простое правило):

#include <control.h>
#include <strmif.h>
#include <uuids.h>
template<> GUID const& com_object<ICreateDevEnum>::id() {return IID_ICreateDevEnum;}
template<> GUID const& com_object<IPropertyBag>::id() {return IID_IPropertyBag;}
template<> GUID const& com_object<IBaseFilter>::id() {return IID_IBaseFilter;}
template<> GUID const& com_object<ICaptureGraphBuilder2>::id() {return IID_ICaptureGraphBuilder2;}
template<> GUID const& com_object<IGraphBuilder>::id() {return IID_IGraphBuilder;}
template<> GUID const& com_object<IVideoWindow>::id() {return IID_IVideoWindow;}
template<> GUID const& com_object<IMediaControl>::id() {return IID_IMediaControl;}

Delirium
01-10-2009, 08:38
У меня почему-то пример из MSDN »
А какая версия MSDN? Их документация часто страдает опечатками и неточностями.

pva
01-10-2009, 12:52
А какая версия MSDN? »
Которая у них на сайте в онлайне

Проблема решена очень просто: ти ли я недоглядел, то ли они лоханулись, если сделать всё по аналогии с описанием группы интерфейсов IEnumXXXX, то получается так:

// дописал wostream& operator<<(wostream&, const GUID&);
// дописал log_message(const wstring&); которая добавляет строчку в ListView

unsigned long cnt_fetched = 0;
IMoniker* dev_item_val[1]; // здесь изменение
// беру только по одному объекту за раз (хотя можно и по 100 взять),
// только чтобы соблюсти правила безопасных исключений C++

while (log_error(filter_mons->Next(1, dev_item_val, &cnt_fetched)) && cnt_fetched)
{
com_object<IMoniker> dev_item(&dev_item_val[0], 0);

{
GUID dev_guid;
wchar_t* str_name;
stop_error(dev_item->GetClassID(&dev_guid));
stop_error(dev_item->GetDisplayName(0, 0, &str_name));

wostringstream ss;
ss << dev_guid << str_name;
__alloc->Free(str_name);

log_message(ss.str());
}

IPropertyBag* prop_bag_val;
com_object<IPropertyBag> prop_bag(&prop_bag_val,
dev_item->BindToStorage(0, 0, IID_IPropertyBag, (void**)&prop_bag_val));

variant<wstring> var_str;
if (log_error(prop_bag->Read(L"FriendlyName", var_str.arg(), 0)))
{
log_message(var_str.value());
}
}

И ещё использовал IFilterMapper2 чтобы получить список моникеров устройств. По крайней мере так сработало правильно.
Появились новые вопросы, некоторые помещу в другое темы; Тов. модератор, если сочтёшь нужным, собери обратно в одну тему

Вопрос: имя фильтра DirectShow содержит 2 классида: категория и устройство. Как их можно попрость COM узнать, что соответствует этим классидам? Через IPropertyBag? как узнать, какие вообще свойства есть в IPropertyBag (у меня не получилось создать IPropertyBag2, который это умеет)?

pva
03-10-2009, 12:47
пример заработал, был косяк в месте IEnumMoniker, нашёл способ получить описание фильтров: http://forum.oszone.net/thread-151991.html
чтобы получить список категорий: http://msdn.microsoft.com/en-us/library/dd375655(VS.85).aspx

вот что меня удивляет - это как с этим косяком у других работает, кто исходники в инете выкладывает.. у меня ни один не заработал




© OSzone.net 2001-2012