summaryrefslogtreecommitdiff
path: root/src/player/backends
diff options
context:
space:
mode:
Diffstat (limited to 'src/player/backends')
-rw-r--r--src/player/backends/direct_show.c464
-rw-r--r--src/player/backends/media_foundation.cpp1110
-rw-r--r--src/player/backends/media_foundation.h115
-rw-r--r--src/player/backends/utility/com_ptr.h148
-rw-r--r--src/player/backends/utility/optional.h517
5 files changed, 2354 insertions, 0 deletions
diff --git a/src/player/backends/direct_show.c b/src/player/backends/direct_show.c
new file mode 100644
index 0000000..111f042
--- /dev/null
+++ b/src/player/backends/direct_show.c
@@ -0,0 +1,464 @@
+/*
+Copyright (c) 2015
+Michał Cichoń <thedmd@interia.pl>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+
+/* receive/play audio stream */
+
+/* based on DShow example player */
+
+#include "config.h"
+#include "../player2_private.h"
+#define COBJMACROS
+#define INITGUID
+#include <objbase.h>
+#include <dshow.h>
+#pragma comment(lib, "strmiids.lib")
+
+# define WM_GRAPH_EVENT (WM_APP + 1)
+
+enum { NO_GRAPH, RUNNING, PAUSED, STOPPED };
+
+static struct _player_static_t
+{
+ bool done;
+ bool initialized;
+ bool hasCOM;
+} BarPlayerGlobal = { 0 };
+
+struct _player_t
+{
+ int state;
+ IGraphBuilder* graph;
+ IMediaControl* control;
+ IMediaEventEx* event;
+ IBasicAudio* audio;
+ IMediaSeeking* media;
+ float volume; // dB
+ float gain; // dB
+};
+
+static bool DSPlayerStaticInit();
+static void DSPlayerStaticTerm(void);
+
+static bool DSPlayerStaticInit()
+{
+ if (BarPlayerGlobal.done)
+ return BarPlayerGlobal.initialized;
+
+ BarPlayerGlobal.done = true;
+
+ atexit(DSPlayerStaticTerm);
+
+ if (FAILED(CoInitializeEx(NULL, COINIT_MULTITHREADED)))
+ return false;
+
+ BarPlayerGlobal.hasCOM = true;
+
+ BarPlayerGlobal.initialized = true;
+
+ return true;
+}
+
+static void DSPlayerStaticTerm(void)
+{
+ if (!BarPlayerGlobal.done)
+ return;
+
+ if (BarPlayerGlobal.hasCOM)
+ {
+ CoUninitialize();
+ BarPlayerGlobal.hasCOM = false;
+ }
+
+ BarPlayerGlobal.initialized = false;
+ BarPlayerGlobal.done = false;
+}
+
+static void DSPlayerApplyVolume(player2_t player)
+{
+ long v = (long)((player->volume + player->gain) * 100.0f);
+
+ if (!player->audio)
+ return;
+
+ if (v < -10000)
+ v = -10000;
+ if (v > 0)
+ v = 0;
+
+ IBasicAudio_put_Volume(player->audio, v);
+}
+
+static void DSPlayerTearDown(player2_t player)
+{
+ /* TODO: send final event */
+
+ if (player->graph)
+ {
+ IGraphBuilder_Release(player->graph);
+ player->graph = NULL;
+ }
+
+ if (player->control)
+ {
+ IMediaControl_Release(player->control);
+ player->control = NULL;
+ }
+
+ if (player->event)
+ {
+ IMediaEventEx_Release(player->event);
+ player->event = NULL;
+ }
+
+ if (player->audio)
+ {
+ IBasicAudio_Release(player->audio);
+ player->audio = NULL;
+ }
+
+ if (player->media)
+ {
+ IMediaSeeking_Release(player->media);
+ player->media = NULL;
+ }
+
+ player->state = NO_GRAPH;
+}
+
+static HRESULT DSPlayerBuild(player2_t player)
+{
+ HRESULT hr;
+
+ DSPlayerTearDown(player);
+
+ hr = CoCreateInstance(&CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, &IID_IGraphBuilder, &player->graph);
+ if (FAILED(hr))
+ return hr;
+
+ hr = IGraphBuilder_QueryInterface(player->graph, &IID_IMediaControl, &player->control);
+ if (FAILED(hr))
+ return hr;
+
+ hr = IGraphBuilder_QueryInterface(player->graph, &IID_IMediaEventEx, &player->event);
+ if (FAILED(hr))
+ return hr;
+
+ hr = IGraphBuilder_QueryInterface(player->graph, &IID_IBasicAudio, &player->audio);
+ if (FAILED(hr))
+ return hr;
+
+ hr = IGraphBuilder_QueryInterface(player->graph, &IID_IMediaSeeking, &player->media);
+ if (FAILED(hr))
+ return hr;
+
+ hr = IMediaEventEx_SetNotifyWindow(player->event, (OAHWND)NULL, WM_GRAPH_EVENT, (LONG_PTR)player);
+ if (FAILED(hr))
+ return hr;
+
+ player->state = STOPPED;
+
+ return S_OK;
+}
+
+static HRESULT DSPlayerAddFilterByCLSID(IGraphBuilder *pGraph, REFGUID clsid, IBaseFilter **ppF, LPCWSTR wszName)
+{
+ IBaseFilter *pFilter = NULL;
+ HRESULT hr;
+ *ppF = 0;
+
+ hr = CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER, &IID_IBaseFilter, &pFilter);
+ if (FAILED(hr))
+ goto done;
+
+ hr = IGraphBuilder_AddFilter(pGraph, pFilter, wszName);
+ if (FAILED(hr))
+ goto done;
+
+ *ppF = pFilter;
+
+ IBaseFilter_AddRef(*ppF);
+
+done:
+ if (pFilter)
+ IBaseFilter_Release(pFilter);
+
+ return hr;
+}
+
+static HRESULT DSPlayerRender(player2_t player, IBaseFilter* source)
+{
+ BOOL bRenderedAnyPin = FALSE;
+
+ IPin* pin = NULL;
+ IEnumPins *enumPins = NULL;
+ IBaseFilter *audioRenderer = NULL;
+ IFilterGraph2* filter = NULL;
+
+ HRESULT hr;
+
+ hr = IGraphBuilder_QueryInterface(player->graph, &IID_IFilterGraph2, &filter);
+ if (FAILED(hr))
+ return hr;
+
+ hr = DSPlayerAddFilterByCLSID(player->graph, &CLSID_DSoundRender, &audioRenderer, L"Audio Renderer");
+ if (FAILED(hr))
+ goto done;
+
+ hr = IBaseFilter_EnumPins(source, &enumPins);
+ if (FAILED(hr))
+ goto done;
+
+ while (S_OK == IEnumPins_Next(enumPins, 1, &pin, NULL))
+ {
+ HRESULT hr2 = IFilterGraph2_RenderEx(filter, pin, AM_RENDEREX_RENDERTOEXISTINGRENDERERS, NULL);
+
+ IPin_Release(pin);
+ if (SUCCEEDED(hr2))
+ bRenderedAnyPin = TRUE;
+ }
+
+done:
+ if (enumPins)
+ IEnumPins_Release(enumPins);
+ if (enumPins)
+ IBaseFilter_Release(audioRenderer);
+ if (enumPins)
+ IFilterGraph2_Release(filter);
+
+ if (SUCCEEDED(hr) && !bRenderedAnyPin)
+ hr = VFW_E_CANNOT_RENDER;
+
+ return hr;
+}
+
+static player2_t DSPlayerCreate()
+{
+ player2_t out = NULL;
+
+ if (!DSPlayerStaticInit())
+ return NULL;
+
+ out = malloc(sizeof(struct _player_t));
+ if (!out)
+ return NULL;
+
+ memset(out, 0, sizeof(struct _player_t));
+
+ return out;
+}
+
+static void DSPlayerDestroy(player2_t player)
+{
+ DSPlayerTearDown(player);
+ free(player);
+}
+
+static void DSPlayerSetVolume(player2_t player, float volume)
+{
+ player->volume = volume;
+ DSPlayerApplyVolume(player);
+}
+
+static float DSPlayerGetVolume(player2_t player)
+{
+ return player->volume;
+}
+
+static void DSPlayerSetGain(player2_t player, float gain)
+{
+ player->gain = gain;
+ DSPlayerApplyVolume(player);
+}
+
+static float DSPlayerGetGain(player2_t player)
+{
+ return player->gain;
+}
+
+static double DSPlayerGetDuration(player2_t player)
+{
+ LONGLONG time;
+ if (SUCCEEDED(IMediaSeeking_GetDuration(player->media, &time)))
+ return time / 10000000.0;
+ else
+ return 0;
+}
+
+static double DSPlayerGetTime(player2_t player)
+{
+ LONGLONG time;
+ if (SUCCEEDED(IMediaSeeking_GetCurrentPosition(player->media, &time)))
+ return time / 10000000.0;
+ else
+ return 0;
+}
+
+static bool DSPlayerOpen(player2_t player, const char* url)
+{
+ IBaseFilter* source = NULL;
+ HRESULT hr;
+ wchar_t* wideUrl = NULL;
+ size_t urlSize;
+ int result;
+
+ hr = DSPlayerBuild(player);
+ if (FAILED(hr))
+ goto done;
+
+ urlSize = strlen(url);
+ result = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, url, urlSize, NULL, 0);
+ wideUrl = malloc((result + 1) * sizeof(wchar_t));
+ if (!wideUrl)
+ {
+ hr = E_OUTOFMEMORY;
+ goto done;
+ }
+ memset(wideUrl, 0, (result + 1) * sizeof(wchar_t));
+
+ MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, url, urlSize, wideUrl, result);
+
+ hr = IGraphBuilder_AddSourceFilter(player->graph, wideUrl, NULL, &source);
+ if (FAILED(hr))
+ goto done;
+
+ hr = DSPlayerRender(player, source);
+
+ DSPlayerApplyVolume(player);
+
+done:
+ if (wideUrl)
+ free(wideUrl);
+ if (FAILED(hr))
+ DSPlayerTearDown(player);
+ if (source)
+ IBaseFilter_Release(source);
+
+ return SUCCEEDED(hr);
+}
+
+static bool DSPlayerPlay(player2_t player)
+{
+ HRESULT hr;
+
+ if (player->state != PAUSED && player->state != STOPPED)
+ return false; /* wrong state */
+
+ hr = IMediaControl_Run(player->control);
+ if (SUCCEEDED(hr))
+ player->state = RUNNING;
+
+ return SUCCEEDED(hr);
+}
+
+static bool DSPlayerPause(player2_t player)
+{
+ HRESULT hr;
+
+ if (player->state != RUNNING)
+ return false; /* wrong state */
+
+ hr = IMediaControl_Pause(player->control);
+ if (SUCCEEDED(hr))
+ player->state = PAUSED;
+
+ return SUCCEEDED(hr);
+}
+
+static bool DSPlayerStop(player2_t player)
+{
+ HRESULT hr;
+
+ if (player->state != RUNNING && player->state != PAUSED)
+ return false; /* wrong state */
+
+ hr = IMediaControl_Stop(player->control);
+ if (SUCCEEDED(hr))
+ player->state = STOPPED;
+
+ return SUCCEEDED(hr);
+}
+
+static bool DSPlayerFinish(player2_t player)
+{
+ if (!player->control)
+ return false;
+
+ DSPlayerTearDown(player);
+ return true;
+}
+
+static bool DSPlayerIsPlaying(player2_t player)
+{
+ return player->state == RUNNING;
+}
+
+static bool DSPlayerIsPaused(player2_t player)
+{
+ return player->state == PAUSED;
+}
+
+static bool DSPlayerIsStopped(player2_t player)
+{
+ return player->state == STOPPED;
+}
+
+static bool DSPlayerIsFinished(player2_t player)
+{
+ LONGLONG time;
+ LONGLONG duration;
+
+ if (!player->media || player->state == NO_GRAPH)
+ return true;
+
+ if (player->state != RUNNING && player->state != STOPPED)
+ return false;
+
+ if (FAILED(IMediaSeeking_GetDuration(player->media, &duration)) ||
+ FAILED(IMediaSeeking_GetCurrentPosition(player->media, &time)))
+ return true;
+
+ return time >= duration;
+}
+
+player2_iface player2_direct_show =
+{
+ .Id = "ds",
+ .Name = "Direct Show",
+ .Create = DSPlayerCreate,
+ .Destroy = DSPlayerDestroy,
+ .SetVolume = DSPlayerSetVolume,
+ .GetVolume = DSPlayerGetVolume,
+ .SetGain = DSPlayerSetGain,
+ .GetGain = DSPlayerGetGain,
+ .GetDuration = DSPlayerGetDuration,
+ .GetTime = DSPlayerGetTime,
+ .Open = DSPlayerOpen,
+ .Play = DSPlayerPlay,
+ .Pause = DSPlayerPause,
+ .Stop = DSPlayerStop,
+ .Finish = DSPlayerFinish,
+ .IsPlaying = DSPlayerIsPlaying,
+ .IsPaused = DSPlayerIsPaused,
+ .IsStopped = DSPlayerIsStopped,
+ .IsFinished = DSPlayerIsFinished
+};
diff --git a/src/player/backends/media_foundation.cpp b/src/player/backends/media_foundation.cpp
new file mode 100644
index 0000000..6aa0d00
--- /dev/null
+++ b/src/player/backends/media_foundation.cpp
@@ -0,0 +1,1110 @@
+/*
+Copyright (c) 2015
+Michał Cichoń <thedmd@interia.pl>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+
+/* receive/play audio stream */
+
+/* based on DShow example player */
+
+extern "C" {
+# include "config.h"
+# include "../player2_private.h"
+}
+#undef restrict
+#undef inline
+# include "media_foundation.h"
+# include <mferror.h>
+# include <cassert>
+# include <cmath>
+
+//# pragma comment(lib, "mf.lib")
+//# pragma comment(lib, "mfplat.lib")
+# pragma comment(lib, "mfuuid.lib")
+
+// MFPlat.dll
+typedef HRESULT (__stdcall *MFSTARTUPPROC)(ULONG Version, DWORD dwFlags/* = MFSTARTUP_FULL*/);
+typedef HRESULT (__stdcall *MFSHUTDOWNPROC)();
+typedef HRESULT (__stdcall *MFCREATESOURCERESOLVERPROC)(IMFSourceResolver** ppISourceResolver);
+
+// MF.dll
+typedef HRESULT (__stdcall *MFGETSERVICEPROC)(IUnknown* punkObject, REFGUID guidService, REFIID riid, LPVOID* ppvObject);
+typedef HRESULT (__stdcall *MFCREATETOPOLOGYPROC)(IMFTopology** ppTopo);
+typedef HRESULT (__stdcall *MFCREATETOPOLOGYNODEPROC)(MF_TOPOLOGY_TYPE NodeType, IMFTopologyNode** ppNode);
+typedef HRESULT (__stdcall *MFCREATEAUDIORENDERERACTIVATEPROC)(IMFActivate** ppActivate);
+typedef HRESULT (__stdcall *MFCREATEMEDIASESSIONPROC)(IMFAttributes* pConfiguration, IMFMediaSession** ppMediaSession);
+
+struct MF
+{
+ bool Loaded;
+ bool Tried;
+ HMODULE MFModule;
+ HMODULE MFPlatModule;
+
+ // MFPlat.dll
+ MFSTARTUPPROC MFStartup;
+ MFSHUTDOWNPROC MFShutdown;
+ MFCREATESOURCERESOLVERPROC MFCreateSourceResolver;
+
+ // MF.dll
+ MFGETSERVICEPROC MFGetService;
+ MFCREATETOPOLOGYPROC MFCreateTopology;
+ MFCREATETOPOLOGYNODEPROC MFCreateTopologyNode;
+ MFCREATEAUDIORENDERERACTIVATEPROC MFCreateAudioRendererActivate;
+ MFCREATEMEDIASESSIONPROC MFCreateMediaSession;
+};
+static_assert(std::is_pod<MF>::value, "");
+
+static MF g_MF = { 0 };
+
+static void MFUnload()
+{
+ if (!g_MF.Tried)
+ return;
+
+ if (g_MF.MFModule)
+ {
+ FreeLibrary(g_MF.MFModule);
+ g_MF.MFModule = nullptr;
+ }
+
+ if (g_MF.MFPlatModule)
+ {
+ FreeLibrary(g_MF.MFPlatModule);
+ g_MF.MFPlatModule = nullptr;
+ }
+
+ g_MF.Loaded = false;
+}
+
+static bool MFLoad()
+{
+ if (g_MF.Tried)
+ return g_MF.Loaded;
+
+ g_MF.Tried = true;
+
+ g_MF.MFPlatModule = LoadLibrary(L"MFPlat.dll");
+ g_MF.MFModule = LoadLibrary(L"MF.dll");
+
+ if (!g_MF.MFPlatModule || !g_MF.MFModule)
+ {
+ MFUnload();
+ return false;
+ }
+
+ bool success = true;
+# define GET_PROC(module, type, name) success &= (g_MF.name = reinterpret_cast<type>(GetProcAddress(module, #name))) != nullptr
+
+ // MFPlat.dll
+ GET_PROC(g_MF.MFPlatModule, MFSTARTUPPROC, MFStartup);
+ GET_PROC(g_MF.MFPlatModule, MFSHUTDOWNPROC, MFShutdown);
+ GET_PROC(g_MF.MFPlatModule, MFCREATESOURCERESOLVERPROC, MFCreateSourceResolver);
+
+ // MF.dll
+ GET_PROC(g_MF.MFModule, MFGETSERVICEPROC, MFGetService);
+ GET_PROC(g_MF.MFModule, MFCREATETOPOLOGYPROC, MFCreateTopology);
+ GET_PROC(g_MF.MFModule, MFCREATETOPOLOGYNODEPROC, MFCreateTopologyNode);
+ GET_PROC(g_MF.MFModule, MFCREATEAUDIORENDERERACTIVATEPROC, MFCreateAudioRendererActivate);
+ GET_PROC(g_MF.MFModule, MFCREATEMEDIASESSIONPROC, MFCreateMediaSession);
+
+ if (!success)
+ {
+ MFUnload();
+ return false;
+ }
+
+ g_MF.Loaded = true;
+
+ atexit(MFUnload);
+
+ return true;
+}
+
+# define SAFE_CALL(expression) do { hr = expression; if (FAILED(hr)) return hr; } while (false)
+
+// Placement new is missing if we do not include <memory>
+//static inline void* operator new(size_t size, void* ptr)
+//{
+// return ptr;
+//}
+
+template <class Q>
+static HRESULT GetEventObject(IMFMediaEvent *mediaEvent, Q** outObject)
+{
+ *outObject = nullptr;
+
+ PROPVARIANT var;
+ HRESULT hr = mediaEvent->GetValue(&var);
+ if (SUCCEEDED(hr))
+ {
+ if (var.vt == VT_UNKNOWN)
+ hr = var.punkVal->QueryInterface(outObject);
+ else
+ hr = MF_E_INVALIDTYPE;
+
+ PropVariantClear(&var);
+ }
+ return hr;
+}
+
+//static HRESULT CreateMediaSource(PCWSTR pszURL, IMFMediaSource **ppSource);
+static HRESULT CreatePlaybackTopology(IMFMediaSource *pSource, IMFPresentationDescriptor *pPD, IMFTopology **ppTopology);
+static float MFTIMEToSeconds(MFTIME time)
+{
+ float seconds = (float)(time / 1e7);
+ return seconds;
+}
+
+static void DefaultMediaPlayerEventCallback(MediaPlayer* player, com_ptr<IMFMediaEvent> mediaEvent, MediaEventType eventType)
+{
+ player->HandleEvent(mediaEvent);
+}
+
+HRESULT MediaPlayer::Create(MediaPlayerEventCallback eventCallback, MediaPlayer** outMediaPlayer)
+{
+ if (!outMediaPlayer)
+ return E_POINTER;
+
+ if (!eventCallback)
+ eventCallback = DefaultMediaPlayerEventCallback;
+
+ // auto mediaPlayer = new (std::nothrow) MediaPlayer(eventCallback);
+
+ // Cheep object construction without throwing exceptions around.
+ auto mediaPlayer = reinterpret_cast<MediaPlayer*>(malloc(sizeof(MediaPlayer)));
+ if (!mediaPlayer)
+ return E_OUTOFMEMORY;
+
+ new (mediaPlayer) MediaPlayer(eventCallback);
+
+ auto hr = mediaPlayer->Initialize();
+ if (SUCCEEDED(hr))
+ *outMediaPlayer = mediaPlayer;
+ else
+ mediaPlayer->Release();
+
+ return hr;
+}
+
+MediaPlayer::MediaPlayer(MediaPlayerEventCallback eventCallback):
+ m_RefCount(1),
+ m_SourceResolver(nullptr),
+ m_CancelCookie(nullptr),
+ m_MediaSession(nullptr),
+ m_MediaSource(nullptr),
+ m_SimpleAudioVolume(nullptr),
+ m_PresentationClock(nullptr),
+ m_StreamAudioVolume(nullptr),
+ m_SetMasterVolume(),
+ m_MasterVolume(1.0f),
+ m_ReplayGain(0.0f),
+ m_EventCallback(eventCallback),
+ m_UserData(nullptr),
+ m_State(Closed),
+ m_CloseEvent(nullptr)
+{
+}
+
+MediaPlayer::~MediaPlayer()
+{
+ // If FALSE, the app did not call Shutdown().
+ assert(m_MediaSession == nullptr);
+
+ // When MediaPlayer calls IMediaEventGenerator::BeginGetEvent on the
+ // media session, it causes the media session to hold a reference
+ // count on the MediaPlayer.
+
+ // This creates a circular reference count between MediaPlayer and the
+ // media session. Calling Shutdown breaks the circular reference
+ // count.
+
+ // If CreateInstance fails, the application will not call
+ // Shutdown. To handle that case, call Shutdown in the destructor.
+ Shutdown();
+}
+
+
+ULONG STDMETHODCALLTYPE MediaPlayer::AddRef()
+{
+ return InterlockedIncrement(&m_RefCount);
+}
+
+ULONG STDMETHODCALLTYPE MediaPlayer::Release()
+{
+ auto count = InterlockedDecrement(&m_RefCount);
+ if (count == 0)
+ {
+ // Do manually deleter job, since malloc was used to allocate memory
+ this->~MediaPlayer();
+ free(this);
+
+ // delete this;
+ }
+ return count;
+}
+
+HRESULT STDMETHODCALLTYPE MediaPlayer::QueryInterface(REFIID iid, void** object)
+{
+ if (!object)
+ return E_POINTER;
+
+ *object = nullptr;
+ if (iid == IID_IUnknown)
+ *object = static_cast<IUnknown*>(this);
+ else if (iid == IID_IMFAsyncCallback)
+ *object = static_cast<IMFAsyncCallback*>(this);
+ else
+ return E_NOINTERFACE;
+
+ AddRef();
+
+ return S_OK;
+}
+
+HRESULT MediaPlayer::Initialize()
+{
+ auto hr = g_MF.MFStartup(MF_VERSION, MFSTARTUP_FULL);
+ if (FAILED(hr))
+ return hr;
+
+ m_CloseEvent = CreateEvent(nullptr, false, false, nullptr);
+ if (!m_CloseEvent)
+ {
+ hr = HRESULT_FROM_WIN32(GetLastError());
+ return hr;
+ }
+
+ return hr;
+}
+
+HRESULT MediaPlayer::Shutdown()
+{
+ auto hr = CloseSession();
+
+ g_MF.MFShutdown();
+
+ if (m_CloseEvent)
+ {
+ CloseHandle(m_CloseEvent);
+ m_CloseEvent = nullptr;
+ }
+
+ return hr;
+}
+
+HRESULT MediaPlayer::CreateSession()
+{
+ HRESULT hr = S_OK;
+
+ SAFE_CALL(CloseSession());
+ assert(m_State == Closed);
+ SAFE_CALL(g_MF.MFCreateMediaSession(nullptr, &m_MediaSession));
+ SAFE_CALL(m_MediaSession->BeginGetEvent(this, nullptr));
+
+ return hr;
+}
+
+HRESULT MediaPlayer::CloseSession()
+{
+ // The IMFMediaSession::Close method is asynchronous, but the
+ // CPlayer::CloseSession method waits on the MESessionClosed mediaEvent.
+ //
+ // MESessionClosed is guaranteed to be the last mediaEvent that the
+ // media session fires.
+
+ HRESULT hr = S_OK;
+
+ if (m_SourceResolver)
+ {
+ if (m_CancelCookie)
+ {
+ SAFE_CALL(m_SourceResolver->CancelObjectCreation(m_CancelCookie));
+ m_CancelCookie = nullptr;
+ }
+
+ m_SourceResolver = nullptr;
+ }
+
+ if (m_MediaSession)
+ {
+ SAFE_CALL(SetState(Closing));
+
+ hr = m_MediaSession->Close();
+ if (SUCCEEDED(hr))
+ {
+ auto waitResult = WaitForSingleObject(m_CloseEvent, 5000);
+ assert(waitResult != WAIT_TIMEOUT);
+ }
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ // Shut down the media source. (Synchronous operation, no events.)
+ if (m_MediaSource)
+ m_MediaSource->Shutdown();
+
+ // Shut down the media session. (Synchronous operation, no events.)
+ if (m_MediaSession)
+ m_MediaSession->Shutdown();
+ }
+
+ m_MediaSource = nullptr;
+ m_MediaSession = nullptr;
+
+ SAFE_CALL(SetState(Closed));
+
+ return hr;
+}
+
+HRESULT MediaPlayer::OpenURL(const wchar_t* url)
+{
+ // 1. Create a new media session.
+ // 2. Create the media source.
+ // 3. Create the topology.
+ // 4. Queue the topology [asynchronous]
+ // 5. Start playback [asynchronous - does not happen in this method.]
+
+ auto doOpenAsync = [this](const wchar_t* url)
+ {
+ HRESULT hr = S_OK;
+
+ SAFE_CALL(CreateSession());
+
+ SAFE_CALL(g_MF.MFCreateSourceResolver(&m_SourceResolver));
+
+ SAFE_CALL(m_SourceResolver->BeginCreateObjectFromURL(
+ url, // URL of the source.
+ MF_RESOLUTION_MEDIASOURCE, // Create a source object.
+ NULL, // Optional property store.
+ &m_CancelCookie, // Receives cookie for cancelation.
+ this, // Callback for recieving events.
+ nullptr)); // User defined state.
+
+ return hr;
+ };
+
+ auto hr = doOpenAsync(url);
+ if (SUCCEEDED(hr))
+ SAFE_CALL(SetState(OpenPending));
+ else
+ SAFE_CALL(SetState(Closed));
+
+ return hr;
+}
+
+HRESULT MediaPlayer::ApplyTopology()
+{
+ HRESULT hr = S_OK;
+
+ com_ptr<IMFTopology> topology;
+ com_ptr<IMFPresentationDescriptor> presentationDescriptor;
+
+ SAFE_CALL(m_MediaSource->CreatePresentationDescriptor(&presentationDescriptor));
+ SAFE_CALL(CreatePlaybackTopology(m_MediaSource, presentationDescriptor, &topology));
+ SAFE_CALL(m_MediaSession->SetTopology(0, topology));
+
+ return hr;
+}
+
+HRESULT MediaPlayer::StartPlayback()
+{
+ assert(m_MediaSession);
+
+ PROPVARIANT start;
+ PropVariantInit(&start);
+
+ if (m_State == Stopped)
+ {
+ // After stop, always play from beginning
+ start.vt = VT_I8;
+ start.uhVal.QuadPart = 0;
+ }
+
+ auto hr = m_MediaSession->Start(nullptr, &start);
+ if (SUCCEEDED(hr))
+ {
+ // Note: Start is an asynchronous operation. However, we
+ // can treat our state as being already started. If Start
+ // fails later, we'll get an MESessionStarted mediaEvent with
+ // an error code, and we will update our state then.
+ SAFE_CALL(SetState(Started));
+ }
+ PropVariantClear(&start);
+
+ return hr;
+}
+
+HRESULT MediaPlayer::Play()
+{
+ if (m_State != Paused && m_State != Stopped)
+ return MF_E_INVALIDREQUEST;
+
+ if (!m_MediaSession || !m_MediaSource)
+ return E_UNEXPECTED;
+
+ return StartPlayback();
+}
+
+HRESULT MediaPlayer::Pause()
+{
+ if (m_State != Started)
+ return MF_E_INVALIDREQUEST;
+
+ if (!m_MediaSession || !m_MediaSource)
+ return E_UNEXPECTED;
+
+ auto hr = m_MediaSession->Pause();
+ if (SUCCEEDED(hr))
+ SAFE_CALL(SetState(Paused));
+
+ return hr;
+}
+
+HRESULT MediaPlayer::Stop()
+{
+ if (m_State != Started && m_State != Paused)
+ return MF_E_INVALIDREQUEST;
+
+ if (!m_MediaSession)
+ return E_UNEXPECTED;
+
+ auto hr = m_MediaSession->Stop();
+ if (SUCCEEDED(hr))
+ SAFE_CALL(SetState(Stopped));
+
+ return hr;
+}
+
+HRESULT MediaPlayer::SetState(State state)
+{
+ if (m_State == state)
+ return S_OK;
+
+ auto previousState = m_State;
+
+ m_State = state;
+
+ HRESULT hr = S_OK;
+ SAFE_CALL(OnStateChange(m_State, previousState));
+ return hr;
+}
+
+MediaPlayer::State MediaPlayer::GetState() const
+{
+ return m_State;
+}
+
+void MediaPlayer::SetMasterVolume(float volume)
+{
+ m_MasterVolume = min(1.0f, max(0.0f, volume));
+ if (FAILED(ApplyMasterVolume(volume)))
+ m_SetMasterVolume = volume;
+}
+
+HRESULT MediaPlayer::ApplyMasterVolume(float volume)
+{
+ if (m_SimpleAudioVolume)
+ return m_SimpleAudioVolume->SetMasterVolume(volume);
+ else
+ return E_FAIL;
+}
+
+float MediaPlayer::GetMasterVolume() const
+{
+ if (m_SimpleAudioVolume)
+ {
+ float masterVolume = 1.0f;
+ if (SUCCEEDED(m_SimpleAudioVolume->GetMasterVolume(&masterVolume)))
+ m_MasterVolume = masterVolume;
+ }
+
+ return m_MasterVolume;
+}
+
+
+void MediaPlayer::SetReplayGain(float replayGain)
+{
+ m_ReplayGain = replayGain;
+ ApplyReplayGain(replayGain);
+}
+
+HRESULT MediaPlayer::ApplyReplayGain(float replayGain)
+{
+ if (m_StreamAudioVolume)
+ {
+ HRESULT hr = S_OK;
+
+ // Attenuation (dB) = 20 * log10(Level)
+ const float attenuation = powf(10.0f, replayGain / 20.0f);
+ const float volume = max(0.0f, min(1.0f, attenuation));
+
+ // This is poor-man method since positive gain is always clipped.
+ // To fully implement replay gain use custom MFT to process
+ // audio data.
+ UINT32 channelCount = 0;
+ SAFE_CALL(m_StreamAudioVolume->GetChannelCount(&channelCount));
+
+ for (UINT32 i = 0; i < channelCount; ++i)
+ hr = m_StreamAudioVolume->SetChannelVolume(i, volume);
+
+ return hr;
+ }
+ else
+ return E_FAIL;
+}
+
+float MediaPlayer::GetReplayGain() const
+{
+ return m_ReplayGain;
+}
+
+optional<float> MediaPlayer::GetPresentationTime() const
+{
+ if (m_PresentationClock)
+ {
+ MFTIME presentationTime = 0;
+ if (SUCCEEDED(m_PresentationClock->GetTime(&presentationTime)))
+ return MFTIMEToSeconds(presentationTime);
+ }
+
+ return nullopt;
+}
+
+optional<float> MediaPlayer::GetDuration() const
+{
+ if (m_MediaSource)
+ {
+ com_ptr<IMFPresentationDescriptor> presentationDescriptor;
+
+ if (SUCCEEDED(m_MediaSource->CreatePresentationDescriptor(&presentationDescriptor)))
+ {
+ MFTIME durationTime = 0;
+ if (SUCCEEDED(presentationDescriptor->GetUINT64(MF_PD_DURATION, reinterpret_cast<UINT64*>(&durationTime))))
+ return MFTIMEToSeconds(durationTime);
+ }
+ }
+
+ return nullopt;
+}
+
+void MediaPlayer::SetUserData(void* userData)
+{
+ m_UserData = userData;
+}
+
+void* MediaPlayer::GetUserData() const
+{
+ return m_UserData;
+}
+
+
+HRESULT MediaPlayer::HandleEvent(IMFMediaEvent* mediaEvent)
+{
+ if (!mediaEvent)
+ return E_POINTER;
+
+ HRESULT hr = S_OK;
+ HRESULT hrStatus = S_OK;
+
+ MediaEventType eventType = MEUnknown;
+ SAFE_CALL(mediaEvent->GetType(&eventType));
+ SAFE_CALL(mediaEvent->GetStatus(&hrStatus));
+ SAFE_CALL(hrStatus);
+
+ switch (eventType)
+ {
+ case MESessionTopologyStatus: SAFE_CALL(OnTopologyStatus(mediaEvent)); break;
+ case MEEndOfPresentation: SAFE_CALL(OnPresentationEnded(mediaEvent)); break;
+ case MENewPresentation: SAFE_CALL(OnNewPresentation(mediaEvent)); break;
+ default: break;
+ }
+
+ SAFE_CALL(OnSessionEvent(mediaEvent, eventType));
+
+ return hr;
+}
+
+HRESULT MediaPlayer::OnTopologyStatus(IMFMediaEvent* mediaEvent)
+{
+ UINT32 status = 0;
+ HRESULT hr = S_OK;
+
+ SAFE_CALL(mediaEvent->GetUINT32(MF_EVENT_TOPOLOGY_STATUS, &status));
+ if (status == MF_TOPOSTATUS_READY)
+ SAFE_CALL(StartPlayback());
+
+ return hr;
+}
+
+HRESULT MediaPlayer::OnPresentationEnded(IMFMediaEvent* mediaEvent)
+{
+ HRESULT hr = S_OK;
+ SAFE_CALL(SetState(Stopped));
+ return hr;
+}
+
+HRESULT MediaPlayer::OnNewPresentation(IMFMediaEvent* mediaEvent)
+{
+ HRESULT hr = S_OK;
+
+ com_ptr<IMFTopology> topology;
+ com_ptr<IMFPresentationDescriptor> presentationDescriptor;
+
+ SAFE_CALL(GetEventObject(mediaEvent, &presentationDescriptor));
+ SAFE_CALL(CreatePlaybackTopology(m_MediaSource, presentationDescriptor, &topology));
+ SAFE_CALL(m_MediaSession->SetTopology(0, topology));
+
+ SAFE_CALL(SetState(OpenPending));
+
+ return S_OK;
+}
+
+HRESULT MediaPlayer::OnSessionEvent(IMFMediaEvent*, MediaEventType)
+{
+ return S_OK;
+}
+
+HRESULT MediaPlayer::OnStateChange(State state, State previousState)
+{
+ HRESULT hr = S_OK;
+
+ if (state == Started && previousState != Paused)
+ {
+ m_SimpleAudioVolume = nullptr;
+ m_PresentationClock = nullptr;
+
+ com_ptr<IMFClock> clock;
+ SAFE_CALL(m_MediaSession->GetClock(&clock));
+ SAFE_CALL(clock->QueryInterface(&m_PresentationClock));
+
+ SAFE_CALL(g_MF.MFGetService(m_MediaSession, MR_POLICY_VOLUME_SERVICE, IID_IMFSimpleAudioVolume, (void**)&m_SimpleAudioVolume));
+
+ // Set master volume to previously set value
+ if (m_SetMasterVolume)
+ {
+ SAFE_CALL(ApplyMasterVolume(*m_SetMasterVolume));
+ m_SetMasterVolume = nullopt;
+ }
+
+ g_MF.MFGetService(m_MediaSession, MR_STREAM_VOLUME_SERVICE, IID_IMFAudioStreamVolume, (void**)&m_StreamAudioVolume);
+ ApplyReplayGain(m_ReplayGain);
+ }
+
+ if (state == Stopped)
+ {
+ m_StreamAudioVolume = nullptr;
+ m_SimpleAudioVolume = nullptr;
+ m_PresentationClock = nullptr;
+ }
+
+ return hr;
+}
+
+HRESULT STDMETHODCALLTYPE MediaPlayer::GetParameters(DWORD* flags, DWORD* queue)
+{
+ return E_NOTIMPL; // optional, not implemented
+}
+
+HRESULT STDMETHODCALLTYPE MediaPlayer::Invoke(IMFAsyncResult* asyncResult)
+{
+ HRESULT hr = S_OK;
+
+ if (m_SourceResolver && m_State == OpenPending)
+ {
+ auto doFinishOpen = [this](IMFAsyncResult* asyncResult)
+ {
+ HRESULT hr = S_OK;
+
+ // We are in async OpenURL
+ MF_OBJECT_TYPE objectType = MF_OBJECT_INVALID;
+ com_ptr<IUnknown> source;
+
+ SAFE_CALL(m_SourceResolver->EndCreateObjectFromURL(asyncResult, &objectType, &source));
+ SAFE_CALL(source->QueryInterface(&m_MediaSource));
+
+ return hr;
+ };
+
+ hr = doFinishOpen(asyncResult);
+
+ m_CancelCookie = nullptr;
+ m_SourceResolver = nullptr;
+
+ if (SUCCEEDED(hr))
+ hr = ApplyTopology();
+
+ if (FAILED(hr))
+ CloseSession();
+
+ return hr;
+ }
+
+ MediaEventType eventType = MEUnknown;
+ com_ptr<IMFMediaEvent> mediaEvent;
+ SAFE_CALL(m_MediaSession->EndGetEvent(asyncResult, &mediaEvent));
+ SAFE_CALL(mediaEvent->GetType(&eventType));
+
+ if (eventType == MESessionClosed)
+ // The session was closed.
+ // The application is waiting on the m_CloseEvent mediaEvent handle.
+ SetEvent(m_CloseEvent);
+ else
+ // For all other events, get the next mediaEvent in the queue.
+ SAFE_CALL(m_MediaSession->BeginGetEvent(this, nullptr));
+
+ // Check the application state.
+
+ // If a call to IMFMediaSession::Close is pending, it means the
+ // application is waiting on the m_CloseEvent mediaEvent and
+ // the application's message loop is blocked.
+
+ // Otherwise, post a private window message to the application.
+ if (m_State != Closing)
+ {
+ // Leave a reference count on the mediaEvent.
+ m_EventCallback(this, std::move(mediaEvent), eventType);
+ }
+
+ return S_OK;
+}
+
+// Create a media source from a URL.
+//static HRESULT CreateMediaSource(const wchar_t* url, IMFMediaSource** mediaSource)
+//{
+// HRESULT hr = S_OK;
+//
+// MF_OBJECT_TYPE objectType = MF_OBJECT_INVALID;
+//
+// com_ptr<IMFSourceResolver> sourceResolver;
+// com_ptr<IUnknown> source;
+//
+// // Create the source resolver.
+// SAFE_CALL(MFCreateSourceResolver(&sourceResolver));
+//
+// // Use the source resolver to create the media source.
+//
+// // Note: For simplicity this sample uses the synchronous method to create
+// // the media source. However, creating a media source can take a noticeable
+// // amount of time, especially for a network source. For a more responsive
+// // UI, use the asynchronous BeginCreateObjectFromURL method.
+// SAFE_CALL(sourceResolver->CreateObjectFromURL(
+// url, // URL of the source.
+// MF_RESOLUTION_MEDIASOURCE, // Create a source object.
+// NULL, // Optional property store.
+// &objectType, // Receives the created object type.
+// &source)); // Receives a pointer to the media source.
+//
+// // Get the IMFMediaSource interface from the media source.
+// SAFE_CALL(source->QueryInterface(IID_PPV_ARGS(mediaSource)));
+//
+// return hr;
+//}
+
+static HRESULT CreateMediaSinkActivate(IMFStreamDescriptor* streamDescriptor, IMFActivate** outActivate)
+{
+ HRESULT hr = S_OK;
+
+ com_ptr<IMFMediaTypeHandler> mediaTypeHandler;
+ com_ptr<IMFActivate> activate;
+
+ // Get the media type handler for the stream.
+ SAFE_CALL(streamDescriptor->GetMediaTypeHandler(&mediaTypeHandler));
+
+ // Get the major media type.
+ GUID majorType = GUID_NULL;
+ SAFE_CALL(mediaTypeHandler->GetMajorType(&majorType));
+
+ // Create an IMFActivate object for the renderer, based on the media type.
+ if (MFMediaType_Audio == majorType)
+ {
+ // Create the audio renderer.
+ SAFE_CALL(g_MF.MFCreateAudioRendererActivate(&activate));
+ }
+ else // Unknown stream type.
+ {
+ SAFE_CALL(E_FAIL);
+ // Optionally, you could deselect this stream instead of failing.
+ }
+
+ // Return IMFActivate pointer to caller.
+ *outActivate = activate.detach();
+
+ return hr;
+}
+
+// Add a source node to a topology.
+static HRESULT AddSourceNode(IMFTopology* topology, IMFMediaSource* mediaSource, IMFPresentationDescriptor* presentationDescriptor, IMFStreamDescriptor* streamDescriptor, IMFTopologyNode* *outTopologyNode)
+{
+ HRESULT hr = S_OK;
+
+ com_ptr<IMFTopologyNode> node;
+
+ // Create the node.
+ SAFE_CALL(g_MF.MFCreateTopologyNode(MF_TOPOLOGY_SOURCESTREAM_NODE, &node));
+ SAFE_CALL(node->SetUnknown(MF_TOPONODE_SOURCE, mediaSource));
+ SAFE_CALL(node->SetUnknown(MF_TOPONODE_PRESENTATION_DESCRIPTOR, presentationDescriptor));
+ SAFE_CALL(node->SetUnknown(MF_TOPONODE_STREAM_DESCRIPTOR, streamDescriptor));
+ SAFE_CALL(topology->AddNode(node));
+
+ *outTopologyNode = node.detach();
+
+ return hr;
+}
+
+// Add an output node to a topology.
+static HRESULT AddOutputNode(IMFTopology* topology, IMFActivate* activate, DWORD streamSinkId, IMFTopologyNode** outTopologyNode)
+{
+ HRESULT hr = S_OK;
+
+ com_ptr<IMFTopologyNode> node;
+
+ SAFE_CALL(g_MF.MFCreateTopologyNode(MF_TOPOLOGY_OUTPUT_NODE, &node));
+ SAFE_CALL(node->SetObject(activate));
+ SAFE_CALL(node->SetUINT32(MF_TOPONODE_STREAMID, streamSinkId));
+ SAFE_CALL(node->SetUINT32(MF_TOPONODE_NOSHUTDOWN_ON_REMOVE, false));
+ SAFE_CALL(topology->AddNode(node));
+
+ *outTopologyNode = node.detach();
+
+ return hr;
+}
+
+// Add a topology branch for one stream.
+//
+// For each stream, this function does the following:
+//
+// 1. Creates a source node associated with the stream.
+// 2. Creates an output node for the renderer.
+// 3. Connects the two nodes.
+//
+// The media session will add any decoders that are needed.
+static HRESULT AddBranchToPartialTopology(IMFTopology* topology, IMFMediaSource* mediaSource, IMFPresentationDescriptor* presentationDescriptor, DWORD streamIndex)
+{
+ HRESULT hr = S_OK;
+
+ com_ptr<IMFStreamDescriptor> streamDescriptor;
+ com_ptr<IMFActivate> activate;
+ com_ptr<IMFTopologyNode> sourceNode;
+ com_ptr<IMFTopologyNode> outputNode;
+
+ BOOL isSelected = false;
+ SAFE_CALL(presentationDescriptor->GetStreamDescriptorByIndex(streamIndex, &isSelected, &streamDescriptor));
+ if (!isSelected)
+ return hr;
+
+ // Create the media sink activation object.
+ SAFE_CALL(CreateMediaSinkActivate(streamDescriptor, &activate));
+ SAFE_CALL(AddSourceNode(topology, mediaSource, presentationDescriptor, streamDescriptor, &sourceNode));
+ SAFE_CALL(AddOutputNode(topology, activate, 0, &outputNode));
+ SAFE_CALL(sourceNode->ConnectOutput(0, outputNode, 0));
+
+ return hr;
+}
+
+// Create a playback topology from a media source.
+static HRESULT CreatePlaybackTopology(IMFMediaSource* mediaSourcee, IMFPresentationDescriptor* presentationDescriptor, IMFTopology** outTopology)
+{
+ HRESULT hr = S_OK;
+
+ com_ptr<IMFTopology> topology;
+
+ // Create a new topology.
+ SAFE_CALL(g_MF.MFCreateTopology(&topology));
+
+ // Get the number of streams in the media source.
+ DWORD streamDescriptorCount = 0;
+ SAFE_CALL(presentationDescriptor->GetStreamDescriptorCount(&streamDescriptorCount));
+
+ // For each stream, create the topology nodes and add them to the topology.
+ for (DWORD i = 0; i < streamDescriptorCount; ++i)
+ SAFE_CALL(AddBranchToPartialTopology(topology, mediaSourcee, presentationDescriptor, i));
+
+ *outTopology = topology.detach();
+
+ return hr;
+}
+
+# undef SAFE_CALL
+
+struct _player_t
+{
+ com_ptr<MediaPlayer> player;
+};
+
+extern "C" player2_t WMFPlayerCreate()
+{
+ if (!MFLoad())
+ return nullptr;
+
+ com_ptr<MediaPlayer> player;
+ auto hr = MediaPlayer::Create(nullptr, &player);
+ if (FAILED(hr))
+ return nullptr;
+
+ auto out = new _player_t();
+ out->player = player;
+ return out;
+}
+
+extern "C" void WMFPlayerDestroy(player2_t player)
+{
+ if (player)
+ delete player;
+}
+
+extern "C" void WMFPlayerSetVolume(player2_t player, float volume)
+{
+ const float attenuation = powf(10.0f, volume / 20.0f);
+ const float linearVolume = max(0.0f, min(1.0f, attenuation));
+ player->player->SetMasterVolume(linearVolume);
+}
+
+extern "C" float WMFPlayerGetVolume(player2_t player)
+{
+ const float linearVolume = player->player->GetMasterVolume();
+ const float volume = linearVolume > 0.0f ? 20.0f * log10f(linearVolume) : 0.0f;
+ return volume;
+}
+
+extern "C" void WMFPlayerSetGain(player2_t player, float gainDb)
+{
+ player->player->SetReplayGain(gainDb);
+}
+
+extern "C" float WMFPlayerGetGain(player2_t player)
+{
+ return player->player->GetReplayGain();
+}
+
+extern "C" double WMFPlayerGetDuration(player2_t player)
+{
+ if (auto duration = player->player->GetDuration())
+ return *duration;
+ else
+ return 0.0f;
+}
+
+extern "C" double WMFPlayerGetTime(player2_t player)
+{
+ if (auto duration = player->player->GetPresentationTime())
+ return *duration;
+ else
+ return 0.0f;
+}
+
+extern "C" bool WMFPlayerOpen(player2_t player, const char* url)
+{
+ auto urlLength = strlen(url);
+ if (urlLength == 0)
+ return false;
+
+ int bufferLength = ::MultiByteToWideChar(CP_UTF8, 0, url, urlLength, nullptr, 0);
+ if (bufferLength == 0)
+ return false;
+
+ wchar_t* buffer = new wchar_t[bufferLength + 1];
+ int result = ::MultiByteToWideChar(CP_UTF8, 0, url, urlLength, buffer, bufferLength);
+ if (result == 0)
+ {
+ delete [] buffer;
+ return false;
+ }
+
+ buffer[bufferLength] = 0;
+
+ auto hr = player->player->OpenURL(buffer);
+
+ delete[] buffer;
+
+ while (player->player->GetState() == MediaPlayer::OpenPending)
+ Sleep(1);
+
+ return SUCCEEDED(hr);
+}
+
+extern "C" bool WMFPlayerPlay(player2_t player)
+{
+ return SUCCEEDED(player->player->Play());
+}
+
+extern "C" bool WMFPlayerPause(player2_t player)
+{
+ return SUCCEEDED(player->player->Pause());
+}
+
+extern "C" bool WMFPlayerStop(player2_t player)
+{
+ return SUCCEEDED(player->player->Stop());
+}
+
+extern "C" bool WMFPlayerFinish(player2_t player)
+{
+ return SUCCEEDED(player->player->Stop());
+}
+
+extern "C" bool WMFPlayerIsPlaying(player2_t player)
+{
+ auto state = player->player->GetState();
+ return state == MediaPlayer::Started;
+}
+
+extern "C" bool WMFPlayerIsPaused(player2_t player)
+{
+ auto state = player->player->GetState();
+ return state == MediaPlayer::Paused;
+}
+
+extern "C" bool WMFPlayerIsStopped(player2_t player)
+{
+ auto state = player->player->GetState();
+ return state == MediaPlayer::Stopped || state == MediaPlayer::Closing || state == MediaPlayer::Closed;
+}
+
+extern "C" bool WMFPlayerIsFinished(player2_t player)
+{
+ return WMFPlayerIsStopped(player);
+ //auto state = player->player->GetState();
+ //return state == MediaPlayer::Closing || state == MediaPlayer::Closed;
+}
+
+extern "C" player2_iface player2_windows_media_foundation =
+{
+ /*.Id =*/ "mf",
+ /*.Name =*/ "Windows Media Foundation",
+ /*.Create =*/ WMFPlayerCreate,
+ /*.Destroy =*/ WMFPlayerDestroy,
+ /*.SetVolume =*/ WMFPlayerSetVolume,
+ /*.GetVolume =*/ WMFPlayerGetVolume,
+ /*.SetGain =*/ WMFPlayerSetGain,
+ /*.GetGain =*/ WMFPlayerGetGain,
+ /*.GetDuration =*/ WMFPlayerGetDuration,
+ /*.GetTime =*/ WMFPlayerGetTime,
+ /*.Open =*/ WMFPlayerOpen,
+ /*.Play =*/ WMFPlayerPlay,
+ /*.Pause =*/ WMFPlayerPause,
+ /*.Stop =*/ WMFPlayerStop,
+ /*.Finish =*/ WMFPlayerFinish,
+ /*.IsPlaying =*/ WMFPlayerIsPlaying,
+ /*.IsPaused =*/ WMFPlayerIsPaused,
+ /*.IsStopped =*/ WMFPlayerIsStopped,
+ /*.IsFinished =*/ WMFPlayerIsFinished
+};
diff --git a/src/player/backends/media_foundation.h b/src/player/backends/media_foundation.h
new file mode 100644
index 0000000..cf10122
--- /dev/null
+++ b/src/player/backends/media_foundation.h
@@ -0,0 +1,115 @@
+# ifndef __TD__BASIC_MEDIA_PLAYER_H__
+# define __TD__BASIC_MEDIA_PLAYER_H__
+# pragma once
+
+# include <mfapi.h>
+# include <mfidl.h>
+# include "utility/com_ptr.h"
+# include "utility/optional.h"
+
+class MediaPlayer;
+
+typedef void (*MediaPlayerEventCallback)(MediaPlayer* player, com_ptr<IMFMediaEvent>, MediaEventType);
+
+class MediaPlayer:
+ private IMFAsyncCallback
+{
+public:
+ enum State
+ {
+ Closed = 0, // no session
+ Ready, // session was created, ready to open file
+ OpenPending, // session is opening a file
+ Started, // session is playing a file
+ Paused, // session is paused
+ Stopped, // session is stopped (but ready to play)
+ Closing // session was closed, waiting for async callback
+ };
+
+ static HRESULT Create(MediaPlayerEventCallback eventCallback, MediaPlayer** outMediaPlayer);
+
+ // IUnknown
+ virtual ULONG STDMETHODCALLTYPE AddRef() override;
+ virtual ULONG STDMETHODCALLTYPE Release() override;
+ virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void** object) override;
+
+ HRESULT OpenURL(const wchar_t* url);
+ HRESULT Play();
+ HRESULT Pause();
+ HRESULT Stop();
+ HRESULT Shutdown();
+
+ State GetState() const;
+
+ void SetMasterVolume(float volume);
+ float GetMasterVolume() const;
+
+ void SetReplayGain(float replayGain);
+ float GetReplayGain() const;
+
+ optional<float> GetPresentationTime() const;
+ optional<float> GetDuration() const;
+
+ void SetUserData(void* userData);
+ void* GetUserData() const;
+
+ HRESULT HandleEvent(IMFMediaEvent* mediaEvent);
+
+protected:
+ MediaPlayer(MediaPlayerEventCallback eventCallback);
+ virtual ~MediaPlayer();
+
+ HRESULT Initialize();
+ HRESULT CreateSession();
+ HRESULT CloseSession();
+ HRESULT StartPlayback();
+ HRESULT ApplyTopology();
+
+ HRESULT ApplyMasterVolume(float volume);
+ HRESULT ApplyReplayGain(float replayGain);
+
+ const com_ptr<IMFMediaSession>& GetMediaSession() const { return m_MediaSession; }
+ const com_ptr<IMFMediaSource>& GetMediaSource() const { return m_MediaSource; }
+
+ virtual HRESULT OnTopologyStatus(IMFMediaEvent* mediaEvent);
+ virtual HRESULT OnPresentationEnded(IMFMediaEvent* mediaEvent);
+ virtual HRESULT OnNewPresentation(IMFMediaEvent* mediaEvent);
+
+ // Override to handle additional session events.
+ virtual HRESULT OnSessionEvent(IMFMediaEvent* mediaEvent, MediaEventType mediaEventType);
+
+ virtual HRESULT OnStateChange(State state, State previousState);
+
+private:
+ // IMFAsyncCallback
+ virtual HRESULT STDMETHODCALLTYPE GetParameters(DWORD* flags, DWORD* queue);
+ virtual HRESULT STDMETHODCALLTYPE Invoke(IMFAsyncResult* asyncResult);
+
+ HRESULT SetState(State state);
+
+ ULONG m_RefCount;
+
+ com_ptr<IMFSourceResolver> m_SourceResolver;
+ com_ptr<IUnknown> m_CancelCookie;
+
+ com_ptr<IMFMediaSession> m_MediaSession;
+ com_ptr<IMFMediaSource> m_MediaSource;
+
+ com_ptr<IMFSimpleAudioVolume> m_SimpleAudioVolume;
+ com_ptr<IMFPresentationClock> m_PresentationClock;
+ com_ptr<IMFAudioStreamVolume> m_StreamAudioVolume;
+
+ optional<float> m_SetMasterVolume;
+ mutable float m_MasterVolume;
+ mutable float m_ReplayGain;
+
+ MediaPlayerEventCallback m_EventCallback;
+
+ void* m_UserData;
+
+ State m_State;
+ HANDLE m_CloseEvent;
+};
+
+
+# endif // __TD__BASIC_MEDIA_PLAYER_H__ \ No newline at end of file
diff --git a/src/player/backends/utility/com_ptr.h b/src/player/backends/utility/com_ptr.h
new file mode 100644
index 0000000..23534b2
--- /dev/null
+++ b/src/player/backends/utility/com_ptr.h
@@ -0,0 +1,148 @@
+# ifndef __TD__COM_PTR_H__
+# define __TD__COM_PTR_H__
+# pragma once
+
+template <typename T>
+class com_ptr
+{
+//private:
+// typedef void (com_ptr::*bool_type)() const;
+// void safe_bool() const {}
+//
+//
+public:
+ com_ptr():
+ _ptr(nullptr)
+ {
+ }
+
+ com_ptr(T* ptr, bool add_ref = false):
+ _ptr(ptr)
+ {
+ if (_ptr && add_ref)
+ _ptr->AddRef();
+ }
+
+ com_ptr(const com_ptr& rhs):
+ _ptr(rhs._ptr)
+ {
+ if (_ptr)
+ _ptr->AddRef();
+ }
+
+ com_ptr(com_ptr&& rhs):
+ _ptr(rhs._ptr)
+ {
+ rhs._ptr = nullptr;
+ }
+
+ template <typename U>
+ com_ptr(const com_ptr<U>& rhs):
+ _ptr(rhs._ptr)
+ {
+ if (_ptr)
+ _ptr->AddRef();
+ }
+
+ ~com_ptr()
+ {
+ if (_ptr)
+ _ptr->Release();
+ }
+
+ com_ptr& operator = (const com_ptr& rhs)
+ {
+ com_ptr(rhs).swap(*this);
+ return *this;
+ }
+
+ com_ptr& operator = (com_ptr&& rhs)
+ {
+ com_ptr(static_cast<com_ptr&&>(rhs)).swap(*this);
+ return *this;
+ }
+
+ template <typename U>
+ com_ptr& operator = (const com_ptr<U>& rhs)
+ {
+ com_ptr(rhs).swap(*this):
+ return *this;
+ }
+
+ com_ptr& operator = (T* rhs)
+ {
+ com_ptr(rhs).swap(*this);
+ return *this;
+ }
+
+ //operator bool_type() const
+ //{
+ // return _ptr ? &com_ptr::safe_bool : nullptr;
+ //}
+
+ //bool_type operator !() const
+ //{
+ // return !((bool_type)*this);
+ //}
+
+ void reset()
+ {
+ com_ptr().swap(*this);
+ }
+
+ void reset(T* rhs)
+ {
+ com_ptr(rhs).swap(*this);
+ }
+
+ void reset(T* rhs, bool add_ref)
+ {
+ com_ptr(rhs, add_ref).swap(*this);
+ }
+
+ T* get() const
+ {
+ return _ptr;
+ }
+
+ T* detach()
+ {
+ auto result = _ptr;
+ _ptr = nullptr;
+ return result;
+ }
+
+ void swap(com_ptr& rhs)
+ {
+ T* temp = rhs._ptr;
+ rhs._ptr = _ptr;
+ _ptr = temp;
+ }
+
+ T& operator * () const { return *_ptr; }
+ T* operator -> () const { return _ptr; }
+
+ operator T* () const { return _ptr; }
+ T** operator & () { return &_ptr; }
+
+private:
+ T* _ptr;
+};
+
+template <typename T, typename U> inline bool operator==(const com_ptr<T>& a, const com_ptr<U>& b) { return a.get() == b.get(); }
+template <typename T, typename U> inline bool operator!=(const com_ptr<T>& a, const com_ptr<U>& b) { return a.get() != b.get(); }
+template <typename T, typename U> inline bool operator==(const com_ptr<T>& a, U* b) { return a.get() == b; }
+template <typename T, typename U> inline bool operator!=(const com_ptr<T>& a, U* b) { return a.get() != b; }
+template <typename T, typename U> inline bool operator==(T* a, const com_ptr<U>& b) { return a == b.get(); }
+template <typename T, typename U> inline bool operator!=(T* a, const com_ptr<U>& b) { return a != b.get(); }
+template <typename T> inline bool operator==(const com_ptr<T>& p, std::nullptr_t) { return p.get() == nullptr; }
+template <typename T> inline bool operator==(std::nullptr_t, const com_ptr<T>& p) { return p.get() == nullptr; }
+template <typename T> inline bool operator!=(const com_ptr<T>& p, std::nullptr_t) { return p.get() != nullptr; }
+template <typename T> inline bool operator!=(std::nullptr_t, const com_ptr<T>& p) { return p.get() != nullptr; }
+template <typename T> inline bool operator<(const com_ptr<T>& a, const com_ptr<T>& b) { return std::less<T*>()(a.get(), b.get()); }
+template <typename T> inline bool operator<=(const com_ptr<T>& a, const com_ptr<T>& b) { return std::less_equal<T*>()(a.get(), b.get()); }
+template <typename T> inline bool operator>(const com_ptr<T>& a, const com_ptr<T>& b) { return std::greater<T*>()(a.get(), b.get()); }
+template <typename T> inline bool operator>=(const com_ptr<T>& a, const com_ptr<T>& b) { return std::greater_equal<T*>()(a.get(), b.get()); }
+template <typename T> void swap(com_ptr<T> & lhs, com_ptr<T> & rhs) { lhs.swap(rhs); }
+
+# endif // __TD__COM_PTR_H__ \ No newline at end of file
diff --git a/src/player/backends/utility/optional.h b/src/player/backends/utility/optional.h
new file mode 100644
index 0000000..9ee0b71
--- /dev/null
+++ b/src/player/backends/utility/optional.h
@@ -0,0 +1,517 @@
+//------------------------------------------------------------------------------
+//
+//------------------------------------------------------------------------------
+# ifndef __TD__OPTIONAL_H__
+# define __TD__OPTIONAL_H__
+# pragma once
+
+# include <type_traits>
+# include <utility>
+# include <cassert>
+
+
+//------------------------------------------------------------------------------
+struct nullopt_t
+{
+ struct init {};
+ nullopt_t(init) {}
+};
+
+const nullopt_t nullopt((nullopt_t::init()));
+
+template <typename T>
+struct optional
+{
+private:
+ typedef typename std::aligned_storage<sizeof(T), std::alignment_of<T>::value>::type storage_type;
+
+ typedef void (optional::*bool_type)() const;
+ void safe_bool() const { }
+
+
+public:
+ optional();
+ optional(nullopt_t);
+ optional(const optional& v);
+ optional(optional&& v);
+ optional(const T& v);
+ optional(T&& v);
+ ~optional();
+
+ operator bool_type() const;
+
+ optional& operator=(nullopt_t);
+
+ optional& operator=(const optional& other);
+ optional& operator=(optional&& other);
+
+ // The function does not participate in overload resolution unless std::is_same<std::decay_t<U>, T>::value is true
+ template <typename U>
+ // optional& operator=(U&& value);
+ optional& operator=(typename std::enable_if<std::is_same<typename std::decay<U>::type, T>::value, U>::type&& value);
+
+ T* operator->();
+ const T* operator->() const;
+ T& operator*();
+ const T& operator*() const;
+
+ T& value();
+ const T& value() const;
+
+# if defined(_MSC_VER) && (_MSC_VER > 1600)
+ template <typename U>
+ T value_or(U&& default_value) const &;
+
+ template <typename U>
+ T value_or(U&& default_value) &&;
+# else
+ T value_or(const T& default_value);
+ T value_or(const T& default_value) const;
+# endif
+
+ bool has_value() const;
+
+ void swap(optional& other);
+
+private:
+ T* value_ptr() { return reinterpret_cast< T*>(&_value); }
+ const T* value_ptr() const { return reinterpret_cast<const T*>(&_value); }
+
+ T& value_ref() { return *value_ptr(); }
+ const T& value_ref() const { return *value_ptr(); }
+
+ bool _has_value;
+ storage_type _value;
+# if defined(_DEBUG)
+ const T& _preview;
+# endif
+};
+
+
+//------------------------------------------------------------------------------
+template <typename T>
+inline optional<T>::optional():
+ _has_value(false)
+# if defined(_DEBUG)
+ ,_preview(value_ref())
+# endif
+{
+}
+
+template <typename T>
+inline optional<T>::optional(nullopt_t):
+ _has_value(false)
+# if defined(_DEBUG)
+ , _preview(value_ref())
+# endif
+{
+}
+
+template <typename T>
+inline optional<T>::optional(const optional& v):
+ _has_value(v._has_value)
+# if defined(_DEBUG)
+ , _preview(value_ref())
+# endif
+{
+ if (_has_value)
+ new (value_ptr()) T(v.value_ref());
+}
+
+template <typename T>
+inline optional<T>::optional(optional&& v):
+ _has_value(v._has_value)
+# if defined(_DEBUG)
+ , _preview(value_ref())
+# endif
+{
+ if (!_has_value)
+ return;
+
+ new (value_ptr()) T(std::move(v.value_ref()));
+ v.value_ref().~T();
+
+ v._has_value = false;
+}
+
+template <typename T>
+inline optional<T>::optional(const T& v):
+ _has_value(true)
+# if defined(_DEBUG)
+ , _preview(value_ref())
+# endif
+{
+ new (value_ptr()) T(v);
+}
+
+template <typename T>
+inline optional<T>::optional(T&& v):
+ _has_value(true)
+# if defined(_DEBUG)
+ , _preview(value_ref())
+# endif
+{
+ new (value_ptr()) T(std::forward<T>(v));
+}
+
+template <typename T>
+inline optional<T>::~optional()
+{
+ if (_has_value)
+ value_ref().~T();
+}
+
+template <typename T>
+inline optional<T>::operator bool_type() const
+{
+ return _has_value ? &optional::safe_bool : nullptr;
+}
+
+template <typename T>
+inline optional<T>& optional<T>::operator= (nullopt_t)
+{
+ optional().swap(*this);
+ _has_value = false;
+ return *this;
+}
+
+template <typename T>
+inline optional<T>& optional<T>::operator=(const optional& other)
+{
+ optional(other).swap(*this);
+ return *this;
+}
+
+template <typename T>
+inline optional<T>& optional<T>::operator=(optional&& other)
+{
+ optional(std::forward<optional>(other)).swap(*this);
+ return *this;
+}
+
+template <typename T>
+template <typename U>
+inline optional<T>& optional<T>::operator=(typename std::enable_if<std::is_same<typename std::decay<U>::type, T>::value, U>::type&& value)
+{
+ optional<T>(value).swap(*this);
+ return *this;
+}
+
+
+template <typename T>
+inline T* optional<T>::operator->()
+{
+ assert(_has_value);
+ return value_ptr();
+}
+
+template <typename T>
+inline const T* optional<T>::operator->() const
+{
+ assert(_has_value);
+ return value_ptr();
+}
+
+template <typename T>
+inline T& optional<T>::operator*()
+{
+ assert(_has_value);
+ return value_ref();
+}
+
+template <typename T>
+inline const T& optional<T>::operator*() const
+{
+ assert(_has_value);
+ return value_ref();
+}
+
+template <typename T>
+inline T& optional<T>::value()
+{
+ assert(_has_value);
+ return value_ref();
+}
+
+template <typename T>
+inline const T& optional<T>::value() const
+{
+ assert(_has_value);
+ return value_ref();
+}
+
+# if defined(_MSC_VER) && (_MSC_VER > 1600)
+template <typename T>
+template <typename U>
+inline T optional<T>::value_or(U&& default_value) const &
+{
+ return bool(*this) ? value_ref() : static_cast<T>(std::forward<U>(default_value));
+}
+
+template <typename T>
+template <typename U>
+inline T optional<T>::value_or(U&& default_value) &&
+{
+ return bool(*this) ? std::move(value_ref()) : static_cast<T>(std::forward<U>(default_value));
+}
+# else
+template <typename T>
+T optional<T>::value_or(const T& default_value)
+{
+ return _has_value ? value_ref() : default_value;
+}
+
+template <typename T>
+T optional<T>::value_or(const T& default_value) const
+{
+ return _has_value ? value_ref() : default_value;
+}
+# endif
+
+template <typename T>
+inline bool optional<T>::has_value() const
+{
+ return _has_value;
+}
+
+template <typename T>
+inline void optional<T>::swap(optional& other)
+{
+ using std::swap;
+
+ if (_has_value && other._has_value)
+ {
+ swap(value_ref(), other.value_ref());
+ }
+ else if (_has_value && !other._has_value)
+ {
+ new (other.value_ptr()) T(std::forward<T>(value_ref()));
+
+ value_ref().~T();
+
+ _has_value = false;
+ other._has_value = true;
+ }
+ else if (!_has_value && other._has_value)
+ {
+ new (value_ptr()) T(std::forward<T>(other.value_ref()));
+
+ other.value_ref().~T();
+
+ _has_value = true;
+ other._has_value = false;
+ }
+}
+
+
+//------------------------------------------------------------------------------
+template <typename T>
+optional<T> make_optional(T&& v)
+{
+ return optional<T>(std::forward<T>(v));
+}
+
+template <typename T>
+inline void swap(optional<T>& lhs, optional<T>& rhs)
+{
+ lhs.swap(rhs);
+}
+
+template <typename T>
+inline bool operator==(const optional<T>& lhs, const optional<T>& rhs)
+{
+ if (static_cast<bool>(lhs) != static_cast<bool>(rhs))
+ return false;
+ if (!static_cast<bool>(lhs))
+ return true;
+ return *lhs == *rhs;
+}
+
+template <typename T>
+bool operator!=(const optional<T>& lhs, const optional<T>& rhs)
+{
+ return !(lhs == rhs);
+}
+
+template <typename T>
+inline bool operator<(const optional<T>& lhs, const optional<T>& rhs)
+{
+ if (!static_cast<bool>(rhs))
+ return false;
+ if (!static_cast<bool>(lhs))
+ return true;
+ return *lhs < *rhs;
+}
+
+template <typename T>
+inline bool operator>(const optional<T>& lhs, const optional<T>& rhs)
+{
+ return rhs < lhs;
+}
+
+template <typename T>
+inline bool operator<=(const optional<T>& lhs, const optional<T>& rhs)
+{
+ return !(rhs < lhs);
+}
+
+template <typename T>
+inline bool operator>=(const optional<T>& lhs, const optional<T>& rhs)
+{
+ return !(lhs < rhs);
+}
+
+template <typename T>
+inline bool operator==(const optional<T>& opt, nullopt_t)
+{
+ return !static_cast<bool>(opt);
+}
+
+template <typename T>
+inline bool operator==(nullopt_t, const optional<T>& opt)
+{
+ return static_cast<bool>(opt);
+}
+
+template <typename T>
+inline bool operator!=(const optional<T>& opt, nullopt_t)
+{
+ return static_cast<bool>(opt);
+}
+
+template <typename T>
+inline bool operator!=(nullopt_t, const optional<T>& opt)
+{
+ return !static_cast<bool>(opt);
+}
+
+template <typename T>
+inline bool operator<(const optional<T>& opt, nullopt_t)
+{
+ return false;
+}
+
+template <typename T>
+inline bool operator<(nullopt_t, const optional<T>& opt)
+{
+ return static_cast<bool>(opt);
+}
+
+template <typename T>
+inline bool operator<=(const optional<T>& opt, nullopt_t)
+{
+ return !opt;
+}
+
+template <typename T>
+inline bool operator<=(nullopt_t, const optional<T>& opt)
+{
+ return true;
+}
+
+template <typename T>
+inline bool operator>(const optional<T>& opt, nullopt_t)
+{
+ return static_cast<bool>(opt);
+}
+
+template <typename T>
+inline bool operator>(nullopt_t, const optional<T>& opt)
+{
+ return false;
+}
+
+template <typename T>
+inline bool operator>=(const optional<T>&, nullopt_t)
+{
+ return true;
+}
+
+template <typename T>
+inline bool operator>=(nullopt_t, const optional<T>& opt)
+{
+ return !opt;
+}
+
+template <typename T>
+inline bool operator==(const optional<T>& opt, const T& v)
+{
+ return static_cast<bool>(opt) ? *opt == v : false;
+}
+
+template <typename T>
+inline bool operator==(const T& v, const optional<T>& opt)
+{
+ return static_cast<bool>(opt) ? *opt == v : false;
+}
+
+template <typename T>
+inline bool operator!=(const optional<T>& opt, const T& v)
+{
+ return static_cast<bool>(opt) ? *opt != v : true;
+}
+
+template <typename T>
+inline bool operator!=(const T& v, const optional<T>& opt)
+{
+ return static_cast<bool>(opt) ? *opt != v : true;
+}
+
+template <typename T>
+inline bool operator<(const optional<T>& opt, const T& v)
+{
+ using namespace std;
+ return static_cast<bool>(opt) ? less<T>(*opt, v) : true;
+}
+
+template <typename T>
+inline bool operator<(const T& v, const optional<T>& opt)
+{
+ using namespace std;
+ return static_cast<bool>(opt) ? less<T>(v, *opt) : false;
+}
+
+template <typename T>
+inline bool operator<=(const optional<T>& opt, const T& v)
+{
+ using namespace std;
+ return static_cast<bool>(opt) ? less_equal<T>(*opt, v) : true;
+}
+
+template <typename T>
+inline bool operator<=(const T& v, const optional<T>& opt)
+{
+ using namespace std;
+ return static_cast<bool>(opt) ? less_equal<T>(v, *opt) : false;
+}
+
+template <typename T>
+inline bool operator>(const optional<T>& opt, const T& v)
+{
+ using namespace std;
+ return static_cast<bool>(opt) ? greater<T>(*opt, v) : false;
+}
+
+template <typename T>
+inline bool operator>(const T& v, const optional<T>& opt)
+{
+ using namespace std;
+ return static_cast<bool>(opt) ? greater<T>(v, *opt) : true;
+}
+
+template <typename T>
+inline bool operator>=(const optional<T>& opt, const T& v)
+{
+ using namespace std;
+ return static_cast<bool>(opt) ? greater_equal<T>(*opt, v) : false;
+}
+
+template <typename T>
+inline bool operator>=(const T& v, const optional<T>& opt)
+{
+ using namespace std;
+ return static_cast<bool>(opt) ? greater_equal<T>(v, *opt) : true;
+}
+
+
+# endif // __TD__OPTIONAL_H__