diff options
Diffstat (limited to 'src/player')
| -rw-r--r-- | src/player/backends/direct_show.c | 464 | ||||
| -rw-r--r-- | src/player/backends/media_foundation.cpp | 1110 | ||||
| -rw-r--r-- | src/player/backends/media_foundation.h | 115 | ||||
| -rw-r--r-- | src/player/backends/utility/com_ptr.h | 148 | ||||
| -rw-r--r-- | src/player/backends/utility/optional.h | 517 | ||||
| -rw-r--r-- | src/player/player2.c | 211 | ||||
| -rw-r--r-- | src/player/player2.h | 53 | ||||
| -rw-r--r-- | src/player/player2_private.h | 35 | 
8 files changed, 2653 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__ diff --git a/src/player/player2.c b/src/player/player2.c new file mode 100644 index 0000000..cd9beae --- /dev/null +++ b/src/player/player2.c @@ -0,0 +1,211 @@ +/* +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" +#include <stdlib.h> +#include <string.h> +#include <memory.h> + +# define length_of(x)   (sizeof(x)/sizeof(*(x))) + +static player2_iface* player2_backends[] = +{ +    &player2_windows_media_foundation, // expermiental +    &player2_direct_show, +}; + +struct _player_t +{ +    player2_iface*  backend; +    player2_t       player; +}; + +bool BarPlayer2Init(player2_t* outPlayer, const char* defaultPlayer) +{ +    player2_t player; +    struct _player_t result; +    int i; + +    memset(&result, 0, sizeof(struct _player_t)); + +    for (i = 0; i < length_of(player2_backends); ++i) +    { +        player2_iface* backend = player2_backends[i]; + +        bool acceptPlayer = true; +        if (defaultPlayer && !(strcmp(backend->Id, defaultPlayer) == 0)) +            acceptPlayer = false; + +        if (acceptPlayer) +            result.player = backend->Create(); + +        if (result.player) +        { +            result.backend = backend; +            break; +        } +    } + +    if (!result.backend) +        return false; + +    player = malloc(sizeof(struct _player_t)); +    if (!player) +        return false; + +    *player = result; + +    *outPlayer = player; + +    return true; +} + +void BarPlayer2Destroy(player2_t player) +{ +    if (player->player) +    { +        player->backend->Destroy(player->player); +        player->player = NULL; +    } +} + +void BarPlayer2SetVolume(player2_t player, float volume) +{ +    if (player->player) +        player->backend->SetVolume(player->player, volume); +} + +float BarPlayer2GetVolume(player2_t player) +{ +    if (player->player) +        return player->backend->GetVolume(player->player); +    else +        return 0.0f; +} + +void BarPlayer2SetGain(player2_t player, float gainDb) +{ +    if (player->player) +        player->backend->SetGain(player->player, gainDb); +} + +float BarPlayer2GetGain(player2_t player) +{ +    if (player->player) +        return player->backend->GetGain(player->player); +    else +        return 0.0f; +} + +double BarPlayer2GetDuration(player2_t player) +{ +    if (player->player) +        return player->backend->GetDuration(player->player); +    else +        return 0.0f; +} + +double BarPlayer2GetTime(player2_t player) +{ +    if (player->player) +        return player->backend->GetTime(player->player); +    else +        return 0.0f; +} + +bool BarPlayer2Open(player2_t player, const char* url) +{ +    if (player->player) +        return player->backend->Open(player->player, url); +    else +        return false; +} + +bool BarPlayer2Play(player2_t player) +{ +    if (player->player) +        return player->backend->Play(player->player); +    else +        return false; +} + +bool BarPlayer2Pause(player2_t player) +{ +    if (player->player) +        return player->backend->Pause(player->player); +    else +        return false; +} + +bool BarPlayer2Stop(player2_t player) +{ +    if (player->player) +        return player->backend->Stop(player->player); +    else +        return false; +} + +bool BarPlayer2Finish(player2_t player) +{ +    if (player->player) +        return player->backend->Finish(player->player); +    else +        return false; +} + +bool BarPlayer2IsPlaying(player2_t player) +{ +    if (player->player) +        return player->backend->IsPlaying(player->player); +    else +        return false; +} + +bool BarPlayer2IsPaused(player2_t player) +{ +    if (player->player) +        return player->backend->IsPaused(player->player); +    else +        return false; +} + +bool BarPlayer2IsStopped(player2_t player) +{ +    if (player->player) +        return player->backend->IsStopped(player->player); +    else +        return false; +} + +bool BarPlayer2IsFinished(player2_t player) +{ +    if (player->player) +        return player->backend->IsFinished(player->player); +    else +        return true; +}
\ No newline at end of file diff --git a/src/player/player2.h b/src/player/player2.h new file mode 100644 index 0000000..710b35d --- /dev/null +++ b/src/player/player2.h @@ -0,0 +1,53 @@ +/* +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. +*/ + +#ifndef __PIANOBAR_PLAYER2_H__ +#define __PIANOBAR_PLAYER2_H__ +#pragma once + +#include "config.h" + +#include <stdbool.h> + +typedef struct _player_t *player2_t; + +bool BarPlayer2Init(player2_t* outPlayer, const char* defaultPlayer); +void BarPlayer2Destroy(player2_t player); +void BarPlayer2SetVolume(player2_t player, float volume); +float BarPlayer2GetVolume(player2_t player); +void BarPlayer2SetGain(player2_t player, float gainDb); +float BarPlayer2GetGain(player2_t player); +double BarPlayer2GetDuration(player2_t player); +double BarPlayer2GetTime(player2_t player); +bool BarPlayer2Open(player2_t player, const char* url); +bool BarPlayer2Play(player2_t player); +bool BarPlayer2Pause(player2_t player); +bool BarPlayer2Stop(player2_t player); +bool BarPlayer2Finish(player2_t player); +bool BarPlayer2IsPlaying(player2_t player); +bool BarPlayer2IsPaused(player2_t player); +bool BarPlayer2IsStopped(player2_t player); +bool BarPlayer2IsFinished(player2_t player); + +#endif /* __PIANOBAR_PLAYER2_H__ */ + diff --git a/src/player/player2_private.h b/src/player/player2_private.h new file mode 100644 index 0000000..757a07e --- /dev/null +++ b/src/player/player2_private.h @@ -0,0 +1,35 @@ +#ifndef __PIANOBAR_PLAYER2_PRIVATE_H__ +#define __PIANOBAR_PLAYER2_PRIVATE_H__ +#pragma once + +#include "config.h" +#include <stdbool.h> +#include "player2.h" + +typedef struct _player2_iface +{ +    const char*     Id; +    const char*     Name; +    player2_t     (*Create)        (); +    void          (*Destroy)       (player2_t player); +    void          (*SetVolume)     (player2_t player, float volume); +    float         (*GetVolume)     (player2_t player); +    void          (*SetGain)       (player2_t player, float gainDb); +    float         (*GetGain)       (player2_t player); +    double        (*GetDuration)   (player2_t player); +    double        (*GetTime)       (player2_t player); +    bool          (*Open)          (player2_t player, const char* url); +    bool          (*Play)          (player2_t player); +    bool          (*Pause)         (player2_t player); +    bool          (*Stop)          (player2_t player); +    bool          (*Finish)        (player2_t player); +    bool          (*IsPlaying)     (player2_t player); +    bool          (*IsPaused)      (player2_t player); +    bool          (*IsStopped)     (player2_t player); +    bool          (*IsFinished)    (player2_t player); +} player2_iface; + +extern player2_iface player2_direct_show; +extern player2_iface player2_windows_media_foundation; + +#endif /* __PIANOBAR_PLAYER2_PRIVATE_H__ */ | 
