add: decode media resources with Media Foundation and playback with XAudio2.

This commit is contained in:
Nomango 2018-10-15 20:16:08 +08:00
parent 80d1a87955
commit a924eed280
13 changed files with 481 additions and 1143 deletions

View File

@ -57,12 +57,11 @@
#include <d2d1.h> #include <d2d1.h>
#include <dwrite.h> #include <dwrite.h>
#include <dinput.h> #include <dinput.h>
#include <shlwapi.h> #include <xaudio2.h>
#include <mfapi.h> #include <mfapi.h>
#include <mfidl.h> #include <mfidl.h>
#include <mferror.h> #include <mfreadwrite.h>
#include <evr.h> #include <shlwapi.h>
#include <mmdeviceapi.h>
// C++ RunTime Header Files // C++ RunTime Header Files
#include <map> #include <map>
@ -83,11 +82,10 @@
#pragma comment(lib, "windowscodecs.lib") #pragma comment(lib, "windowscodecs.lib")
#pragma comment(lib, "winmm.lib") #pragma comment(lib, "winmm.lib")
#pragma comment(lib, "dinput8.lib") #pragma comment(lib, "dinput8.lib")
#pragma comment(lib, "shlwapi") #pragma comment(lib, "xaudio2.lib")
#pragma comment(lib, "mfplay.lib")
#pragma comment(lib, "mf.lib")
#pragma comment(lib, "mfplat.lib") #pragma comment(lib, "mfplat.lib")
#pragma comment(lib, "mfuuid.lib") #pragma comment(lib, "mfreadwrite.lib")
#pragma comment(lib, "shlwapi.lib")
#ifndef HINST_THISCOMPONENT #ifndef HINST_THISCOMPONENT

View File

@ -149,30 +149,21 @@ namespace e2d
~Audio(); ~Audio();
// 获取音量 // 开启设备
float GetVolume(); void Open();
// 设置音量 // 关闭设备
void SetVolume( void Close();
float volume
);
// 是否静音 // 创建音源
bool GetMute(); HRESULT CreateVoice(
IXAudio2SourceVoice ** voice,
// 设置静音 WAVEFORMATEX * wfx
void SetMute(
bool mute
); );
protected: protected:
LPWSTR device_id; // Device ID. IXAudio2 * x_audio2_;
IMMDeviceEnumerator *enum_; // Audio device enumerator. IXAudio2MasteringVoice* mastering_voice_;
IMMDeviceCollection *devices_; // Audio device collection.
IMMDevice *device_; // An audio device.
IMFAttributes *attributes_; // Attribute store.
IMFMediaSink *sink_; // Streaming audio renderer (SAR)
IMFSimpleAudioVolume *audio_volume;
}; };

View File

