Magic_Game/core/tools/Music.cpp

903 lines
18 KiB
C++
Raw Normal View History

2018-10-03 22:02:46 +08:00
// Copyright (c) 2016-2018 Easy2D - Nomango
//
// 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.
2018-09-05 13:33:39 +08:00
#include "..\e2dtool.h"
#include <cguid.h>
#define MF_CLASS_NAME L"MediaFoundationCallbackWnd"
#define WM_APP_PLAYER_EVENT (WM_APP + 1)
namespace
2018-02-01 22:07:44 +08:00
{
HINSTANCE media_instance = nullptr;
inline void Trace(wchar_t* msg)
{
WARN("Trace error: %s.", msg);
}
inline void Trace(wchar_t* msg, HRESULT hr)
{
WARN("Trace error: %s (%#X).", msg, hr);
}
2018-02-01 22:07:44 +08:00
}
2018-05-17 23:53:27 +08:00
e2d::Music::Music()
: m_pSession(nullptr)
, m_pSource(nullptr)
, m_hwndEvent(nullptr)
, m_state(State::Closed)
, m_hCloseEvent(nullptr)
, m_nTimes(0)
{
if (!media_instance)
{
media_instance = ::GetModuleHandle(NULL);
WNDCLASS wc;
wc.style = 0;
wc.lpfnWndProc = Music::MediaProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = media_instance;
wc.hIcon = 0;
wc.hCursor = ::LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = NULL;
wc.lpszMenuName = NULL;
wc.lpszClassName = MF_CLASS_NAME;
if (!::RegisterClass(&wc))
{
return;
}
}
m_hwndEvent = ::CreateWindowExW(
WS_EX_APPWINDOW,
MF_CLASS_NAME,
NULL,
WS_POPUPWINDOW,
0, 0, 0, 0,
NULL,
NULL,
media_instance,
NULL
);
if (m_hwndEvent)
{
::SetWindowLongW(m_hwndEvent, GWLP_USERDATA, (LONG_PTR)this);
}
m_hCloseEvent = ::CreateEventW(NULL, FALSE, FALSE, NULL);
if (m_hCloseEvent == NULL)
{
ThrowIfFailed(
HRESULT_FROM_WIN32(GetLastError())
);
}
2018-05-17 23:53:27 +08:00
}
2018-04-27 17:07:47 +08:00
2018-09-04 22:42:34 +08:00
e2d::Music::Music(const e2d::String & file_path)
2018-05-17 23:53:27 +08:00
{
2018-10-06 10:25:29 +08:00
this->Load(file_path);
2018-05-17 23:53:27 +08:00
}
2018-04-27 17:07:47 +08:00
e2d::Music::Music(const Resource& res)
2018-05-17 23:53:27 +08:00
{
2018-10-06 10:25:29 +08:00
this->Load(res);
2018-05-17 23:53:27 +08:00
}
2018-04-27 17:07:47 +08:00
2018-05-17 23:53:27 +08:00
e2d::Music::~Music()
{
2018-09-04 22:42:34 +08:00
Close();
if (m_hCloseEvent)
{
::CloseHandle(m_hCloseEvent);
m_hCloseEvent = NULL;
}
DestroyWindow(m_hwndEvent);
}
2018-10-06 10:25:29 +08:00
bool e2d::Music::Load(const e2d::String & file_path)
{
// 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.]
IMFTopology *pTopology = NULL;
IMFPresentationDescriptor* pSourcePD = NULL;
// Close the old session, if any.
Close();
// Create the media session.
HRESULT hr = MFCreateMediaSession(NULL, &m_pSession);
if (FAILED(hr))
2018-02-01 22:07:44 +08:00
{
goto done;
2018-08-12 12:06:06 +08:00
}
// Start pulling events from the media session
hr = m_pSession->BeginGetEvent((IMFAsyncCallback*)this, NULL);
if (FAILED(hr))
2018-08-12 12:06:06 +08:00
{
goto done;
2018-02-01 22:07:44 +08:00
}
// Create the media source.
hr = CreateMediaSource(static_cast<WCHAR*>(file_path), &m_pSource);
if (FAILED(hr))
{
goto done;
}
// Create the presentation descriptor for the media source.
hr = m_pSource->CreatePresentationDescriptor(&pSourcePD);
if (FAILED(hr))
{
goto done;
}
2018-05-24 15:47:38 +08:00
// Create a partial topology.
hr = CreatePlaybackTopology(m_pSource, pSourcePD, &pTopology);
if (FAILED(hr))
{
goto done;
}
// Set the topology on the media session.
hr = m_pSession->SetTopology(0, pTopology);
if (FAILED(hr))
{
goto done;
}
m_state = State::Loaded;
// If SetTopology succeeds, the media session will queue an
// MESessionTopologySet event.
2018-02-01 22:07:44 +08:00
done:
if (FAILED(hr))
{
m_state = State::Closed;
}
SafeRelease(pSourcePD);
SafeRelease(pTopology);
return hr;
}
bool e2d::Music::Load(const Resource& res)
{
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
return false;
}
bool e2d::Music::Play(int loop_count)
{
if (m_pSession == NULL || m_pSource == NULL)
{
return false;
}
HRESULT hr = StartPlayback();
if (SUCCEEDED(hr))
{
m_nTimes = loop_count;
return true;
}
return false;
}
void e2d::Music::Pause()
{
if (m_state != State::Started)
2018-02-01 22:07:44 +08:00
{
return;
}
if (m_pSession == NULL || m_pSource == NULL)
{
return;
}
HRESULT hr = m_pSession->Pause();
if (SUCCEEDED(hr))
{
m_state = State::Paused;
}
}
void e2d::Music::Stop()
{
if (m_state != State::Started && m_state != State::Paused)
{
return;
}
if (m_pSession == NULL)
{
return;
2018-02-01 22:07:44 +08:00
}
HRESULT hr = m_pSession->Stop();
if (SUCCEEDED(hr))
{
m_state = State::Stopped;
}
}
void e2d::Music::Close()
{
// The IMFMediaSession::Close method is asynchronous, but the
// e2d::Music::CloseSession method waits on the MESessionClosed event.
//
// MESessionClosed is guaranteed to be the last event that the
// media session fires.
HRESULT hr = S_OK;
// First close the media session.
if (m_pSession)
{
DWORD dwWaitResult = 0;
m_state = State::Closing;
hr = m_pSession->Close();
// Wait for the close operation to complete
if (SUCCEEDED(hr))
{
WaitForSingleObject(m_hCloseEvent, 5000);
}
}
// Complete shutdown operations.
if (SUCCEEDED(hr))
{
// Shut down the media source. (Synchronous operation, no events.)
if (m_pSource)
{
(void)m_pSource->Shutdown();
}
// Shut down the media session. (Synchronous operation, no events.)
if (m_pSession)
{
(void)m_pSession->Shutdown();
}
}
SafeRelease(m_pSource);
SafeRelease(m_pSession);
m_state = State::Closed;
}
bool e2d::Music::IsPlaying() const
{
return m_state == State::Started;
}
bool e2d::Music::SetVolume(float volume)
{
if (!m_pSession)
return false;
IMFSimpleAudioVolume* pAudioVolume = NULL;
HRESULT hr = GetSimpleAudioVolume(&pAudioVolume);
if (SUCCEEDED(hr))
{
volume = std::min(std::max(volume, 0.f), 1.f);
hr = pAudioVolume->SetMasterVolume(volume);
}
SafeRelease(pAudioVolume);
return SUCCEEDED(hr);
}
float e2d::Music::GetVolume() const
{
if (!m_pSession)
return 0.f;
float volume = 0.f;
IMFSimpleAudioVolume* pAudioVolume = NULL;
HRESULT hr = GetSimpleAudioVolume(&pAudioVolume);
if (SUCCEEDED(hr))
{
hr = pAudioVolume->GetMasterVolume(&volume);
}
SafeRelease(pAudioVolume);
if (SUCCEEDED(hr))
{
return volume;
}
return 0.f;
}
HRESULT e2d::Music::QueryInterface(REFIID riid, void** ppv)
{
static const QITAB qit[] =
{
QITABENT(Music, IMFAsyncCallback),
{ 0 }
};
return QISearch(this, qit, riid, ppv);
}
ULONG e2d::Music::AddRef()
{
return InterlockedIncrement(&m_nRefCount);
}
ULONG e2d::Music::Release()
{
ULONG uCount = InterlockedDecrement(&m_nRefCount);
if (uCount == 0)
{
delete this;
}
return uCount;
}
HRESULT e2d::Music::GetParameters(DWORD *, DWORD *)
{
// Implementation of this method is optional.
return E_NOTIMPL;
}
HRESULT e2d::Music::Invoke(IMFAsyncResult *pResult)
{
MediaEventType meType = MEUnknown; // Event type
IMFMediaEvent *pEvent = NULL;
// Get the event from the event queue.
HRESULT hr = m_pSession->EndGetEvent(pResult, &pEvent);
if (FAILED(hr))
2018-03-02 23:49:57 +08:00
{
goto done;
2018-03-02 23:49:57 +08:00
}
// Get the event type.
hr = pEvent->GetType(&meType);
if (FAILED(hr))
2018-03-02 23:49:57 +08:00
{
goto done;
2018-03-02 23:49:57 +08:00
}
2018-02-01 22:07:44 +08:00
if (meType == MESessionClosed)
{
// The session was closed.
// The application is waiting on the m_hCloseEvent event handle.
SetEvent(m_hCloseEvent);
}
else
{
// For all other events, get the next event in the queue.
hr = m_pSession->BeginGetEvent(this, NULL);
if (FAILED(hr))
{
goto done;
}
}
// Check the application state.
// If a call to IMFMediaSession::Close is pending, it means the
// application is waiting on the m_hCloseEvent event and
// the application's message loop is blocked.
2018-02-06 21:11:54 +08:00
// Otherwise, post a private window message to the application.
2018-02-01 22:07:44 +08:00
if (m_state != State::Closing)
{
// Leave a reference count on the event.
pEvent->AddRef();
::PostMessage(m_hwndEvent, WM_APP_PLAYER_EVENT, (WPARAM)pEvent, (LPARAM)meType);
}
2018-05-17 23:53:27 +08:00
done:
SafeRelease(pEvent);
return S_OK;
}
HRESULT e2d::Music::StartPlayback()
{
PROPVARIANT varStart;
PropVariantInit(&varStart);
HRESULT hr = m_pSession->Start(&GUID_NULL, &varStart);
if (SUCCEEDED(hr))
2018-02-01 22:07:44 +08:00
{
// 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 event with
// an error code, and we will update our state then.
m_state = State::Started;
2018-02-01 22:07:44 +08:00
}
PropVariantClear(&varStart);
return hr;
}
HRESULT e2d::Music::HandleEvent(UINT_PTR pEventPtr)
{
HRESULT hrStatus = S_OK;
MediaEventType meType = MEUnknown;
IMFMediaEvent *pEvent = (IMFMediaEvent*)pEventPtr;
if (pEvent == NULL)
2018-02-01 22:07:44 +08:00
{
return E_POINTER;
2018-02-01 22:07:44 +08:00
}
// Get the event type.
HRESULT hr = pEvent->GetType(&meType);
if (FAILED(hr))
2018-02-01 22:07:44 +08:00
{
goto done;
}
// Get the event status. If the operation that triggered the event
// did not succeed, the status is a failure code.
hr = pEvent->GetStatus(&hrStatus);
// Check if the async operation succeeded.
if (SUCCEEDED(hr) && FAILED(hrStatus))
{
hr = hrStatus;
}
if (FAILED(hr))
{
goto done;
}
switch (meType)
{
case MEEndOfPresentation:
printf("%d\n", m_nTimes);
if (m_nTimes)
2018-02-01 22:07:44 +08:00
{
--m_nTimes;
hr = StartPlayback();
2018-02-01 22:07:44 +08:00
}
else
{
m_state = State::Stopped;
hr = S_OK;
}
break;
case MENewPresentation:
hr = OnNewPresentation(pEvent);
break;
default:
break;
2018-02-01 22:07:44 +08:00
}
done:
SafeRelease(pEvent);
return hr;
}
HRESULT e2d::Music::OnNewPresentation(IMFMediaEvent *pEvent)
{
IMFPresentationDescriptor *pPD = NULL;
IMFTopology *pTopology = NULL;
// Get the presentation descriptor from the event.
PROPVARIANT var;
HRESULT hr = pEvent->GetValue(&var);
if (FAILED(hr))
2018-03-02 23:49:57 +08:00
{
goto done;
2018-03-02 23:49:57 +08:00
}
if (var.vt == VT_UNKNOWN)
2018-03-02 23:49:57 +08:00
{
hr = var.punkVal->QueryInterface(&pPD);
2018-03-02 23:49:57 +08:00
}
else
{
hr = MF_E_INVALIDTYPE;
}
PropVariantClear(&var);
2018-03-02 23:49:57 +08:00
if (FAILED(hr))
{
goto done;
}
2018-03-02 23:49:57 +08:00
// Create a partial topology.
hr = CreatePlaybackTopology(m_pSource, pPD, &pTopology);
if (FAILED(hr))
{
goto done;
}
2018-03-02 23:49:57 +08:00
// Set the topology on the media session.
hr = m_pSession->SetTopology(0, pTopology);
if (FAILED(hr))
{
goto done;
2018-02-01 22:07:44 +08:00
}
m_state = State::Loaded;
done:
SafeRelease(pTopology);
SafeRelease(pPD);
return S_OK;
2018-05-17 23:53:27 +08:00
}
HRESULT e2d::Music::GetSimpleAudioVolume(IMFSimpleAudioVolume ** ppAudioVolume) const
2018-05-17 23:53:27 +08:00
{
if (ppAudioVolume == NULL)
return E_POINTER;
IMFGetService* pGetService = NULL;
HRESULT hr = m_pSession->QueryInterface(IID_IMFGetService, (void **)&pGetService);
if (SUCCEEDED(hr))
2018-02-01 22:07:44 +08:00
{
hr = pGetService->GetService(MR_CAPTURE_POLICY_VOLUME_SERVICE, IID_PPV_ARGS(ppAudioVolume));
2018-02-01 22:07:44 +08:00
}
SafeRelease(pGetService);
return hr;
}
HRESULT e2d::Music::CreateMediaSource(PCWSTR sURL, IMFMediaSource **ppSource)
{
MF_OBJECT_TYPE ObjectType = MF_OBJECT_INVALID;
IMFSourceResolver* pSourceResolver = NULL;
IUnknown* pSource = NULL;
// Create the source resolver.
HRESULT hr = MFCreateSourceResolver(&pSourceResolver);
if (FAILED(hr))
{
goto done;
}
2018-01-30 16:45:38 +08:00
// Use the source resolver to create the media source.
2018-01-30 16:45:38 +08:00
// 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.
2018-01-30 16:45:38 +08:00
hr = pSourceResolver->CreateObjectFromURL(
sURL, // URL of the source.
MF_RESOLUTION_MEDIASOURCE, // Create a source object.
NULL, // Optional property store.
&ObjectType, // Receives the created object type.
&pSource // Receives a pointer to the media source.
);
if (FAILED(hr))
{
goto done;
}
2018-01-30 16:45:38 +08:00
// Get the IMFMediaSource interface from the media source.
hr = pSource->QueryInterface(IID_PPV_ARGS(ppSource));
2018-01-30 16:45:38 +08:00
done:
SafeRelease(pSourceResolver);
SafeRelease(pSource);
return hr;
}
2018-01-30 16:45:38 +08:00
HRESULT e2d::Music::CreateMediaSinkActivate(
IMFStreamDescriptor *pSourceSD,
IMFActivate **ppActivate)
{
IMFMediaTypeHandler *pHandler = NULL;
IMFActivate *pActivate = NULL;
2018-02-01 22:07:44 +08:00
// Get the media type handler for the stream.
HRESULT hr = pSourceSD->GetMediaTypeHandler(&pHandler);
if (FAILED(hr))
{
goto done;
}
2018-01-30 16:45:38 +08:00
// Get the major media type.
GUID guidMajorType;
hr = pHandler->GetMajorType(&guidMajorType);
if (FAILED(hr))
2018-01-30 16:45:38 +08:00
{
goto done;
}
2018-02-01 22:07:44 +08:00
// Create an IMFActivate object for the renderer, based on the media type.
if (MFMediaType_Audio == guidMajorType)
{
// Create the audio renderer.
hr = MFCreateAudioRendererActivate(&pActivate);
}
else if (MFMediaType_Video == guidMajorType)
{
// Create the video renderer.
hr = MFCreateVideoRendererActivate(m_hwndEvent, &pActivate);
2018-01-30 16:45:38 +08:00
}
2018-02-01 22:07:44 +08:00
else
{
// Unknown stream type.
hr = E_FAIL;
// Optionally, you could deselect this stream instead of failing.
}
if (FAILED(hr))
{
goto done;
}
2018-01-30 16:45:38 +08:00
// Return IMFActivate pointer to caller.
*ppActivate = pActivate;
(*ppActivate)->AddRef();
2018-02-01 22:07:44 +08:00
done:
SafeRelease(pHandler);
SafeRelease(pActivate);
return hr;
}
2018-01-30 16:45:38 +08:00
HRESULT e2d::Music::AddSourceNode(
IMFTopology *pTopology,
IMFMediaSource *pSource,
IMFPresentationDescriptor *pPD,
IMFStreamDescriptor *pSD,
IMFTopologyNode **ppNode)
{
IMFTopologyNode *pNode = NULL;
// Create the node.
HRESULT hr = MFCreateTopologyNode(MF_TOPOLOGY_SOURCESTREAM_NODE, &pNode);
if (FAILED(hr))
{
goto done;
2018-02-01 22:07:44 +08:00
}
// Set the attributes.
hr = pNode->SetUnknown(MF_TOPONODE_SOURCE, pSource);
if (FAILED(hr))
2018-01-30 16:45:38 +08:00
{
goto done;
2018-01-30 16:45:38 +08:00
}
2018-02-01 22:07:44 +08:00
hr = pNode->SetUnknown(MF_TOPONODE_PRESENTATION_DESCRIPTOR, pPD);
if (FAILED(hr))
{
goto done;
}
2018-01-30 16:45:38 +08:00
hr = pNode->SetUnknown(MF_TOPONODE_STREAM_DESCRIPTOR, pSD);
if (FAILED(hr))
{
goto done;
}
// Add the node to the topology.
hr = pTopology->AddNode(pNode);
if (FAILED(hr))
{
goto done;
}
2018-01-30 16:45:38 +08:00
// Return the pointer to the caller.
*ppNode = pNode;
(*ppNode)->AddRef();
2018-01-30 16:45:38 +08:00
done:
SafeRelease(pNode);
return hr;
2018-01-30 16:45:38 +08:00
}
HRESULT e2d::Music::AddOutputNode(
IMFTopology *pTopology,
IMFActivate *pActivate,
DWORD dwId,
IMFTopologyNode **ppNode)
2018-01-30 16:45:38 +08:00
{
IMFTopologyNode *pNode = NULL;
2018-01-30 16:45:38 +08:00
// Create the node.
HRESULT hr = MFCreateTopologyNode(MF_TOPOLOGY_OUTPUT_NODE, &pNode);
if (FAILED(hr))
{
goto done;
}
2018-01-30 16:45:38 +08:00
// Set the object pointer.
hr = pNode->SetObject(pActivate);
if (FAILED(hr))
{
goto done;
}
2018-01-30 16:45:38 +08:00
// Set the stream sink ID attribute.
hr = pNode->SetUINT32(MF_TOPONODE_STREAMID, dwId);
if (FAILED(hr))
2018-02-01 22:07:44 +08:00
{
goto done;
}
2018-01-30 16:45:38 +08:00
hr = pNode->SetUINT32(MF_TOPONODE_NOSHUTDOWN_ON_REMOVE, FALSE);
if (FAILED(hr))
{
goto done;
}
2018-01-30 16:45:38 +08:00
// Add the node to the topology.
hr = pTopology->AddNode(pNode);
if (FAILED(hr))
{
goto done;
2018-01-30 16:45:38 +08:00
}
// Return the pointer to the caller.
*ppNode = pNode;
(*ppNode)->AddRef();
2018-01-30 16:45:38 +08:00
done:
SafeRelease(pNode);
return hr;
2018-01-30 16:45:38 +08:00
}
HRESULT e2d::Music::AddBranchToPartialTopology(
IMFTopology *pTopology,
IMFMediaSource *pSource,
IMFPresentationDescriptor *pPD,
DWORD iStream)
2018-01-30 16:45:38 +08:00
{
// 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.
IMFStreamDescriptor *pSD = NULL;
IMFActivate *pSinkActivate = NULL;
IMFTopologyNode *pSourceNode = NULL;
IMFTopologyNode *pOutputNode = NULL;
BOOL fSelected = FALSE;
HRESULT hr = pPD->GetStreamDescriptorByIndex(iStream, &fSelected, &pSD);
if (FAILED(hr))
{
goto done;
}
2018-07-25 16:44:22 +08:00
if (fSelected)
2018-01-30 16:45:38 +08:00
{
// Create the media sink activation object.
hr = CreateMediaSinkActivate(pSD, &pSinkActivate);
if (FAILED(hr))
{
goto done;
}
2018-01-30 16:45:38 +08:00
// Add a source node for this stream.
hr = AddSourceNode(pTopology, pSource, pPD, pSD, &pSourceNode);
if (FAILED(hr))
{
goto done;
}
2018-01-30 16:45:38 +08:00
// Create the output node for the renderer.
hr = AddOutputNode(pTopology, pSinkActivate, 0, &pOutputNode);
if (FAILED(hr))
{
goto done;
}
2018-01-30 16:45:38 +08:00
// Connect the source node to the output node.
hr = pSourceNode->ConnectOutput(0, pOutputNode, 0);
}
// else: If not selected, don't add the branch.
done:
SafeRelease(pSD);
SafeRelease(pSinkActivate);
SafeRelease(pSourceNode);
SafeRelease(pOutputNode);
return hr;
}
2018-01-30 16:45:38 +08:00
HRESULT e2d::Music::CreatePlaybackTopology(
IMFMediaSource *pSource,
IMFPresentationDescriptor *pPD,
IMFTopology **ppTopology)
{
IMFTopology *pTopology = NULL;
DWORD cSourceStreams = 0;
2018-02-01 22:07:44 +08:00
// Create a new topology.
HRESULT hr = MFCreateTopology(&pTopology);
if (FAILED(hr))
{
goto done;
}
2018-02-01 22:07:44 +08:00
// Get the number of streams in the media source.
hr = pPD->GetStreamDescriptorCount(&cSourceStreams);
if (FAILED(hr))
{
goto done;
}
2018-02-01 22:07:44 +08:00
// For each stream, create the topology nodes and add them to the topology.
for (DWORD i = 0; i < cSourceStreams; i++)
2018-01-30 16:45:38 +08:00
{
hr = AddBranchToPartialTopology(pTopology, pSource, pPD, i);
if (FAILED(hr))
2018-02-01 22:07:44 +08:00
{
goto done;
2018-02-01 22:07:44 +08:00
}
}
// Return the IMFTopology pointer to the caller.
*ppTopology = pTopology;
(*ppTopology)->AddRef();
2018-02-01 22:07:44 +08:00
done:
SafeRelease(pTopology);
return hr;
}
LRESULT e2d::Music::MediaProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
switch (Msg)
{
case WM_APP_PLAYER_EVENT:
2018-02-01 22:07:44 +08:00
{
Music * music = reinterpret_cast<Music*>(
static_cast<LONG_PTR>(::GetWindowLongPtrW(hWnd, GWLP_USERDATA))
);
if (music)
{
music->HandleEvent(wParam);
}
2018-02-01 22:07:44 +08:00
}
break;
2018-02-01 22:07:44 +08:00
default:
return ::DefWindowProc(hWnd, Msg, wParam, lParam);
2018-01-30 16:45:38 +08:00
}
return 0;
2018-01-30 16:45:38 +08:00
}