DirectShow mp4 проигрыватель
В наличии винда Win7 x64 и студия VS2019.
Давно хочу увидеть какой-то живой пример исходников mp4 плеера. DirectShow вроде как должен уметь показывать видео.
У microsoft даже есть туториал DirectShow проигрывателя, там можно добавлять свои фильтры.
Кое-как получилось проиграть видео, но первый кадр ещё ничего себе, а остальное квадратиками. Некоторые видео более менее ничего себе, но большинство очень плохое качество. Какие фильтры для mp4+h264 (самые распространённые mp4) нужно подключить, и какие у них GUID?
С нуля понятно такую программу не напишешь, нашёл у microsoft примеры, скачал https://github.com/microsoft/Windows-classic-samples их.
Там в папке Samples\Win7Samples\Multimedia\DirectShow\Players\DShowPlayer Есть проэкт DShowPlayer.sln. Часть либ он берет с папки Samples\Win7Samples\Multimedia\DirectShow\common
Удалось с 3-го раза подружить между собой все *.cpp и *.h Чего то не сразу дошло что папку common нужно тоже перекопировать на два уровня ниже. Проэкт поднялся. AVI проигрывает нормально, а mp4 не играеет вообще никак.
Судя по сообщениям "не найдена комбинация фильтвов", понимаю что проблема с кодеками. Зная что есть старенький kmplayer который всё играет, нашел как ax-фильтр подкинуть в DirectShow. По моей логике - сначало нужен сплитер, который разделит потоки на аудио-видео. Думаю... это mp4splitter.ax. Нагуглил его GUID и как добавить фильтр. Открываем DShowPlayer.cpp, и допиливаем его, как ниже
// DShowPlayer.cpp: Implements DirectShow playback functionality.
// ....
// Находим метод
HRESULT DShowPlayer::OpenFile(const WCHAR* sFileName)
{
HRESULT hr = S_OK;
IBaseFilter *pSource = NULL;
// Create a new filter graph. (This also closes the old one, if any.)
hr = InitializeGraph();
// Add the source filter to the graph.
if (SUCCEEDED(hr))
{
hr = m_pGraph->AddSourceFilter(sFileName, NULL, &pSource);
}
// Добавляем сюда кодек
IUnknown* pUnk = 0; // Переменная для кодека
IUnknown* pUnk2 = 0;
GUID CLSID_MPEG4Splitter; // Гуид для кодека
CLSIDFromString(L"{61F47056-E400-43D3-AF1E-AB7DFFD4C4AD}", &CLSID_MPEG4Splitter); // Может можно проще, так точно работает
hr = CreateObjectFromPath(TEXT("mp4splitter.ax"), CLSID_MPEG4Splitter, &pUnk); // Загружаем фильтр
if (pUnk != 0) hr = pUnk->QueryInterface(IID_IBaseFilter, (void**)&pUnk2); // Приводим к нужному интерфейсу
if (pUnk2 != 0)
m_pGraph->AddFilter((IBaseFilter*)pUnk2, L"mp4splitter"); // Добавляем фильтр
// Остальное без изменений
Дальше... тут https://stackoverflow.com/questions/11191996/using-a-directshow-filter-without-registering-it-via-a-private-cocreateinstance забираем метод CreateObjectFromPath и добавляем его в DShowPlayer.cpp
typedef HRESULT(STDAPICALLTYPE* FN_DLLGETCLASSOBJECT)(REFCLSID clsid, REFIID iid, void** ppv);
HRESULT CreateObjectFromPath(TCHAR* pPath, REFCLSID clsid, IUnknown** ppUnk)
{
// load the target DLL directly
HMODULE lib = LoadLibrary(pPath);
if (!lib)
{
return HRESULT_FROM_WIN32(GetLastError());
}
// the entry point is an exported function
FN_DLLGETCLASSOBJECT fn = (FN_DLLGETCLASSOBJECT)GetProcAddress(lib, "DllGetClassObject");
if (fn == NULL)
{
return HRESULT_FROM_WIN32(GetLastError());
}
// create a class factory
IUnknown * pUnk;
HRESULT hr = fn(clsid, IID_IUnknown, (void**)(IUnknown**)&pUnk);
if (SUCCEEDED(hr))
{
IClassFactory * pCF = (IClassFactory*) pUnk;
if (pCF == NULL)
{
hr = E_NOINTERFACE;
}
else
{
// ask the class factory to create the object
hr = pCF->CreateInstance(NULL, IID_IUnknown, (void**)ppUnk);
}
}
return hr;
}
После таких махинаций mp4 начало некрасиво, но кое-как проигрывать.
Осталось понять какие ".ax" нужно взять, и какие guid кодеков из ax нужно добавить.