@ -71,19 +71,10 @@ namespace e2d
// 音乐 // 音乐
class Music class Music
: public IMFAsyncCallback
{ {
public: public:
Music(); Music();
explicit Music(
const e2d::String& file_path /* 音乐文件路径 */
);
explicit Music(
const Resource& res /* 音乐资源 */
);
virtual ~Music(); virtual ~Music();
// 打开音乐文件 // 打开音乐文件
@ -104,6 +95,9 @@ namespace e2d
// 暂停 // 暂停
void Pause(); void Pause();
// 继续
void Resume();
// 停止 // 停止
void Stop(); void Stop();
@ -113,99 +107,26 @@ namespace e2d
// 是否正在播放 // 是否正在播放
bool IsPlaying() const; bool IsPlaying() const;
// 设置音量
bool SetVolume(
float volume /* 范围: 0.0 ~ 1.0 */
);
// 获取音量 // 获取音量
float GetVolume() const; float GetVolume() const;
// IUnknown methods // 设置音量
STDMETHODIMP QueryInterface(REFIID iid, void** ppv) override; bool SetVolume(
STDMETHODIMP_(ULONG) AddRef() override; float volume /* 1.0 为原始音量 */
STDMETHODIMP_(ULONG) Release() override; );
// IMFAsyncCallback methods // 获取 IXAudio2SourceVoice 对象
STDMETHODIMP GetParameters(DWORD*, DWORD*) override; IXAudio2SourceVoice * GetSourceVoice() const;
STDMETHODIMP Invoke(IMFAsyncResult* pAsyncResult) override;
protected: protected:
enum class State : int
{
Closed = 0,
Loaded,
Started,
Paused,
Stopped,
Closing
};
E2D_DISABLE_COPY(Music); E2D_DISABLE_COPY(Music);
// Media event handlers
HRESULT OnNewPresentation(IMFMediaEvent *pEvent);
HRESULT StartPlayback();
HRESULT CreateMediaSource(PCWSTR sURL, IMFMediaSource **ppSource);
HRESULT HandleEvent(UINT_PTR pUnkPtr);
// Add a source node to a topology.
HRESULT AddSourceNode(
IMFTopology *pTopology, // Topology.
IMFMediaSource *pSource, // Media source.
IMFPresentationDescriptor *pPD, // Presentation descriptor.
IMFStreamDescriptor *pSD, // Stream descriptor.
IMFTopologyNode **ppNode // Receives the node pointer.
);
// Add an output node to a topology.
HRESULT AddOutputNode(
IMFTopology *pTopology, // Topology.
IMFActivate *pActivate, // Media sink activation object.
DWORD dwId, // Identifier of the stream sink.
IMFTopologyNode **ppNode // Receives the node pointer.
);
HRESULT AddBranchToPartialTopology(
IMFTopology *pTopology, // Topology.
IMFMediaSource *pSource, // Media source.
IMFPresentationDescriptor *pPD, // Presentation descriptor.
DWORD iStream // Stream index.
);
// Create a playback topology from a media source.
HRESULT CreatePlaybackTopology(
IMFMediaSource *pSource, // Media source.
IMFPresentationDescriptor *pPD, // Presentation descriptor.
IMFTopology **ppTopology // Receives a pointer to the topology.
);
// Create an activation object for a renderer, based on the stream media type.
HRESULT CreateMediaSinkActivate(
IMFStreamDescriptor *pSourceSD, // Pointer to the stream descriptor.
IMFActivate **ppActivate
);
HRESULT GetSimpleAudioVolume(
IMFSimpleAudioVolume** ppAudioVolume
) const;
static LRESULT CALLBACK MediaProc(
HWND hWnd,
UINT Msg,
WPARAM wParam,
LPARAM lParam
);
protected: protected:
long m_nRefCount; bool opened_;
int m_nTimes; bool playing_;
IMFMediaSession * m_pSession; UINT32 size_;
IMFMediaSource *m_pSource; BYTE* wave_data_;
HWND m_hwndEvent; IXAudio2SourceVoice* voice_;
State m_state;
HANDLE m_hCloseEvent;
}; };
@ -280,11 +201,11 @@ namespace e2d
); );
// 获取音量 // 获取音量
float GetVolume(); float GetVolume() const;
// 设置音量 // 设置音量
void SetVolume( void SetVolume(
float volume /* 范围: 0.0 ~ 1.0 */ float volume /* 1.0 为原始音量 */
); );
// 暂停所有音乐 // 暂停所有音乐

View File

@ -1,116 +0,0 @@
// 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.
#include "..\e2dimpl.h"
e2d::MediaAsyncCallback::MediaAsyncCallback(HWND hwnd, IMFMediaSession * pSession, HANDLE hCloseEvent)
: m_pSession(pSession)
, m_hCloseEvent(hCloseEvent)
, m_hwnd(hwnd)
{
}
HRESULT e2d::MediaAsyncCallback::QueryInterface(REFIID riid, void** ppv)
{
static const QITAB qit[] =
{
QITABENT(MediaAsyncCallback, IMFAsyncCallback),
{ 0 }
};
return QISearch(this, qit, riid, ppv);
}
ULONG e2d::MediaAsyncCallback::AddRef()
{
return InterlockedIncrement(&m_nRefCount);
}
ULONG e2d::MediaAsyncCallback::Release()
{
ULONG uCount = InterlockedDecrement(&m_nRefCount);
if (uCount == 0)
{
delete this;
}
return uCount;
}
HRESULT e2d::MediaAsyncCallback::GetParameters(DWORD *, DWORD *)
{
// Implementation of this method is optional.
return E_NOTIMPL;
}
HRESULT e2d::MediaAsyncCallback::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))
{
goto done;
}
// Get the event type.
hr = pEvent->GetType(&meType);
if (FAILED(hr))
{
goto done;
}
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.
// Otherwise, post a private window message to the application.
//if (m_state != Closing)
{
// Leave a reference count on the event.
pEvent->AddRef();
::PostMessage(m_hwnd, WM_APP + 1, (WPARAM)pEvent, (LPARAM)meType);
}
done:
SafeRelease(pEvent);
return S_OK;
}

View File

@ -22,128 +22,46 @@
e2d::Audio::Audio() e2d::Audio::Audio()
: enum_(nullptr) : x_audio2_(nullptr)
, devices_(nullptr) , mastering_voice_(nullptr)
, device_(nullptr)
, attributes_(nullptr)
, sink_(nullptr)
, device_id(nullptr)
, audio_volume(nullptr)
{ {
ThrowIfFailed( ThrowIfFailed(
MFStartup(MF_VERSION) MFStartup(MF_VERSION)
); );
// Create the device enumerator.
ThrowIfFailed( ThrowIfFailed(
CoCreateInstance( XAudio2Create(&x_audio2_)
__uuidof(MMDeviceEnumerator),
NULL,
CLSCTX_ALL,
__uuidof(IMMDeviceEnumerator),
(void**)&enum_
)
);
// Enumerate the rendering devices.
ThrowIfFailed(
enum_->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE, &devices_)
);
// Get ID of the first device in the list.
ThrowIfFailed(
devices_->Item(0, &device_)
); );
ThrowIfFailed( ThrowIfFailed(
device_->GetId(&device_id) x_audio2_->CreateMasteringVoice(&mastering_voice_)
); );
// Create an attribute store and set the device ID attribute.
ThrowIfFailed(
MFCreateAttributes(&attributes_, 2)
);
ThrowIfFailed(
attributes_->SetString(
MF_AUDIO_RENDERER_ATTRIBUTE_ENDPOINT_ID,
device_id
)
);
// Create the audio renderer.
ThrowIfFailed(
MFCreateAudioRenderer(attributes_, &sink_)
);
IMFGetService* service = NULL;
ThrowIfFailed(
sink_->QueryInterface(IID_IMFGetService, (void **)&service)
);
ThrowIfFailed(
service->GetService(MR_POLICY_VOLUME_SERVICE, IID_PPV_ARGS(&audio_volume))
);
SafeRelease(service);
} }
e2d::Audio::~Audio() e2d::Audio::~Audio()
{ {
SafeRelease(enum_); if (mastering_voice_)
SafeRelease(devices_); {
SafeRelease(device_); mastering_voice_->DestroyVoice();
SafeRelease(attributes_); mastering_voice_ = nullptr;
SafeRelease(audio_volume); }
SafeRelease(sink_);
CoTaskMemFree(device_id); SafeRelease(x_audio2_);
MFShutdown(); MFShutdown();
} }
float e2d::Audio::GetVolume() HRESULT e2d::Audio::CreateVoice(IXAudio2SourceVoice ** voice, WAVEFORMATEX * wfx)
{ {
float volume = 0.f; return x_audio2_->CreateSourceVoice(voice, wfx, 0, XAUDIO2_DEFAULT_FREQ_RATIO);
if (audio_volume)
{
HRESULT hr = audio_volume->GetMasterVolume(&volume);
if (SUCCEEDED(hr))
{
return volume;
}
}
return 0.f;
} }
void e2d::Audio::SetVolume(float volume) void e2d::Audio::Open()
{ {
if (audio_volume) x_audio2_->StartEngine();
{
volume = std::min(std::max(volume, 0.f), 1.f);
HRESULT hr = audio_volume->SetMasterVolume(volume);
printf("ºÇºÇ%#X\n", hr);
}
} }
bool e2d::Audio::GetMute() void e2d::Audio::Close()
{ {
BOOL mute = FALSE; x_audio2_->StopEngine();
if (audio_volume)
{
HRESULT hr = audio_volume->GetMute(&mute);
if (SUCCEEDED(hr))
{
return mute ? true : false;
}
}
return FALSE;
}
void e2d::Audio::SetMute(bool mute)
{
if (audio_volume)
{
audio_volume->SetMute(mute ? TRUE : FALSE);
}
} }

File diff suppressed because it is too large Load Diff

View File

@ -68,7 +68,7 @@ void e2d::Player::Resume(const String & file_path)
size_t hash = file_path.GetHash(); size_t hash = file_path.GetHash();
if (musics_.end() != musics_.find(hash)) if (musics_.end() != musics_.find(hash))
musics_[hash]->Play(); musics_[hash]->Resume();
} }
void e2d::Player::Stop(const String & file_path) void e2d::Player::Stop(const String & file_path)
@ -137,7 +137,7 @@ void e2d::Player::Pause(const Resource& res)
void e2d::Player::Resume(const Resource& res) void e2d::Player::Resume(const Resource& res)
{ {
if (musics_.end() != musics_.find(res.id)) if (musics_.end() != musics_.find(res.id))
musics_[res.id]->Play(); musics_[res.id]->Resume();
} }
void e2d::Player::Stop(const Resource& res) void e2d::Player::Stop(const Resource& res)
@ -153,14 +153,14 @@ bool e2d::Player::IsPlaying(const Resource& res)
return false; return false;
} }
float e2d::Player::GetVolume() float e2d::Player::GetVolume() const
{ {
return volume_; return volume_;
} }
void e2d::Player::SetVolume(float volume) void e2d::Player::SetVolume(float volume)
{ {
volume_ = std::min(std::max(volume, 0.f), 1.f); volume_ = std::min(std::max(volume, -224.f), 224.f);
for (const auto& pair : musics_) for (const auto& pair : musics_)
{ {
pair.second->SetVolume(volume_); pair.second->SetVolume(volume_);
@ -179,7 +179,7 @@ void e2d::Player::ResumeAll()
{ {
for (const auto& pair : musics_) for (const auto& pair : musics_)
{ {
pair.second->Play(); pair.second->Resume();
} }
} }

View File

@ -45,7 +45,6 @@
<ClCompile Include="..\..\core\events\KeyEvent.cpp" /> <ClCompile Include="..\..\core\events\KeyEvent.cpp" />
<ClCompile Include="..\..\core\events\MouseEvent.cpp" /> <ClCompile Include="..\..\core\events\MouseEvent.cpp" />
<ClCompile Include="..\..\core\impl\TextRenderer.cpp" /> <ClCompile Include="..\..\core\impl\TextRenderer.cpp" />
<ClCompile Include="..\..\core\impl\VoiceCallback.cpp" />
<ClCompile Include="..\..\core\modules\Audio.cpp" /> <ClCompile Include="..\..\core\modules\Audio.cpp" />
<ClCompile Include="..\..\core\modules\Device.cpp" /> <ClCompile Include="..\..\core\modules\Device.cpp" />
<ClCompile Include="..\..\core\modules\Game.cpp" /> <ClCompile Include="..\..\core\modules\Game.cpp" />

View File

@ -141,9 +141,6 @@
<ClCompile Include="..\..\core\impl\TextRenderer.cpp"> <ClCompile Include="..\..\core\impl\TextRenderer.cpp">
<Filter>impl</Filter> <Filter>impl</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="..\..\core\impl\VoiceCallback.cpp">
<Filter>impl</Filter>
</ClCompile>
<ClCompile Include="..\..\core\events\KeyEvent.cpp"> <ClCompile Include="..\..\core\events\KeyEvent.cpp">
<Filter>events</Filter> <Filter>events</Filter>
</ClCompile> </ClCompile>

View File

@ -189,7 +189,6 @@
<ClCompile Include="..\..\core\events\KeyEvent.cpp" /> <ClCompile Include="..\..\core\events\KeyEvent.cpp" />
<ClCompile Include="..\..\core\events\MouseEvent.cpp" /> <ClCompile Include="..\..\core\events\MouseEvent.cpp" />
<ClCompile Include="..\..\core\impl\TextRenderer.cpp" /> <ClCompile Include="..\..\core\impl\TextRenderer.cpp" />
<ClCompile Include="..\..\core\impl\VoiceCallback.cpp" />
<ClCompile Include="..\..\core\modules\Audio.cpp" /> <ClCompile Include="..\..\core\modules\Audio.cpp" />
<ClCompile Include="..\..\core\modules\Device.cpp" /> <ClCompile Include="..\..\core\modules\Device.cpp" />
<ClCompile Include="..\..\core\modules\Game.cpp" /> <ClCompile Include="..\..\core\modules\Game.cpp" />

View File

@ -141,9 +141,6 @@
<ClCompile Include="..\..\core\impl\TextRenderer.cpp"> <ClCompile Include="..\..\core\impl\TextRenderer.cpp">
<Filter>impl</Filter> <Filter>impl</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="..\..\core\impl\VoiceCallback.cpp">
<Filter>impl</Filter>
</ClCompile>
<ClCompile Include="..\..\core\events\KeyEvent.cpp"> <ClCompile Include="..\..\core\events\KeyEvent.cpp">
<Filter>events</Filter> <Filter>events</Filter>
</ClCompile> </ClCompile>

View File

@ -101,7 +101,7 @@
<Optimization>Disabled</Optimization> <Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>false</SDLCheck> <SDLCheck>false</SDLCheck>
<DebugInformationFormat>None</DebugInformationFormat> <DebugInformationFormat>EditAndContinue</DebugInformationFormat>
<MinimalRebuild>false</MinimalRebuild> <MinimalRebuild>false</MinimalRebuild>
<TreatWarningAsError>true</TreatWarningAsError> <TreatWarningAsError>true</TreatWarningAsError>
</ClCompile> </ClCompile>
@ -222,7 +222,6 @@
<ClCompile Include="..\..\core\events\KeyEvent.cpp" /> <ClCompile Include="..\..\core\events\KeyEvent.cpp" />
<ClCompile Include="..\..\core\events\MouseEvent.cpp" /> <ClCompile Include="..\..\core\events\MouseEvent.cpp" />
<ClCompile Include="..\..\core\impl\TextRenderer.cpp" /> <ClCompile Include="..\..\core\impl\TextRenderer.cpp" />
<ClCompile Include="..\..\core\impl\MediaAsyncCallback.cpp" />
<ClCompile Include="..\..\core\modules\Audio.cpp" /> <ClCompile Include="..\..\core\modules\Audio.cpp" />
<ClCompile Include="..\..\core\modules\Device.cpp" /> <ClCompile Include="..\..\core\modules\Device.cpp" />
<ClCompile Include="..\..\core\modules\Game.cpp" /> <ClCompile Include="..\..\core\modules\Game.cpp" />

View File

@ -216,9 +216,6 @@
<ClCompile Include="..\..\core\modules\Device.cpp"> <ClCompile Include="..\..\core\modules\Device.cpp">
<Filter>modules</Filter> <Filter>modules</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="..\..\core\impl\MediaAsyncCallback.cpp">
<Filter>impl</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="..\..\core\easy2d.h" /> <ClInclude Include="..\..\core\easy2d.h" />