Skip to content

Instantly share code, notes, and snippets.

@sevaa
Created March 3, 2022 19:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sevaa/88754eafffd68d8671ec593dc310faec to your computer and use it in GitHub Desktop.
Save sevaa/88754eafffd68d8671ec593dc310faec to your computer and use it in GitHub Desktop.
#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