Created
March 3, 2022 19:57
-
-
Save sevaa/88754eafffd68d8671ec593dc310faec to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#define INITGUID | |
#include <SDKDDKVer.h> | |
#define WIN32_LEAN_AND_MEAN | |
#include <windows.h> | |
#include <mfidl.h> | |
#include <mfapi.h> | |
#include <evr.h> | |
#include <cguid.h> | |
#include <propvarutil.h> | |
#include <comdef.h> | |
#include <crtdbg.h> | |
#pragma comment(lib, "Mfplat.lib") | |
#pragma comment(lib, "Mf.lib") | |
_COM_SMARTPTR_TYPEDEF(IMFTopology, IID_IMFTopology); | |
_COM_SMARTPTR_TYPEDEF(IMFSourceResolver, IID_IMFSourceResolver); | |
_COM_SMARTPTR_TYPEDEF(IMFMediaSource, __uuidof(IMFMediaSource)); | |
_COM_SMARTPTR_TYPEDEF(IMFPresentationDescriptor, IID_IMFPresentationDescriptor); | |
_COM_SMARTPTR_TYPEDEF(IMFStreamDescriptor, IID_IMFStreamDescriptor); | |
_COM_SMARTPTR_TYPEDEF(IMFTopologyNode, IID_IMFTopologyNode); | |
_COM_SMARTPTR_TYPEDEF(IMFMediaTypeHandler, IID_IMFMediaTypeHandler); | |
_COM_SMARTPTR_TYPEDEF(IMFMediaSession, IID_IMFMediaSession); | |
_COM_SMARTPTR_TYPEDEF(IMFVideoDisplayControl, __uuidof(IMFVideoDisplayControl)); | |
_COM_SMARTPTR_TYPEDEF(IMFActivate, IID_IMFActivate); | |
_COM_SMARTPTR_TYPEDEF(IMFGetService, __uuidof(IMFGetService)); | |
_COM_SMARTPTR_TYPEDEF(IMFMediaEvent, __uuidof(IMFMediaEvent)); | |
_COM_SMARTPTR_TYPEDEF(IMFMediaEventGenerator, __uuidof(IMFMediaEventGenerator)); | |
IMFMediaSessionPtr g_Sess; | |
IMFMediaEventGeneratorPtr g_SessEvents; | |
IMFActivatePtr g_AudioSink, g_VideoSink; | |
static void Check(HRESULT hr) | |
{ | |
if (!SUCCEEDED(hr)) | |
_com_raise_error(hr); | |
} | |
static void Load(LPCWSTR Name, IMFTopologyPtr &Topo) | |
{ | |
Check(MFCreateTopology(&Topo)); | |
IMFSourceResolverPtr sres; | |
Check(MFCreateSourceResolver(&sres)); | |
IUnknownPtr usrc; | |
MF_OBJECT_TYPE otype; | |
wchar_t URL[300]; | |
wcscpy_s(URL, L"file://"); | |
wcscat_s(URL, Name); | |
Check(sres->CreateObjectFromURL(URL, MF_RESOLUTION_MEDIASOURCE, 0, &otype, &usrc)); | |
IMFMediaSourcePtr src(usrc); | |
src = usrc; | |
IMFPresentationDescriptorPtr pdesc; | |
Check(src->CreatePresentationDescriptor(&pdesc)); | |
DWORD i, nStm; | |
Check(pdesc->GetStreamDescriptorCount(&nStm)); | |
for (i = 0; i < nStm; i++) | |
{ | |
IMFStreamDescriptorPtr sdesc; | |
BOOL Sel; | |
Check(pdesc->GetStreamDescriptorByIndex(i, &Sel, &sdesc)); | |
if (Sel) | |
{ | |
IMFMediaTypeHandlerPtr ha; | |
Check(sdesc->GetMediaTypeHandler(&ha)); | |
GUID mtype; | |
Check(ha->GetMajorType(&mtype)); | |
IMFActivatePtr act; | |
if (InlineIsEqualGUID(mtype, MFMediaType_Audio)) | |
act = g_AudioSink; | |
else if (InlineIsEqualGUID(mtype, MFMediaType_Video)) | |
act = g_VideoSink; | |
IMFTopologyNodePtr SrcNode, OutNode; | |
Check(MFCreateTopologyNode(MF_TOPOLOGY_SOURCESTREAM_NODE, &SrcNode)); | |
Check(SrcNode->SetUnknown(MF_TOPONODE_SOURCE, src)); | |
Check(SrcNode->SetUnknown(MF_TOPONODE_PRESENTATION_DESCRIPTOR, pdesc)); | |
Check(SrcNode->SetUnknown(MF_TOPONODE_STREAM_DESCRIPTOR, sdesc)); | |
Check(Topo->AddNode(SrcNode)); | |
Check(MFCreateTopologyNode(MF_TOPOLOGY_OUTPUT_NODE, &OutNode)); | |
Check(OutNode->SetObject(act)); | |
Check(OutNode->SetUINT32(MF_TOPONODE_STREAMID, 0)); | |
Check(Topo->AddNode(OutNode)); | |
Check(SrcNode->ConnectOutput(0, OutNode, 0)); | |
} | |
} | |
} | |
class CCallback : public IMFAsyncCallback | |
{ | |
STDMETHODIMP QueryInterface(REFIID iid, void** ppv) | |
{ | |
if (InlineIsEqualGUID(iid, IID_IUnknown) || InlineIsEqualGUID(iid, __uuidof(IMFAsyncCallback))) | |
{ | |
*ppv = this; | |
return S_OK; | |
} | |
else | |
return E_NOINTERFACE; | |
} | |
STDMETHODIMP_(ULONG) AddRef() { return 1; } | |
STDMETHODIMP_(ULONG) Release() { return 1; } | |
STDMETHODIMP GetParameters(DWORD*, DWORD*) { return E_NOTIMPL; } | |
STDMETHODIMP Invoke(IMFAsyncResult* pRes) | |
{ | |
if (g_SessEvents) | |
{ | |
IMFMediaEventPtr pEvt; | |
g_SessEvents->EndGetEvent(pRes, &pEvt); | |
MediaEventType et = 0; | |
Check(pEvt->GetType(&et)); | |
if (et == MESessionTopologySet) | |
{ | |
PROPVARIANT v; | |
InitPropVariantFromInt64(0, &v); | |
Check(g_Sess->Start(&GUID_NULL, &v)); | |
} | |
g_SessEvents->BeginGetEvent(this, 0); | |
} | |
return S_OK; | |
} | |
} g_Callback; | |
LRESULT CALLBACK PlayerWndProc(HWND hWnd, unsigned Msg, WPARAM wParam, LPARAM lParam) | |
{ | |
static IMFTopologyPtr Topo[2]; | |
static int Playing = 0; | |
static int i = 1; | |
PAINTSTRUCT ps; | |
switch (Msg) | |
{ | |
case WM_CREATE: | |
Check(MFCreateVideoRendererActivate(hWnd, &g_VideoSink)); | |
Load(L"C:\\Temp\\SlowPlayer\\a.m4v", Topo[0]); | |
Load(L"C:\\Temp\\SlowPlayer\\b.m4v", Topo[1]); | |
Check(g_Sess->SetTopology(MFSESSION_SETTOPOLOGY_IMMEDIATE, Topo[0])); | |
//Start will be called in the event handler | |
SetTimer(hWnd, 0, 10*1000, 0); | |
break; | |
case WM_TIMER: | |
_RPT1(0, "Switch %d\n", i++); | |
Playing = 1 - Playing; | |
Check(g_Sess->SetTopology(MFSESSION_SETTOPOLOGY_IMMEDIATE, Topo[Playing])); | |
break; | |
case WM_NCHITTEST: | |
return HTCAPTION; | |
case WM_PAINT: | |
BeginPaint(hWnd, &ps); | |
EndPaint(hWnd, &ps); | |
break; | |
default: | |
return DefWindowProc(hWnd, Msg, wParam, lParam); | |
} | |
return 0; | |
} | |
int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE, LPWSTR, int) | |
{ | |
Check(CoInitializeEx(0, COINIT_APARTMENTTHREADED)); | |
Check(MFStartup(MF_VERSION, MFSTARTUP_LITE)); | |
SetThreadExecutionState(ES_DISPLAY_REQUIRED | ES_CONTINUOUS); | |
Check(MFCreateMediaSession(0, &g_Sess)); | |
g_SessEvents = g_Sess; | |
Check(g_SessEvents->BeginGetEvent(&g_Callback, 0)); | |
Check(MFCreateAudioRendererActivate(&g_AudioSink)); | |
//Video renderer will be created along with the window | |
WNDCLASSEXW wcex; | |
wcex.cbSize = sizeof(WNDCLASSEX); | |
wcex.style = CS_HREDRAW | CS_VREDRAW; | |
wcex.lpfnWndProc = PlayerWndProc; | |
wcex.cbClsExtra = wcex.cbWndExtra = 0; | |
wcex.hInstance = hInstance; | |
wcex.hIcon = wcex.hIconSm = 0; | |
wcex.hCursor = LoadCursor(0, IDC_ARROW); | |
wcex.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); | |
wcex.lpszMenuName = 0; | |
wcex.lpszClassName = L"SlowPlayer"; | |
HWND hWnd = CreateWindowEx(WS_EX_TOPMOST, (LPCWSTR)RegisterClassEx(&wcex), | |
0, WS_POPUP|WS_VISIBLE, 0, 0, 320, 240, 0, 0, hInstance, 0); | |
MSG msg; | |
while (GetMessage(&msg, nullptr, 0, 0)) | |
{ | |
TranslateMessage(&msg); | |
DispatchMessage(&msg); | |
} | |
MFShutdown(); | |
CoUninitialize(); | |
return (int)msg.wParam; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment