feat: support register custom audio transcoder

This commit is contained in:
Nomango 2023-10-06 18:07:07 +08:00
parent 7a1c565a92
commit 641f78b41e
20 changed files with 1211 additions and 990 deletions

View File

@ -1,9 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<ClInclude Include="..\..\src\kiwano-audio\libraries.h" />
<ClInclude Include="..\..\src\kiwano-audio\AudioData.h" />
<ClInclude Include="..\..\src\kiwano-audio\AudioModule.h" />
<ClInclude Include="..\..\src\kiwano-audio\kiwano-audio.h" />
<ClInclude Include="..\..\src\kiwano-audio\libraries.h" />
<ClInclude Include="..\..\src\kiwano-audio\MediaFoundation\mflib.h" />
<ClInclude Include="..\..\src\kiwano-audio\MediaFoundation\Transcoder.h" />
<ClInclude Include="..\..\src\kiwano-audio\Sound.h" />
<ClInclude Include="..\..\src\kiwano-audio\SoundPlayer.h" />
<ClInclude Include="..\..\src\kiwano-audio\Transcoder.h" />
@ -27,11 +30,13 @@
</ProjectConfiguration>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\..\src\kiwano-audio\libraries.cpp" />
<ClCompile Include="..\..\src\kiwano-audio\AudioData.cpp" />
<ClCompile Include="..\..\src\kiwano-audio\AudioModule.cpp" />
<ClCompile Include="..\..\src\kiwano-audio\libraries.cpp" />
<ClCompile Include="..\..\src\kiwano-audio\MediaFoundation\mflib.cpp" />
<ClCompile Include="..\..\src\kiwano-audio\MediaFoundation\Transcoder.cpp" />
<ClCompile Include="..\..\src\kiwano-audio\Sound.cpp" />
<ClCompile Include="..\..\src\kiwano-audio\SoundPlayer.cpp" />
<ClCompile Include="..\..\src\kiwano-audio\Transcoder.cpp" />
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{1B97937D-8184-426C-BE71-29A163DC76C9}</ProjectGuid>

View File

@ -5,14 +5,32 @@
<ClInclude Include="..\..\src\kiwano-audio\Sound.h" />
<ClInclude Include="..\..\src\kiwano-audio\SoundPlayer.h" />
<ClInclude Include="..\..\src\kiwano-audio\Transcoder.h" />
<ClInclude Include="..\..\src\kiwano-audio\libraries.h" />
<ClInclude Include="..\..\src\kiwano-audio\AudioModule.h" />
<ClInclude Include="..\..\src\kiwano-audio\AudioData.h" />
<ClInclude Include="..\..\src\kiwano-audio\MediaFoundation\Transcoder.h">
<Filter>MediaFoundation</Filter>
</ClInclude>
<ClInclude Include="..\..\src\kiwano-audio\libraries.h" />
<ClInclude Include="..\..\src\kiwano-audio\MediaFoundation\mflib.h">
<Filter>MediaFoundation</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\..\src\kiwano-audio\Sound.cpp" />
<ClCompile Include="..\..\src\kiwano-audio\SoundPlayer.cpp" />
<ClCompile Include="..\..\src\kiwano-audio\Transcoder.cpp" />
<ClCompile Include="..\..\src\kiwano-audio\libraries.cpp" />
<ClCompile Include="..\..\src\kiwano-audio\AudioModule.cpp" />
<ClCompile Include="..\..\src\kiwano-audio\AudioData.cpp" />
<ClCompile Include="..\..\src\kiwano-audio\MediaFoundation\Transcoder.cpp">
<Filter>MediaFoundation</Filter>
</ClCompile>
<ClCompile Include="..\..\src\kiwano-audio\libraries.cpp" />
<ClCompile Include="..\..\src\kiwano-audio\MediaFoundation\mflib.cpp">
<Filter>MediaFoundation</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<Filter Include="MediaFoundation">
<UniqueIdentifier>{213a58dc-8054-4bc7-bac5-456e889ae038}</UniqueIdentifier>
</Filter>
</ItemGroup>
</Project>

View File

@ -0,0 +1,44 @@
// Copyright (c) 2016-2018 Kiwano - 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 <kiwano-audio/AudioData.h>
namespace kiwano
{
namespace audio
{
AudioData::AudioData(const BinaryData& data, const AudioMeta& meta)
: data_(data)
, meta_(meta)
{
}
AudioMeta AudioData::GetMeta() const
{
return meta_;
}
BinaryData AudioData::GetData() const
{
return data_;
}
} // namespace audio
} // namespace kiwano

View File

@ -0,0 +1,92 @@
// Copyright (c) 2016-2018 Kiwano - 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.
#pragma once
#include <kiwano/core/BinaryData.h>
#include <kiwano/base/ObjectBase.h>
#include <kiwano/platform/NativeObject.hpp>
namespace kiwano
{
namespace audio
{
KGE_DECLARE_SMART_PTR(AudioData);
/**
* \addtogroup Audio
* @{
*/
/**
* \~chinese
* @brief
*/
enum class AudioFormat
{
PCM,
};
/**
* \~chinese
* @brief
*/
struct AudioMeta
{
AudioFormat format = AudioFormat::PCM; ///< 音频格式
uint16_t channels = 2; ///< 声道单声道为1立体声为2
uint32_t samples_per_sec = 44100; ///< 采样率11025 表示 11.025kHz。PCM格式的采样率通常为44.1kHz
uint16_t bits_per_sample = 16; ///< 位深PCM格式为 8 或 16
uint16_t block_align = 4; ///< 块对齐PCM格式通常是 (channels * bits_per_sample) / 8
};
/**
* \~chinese
* @brief
*/
class AudioData : public NativeObject
{
public:
/// \~chinese
/// @brief 音频数据
/// @param data 音频数据
/// @param meta 音频元数据
AudioData(const BinaryData& data, const AudioMeta& meta);
/// \~chinese
/// @brief 获取音频元数据
AudioMeta GetMeta() const;
/// \~chinese
/// @brief 获取数据
BinaryData GetData() const;
protected:
AudioData() = default;
protected:
BinaryData data_;
AudioMeta meta_;
};
/** @} */
} // namespace audio
} // namespace kiwano

View File

@ -17,11 +17,12 @@
// 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 <kiwano-audio/AudioModule.h>
#include <kiwano-audio/libraries.h>
#include <kiwano/core/Exception.h>
#include <kiwano/utils/Logger.h>
#include <kiwano/platform/FileSystem.h>
#include <kiwano-audio/AudioModule.h>
#include <kiwano-audio/libraries.h>
#include <kiwano-audio/MediaFoundation/Transcoder.h>
namespace kiwano
{
@ -89,18 +90,18 @@ void AudioModule::SetupModule()
{
KGE_DEBUG_LOGF("Creating audio resources");
HRESULT hr = dlls::MediaFoundation::Get().MFStartup(MF_VERSION, MFSTARTUP_FULL);
if (SUCCEEDED(hr))
{
hr = dlls::XAudio2::Get().XAudio2Create(&x_audio2_, 0, XAUDIO2_DEFAULT_PROCESSOR);
}
HRESULT hr = dlls::XAudio2::Get().XAudio2Create(&x_audio2_, 0, XAUDIO2_DEFAULT_PROCESSOR);
if (SUCCEEDED(hr))
{
hr = x_audio2_->CreateMasteringVoice(&mastering_voice_);
}
if (SUCCEEDED(hr))
{
RegisterTranscoder("*", MakePtr<MFTranscoder>());
}
KGE_THROW_IF_FAILED(hr, "Create audio resources failed");
}
@ -119,28 +120,28 @@ void AudioModule::DestroyModule()
x_audio2_->Release();
x_audio2_ = nullptr;
}
dlls::MediaFoundation::Get().MFShutdown();
}
bool AudioModule::CreateSound(Sound& sound, const AudioMetadata& metadata)
bool AudioModule::CreateSound(Sound& sound, AudioDataPtr data)
{
KGE_ASSERT(x_audio2_ && "AudioModule hasn't been initialized!");
HRESULT hr = S_OK;
WAVEFORMATEX wave_format = { 0 };
wave_format.wFormatTag = ConvertWaveFormat(metadata.format);
wave_format.nChannels = WORD(metadata.channels);
wave_format.nSamplesPerSec = DWORD(metadata.samples_per_sec);
wave_format.wBitsPerSample = WORD(metadata.bits_per_sample);
wave_format.nBlockAlign = WORD(metadata.block_align);
wave_format.nAvgBytesPerSec = DWORD(metadata.samples_per_sec * metadata.block_align);
WAVEFORMATEX* wave_format_ptr = &wave_format;
if (metadata.extra_data != nullptr)
WAVEFORMATEX* wave_fmt = data->GetNative<WAVEFORMATEX*>();
if (wave_fmt == nullptr)
{
wave_format_ptr = reinterpret_cast<WAVEFORMATEX*>(metadata.extra_data);
const auto meta = data->GetMeta();
WAVEFORMATEX tmp = { 0 };
tmp.wFormatTag = ConvertWaveFormat(meta.format);
tmp.nChannels = WORD(meta.channels);
tmp.nSamplesPerSec = DWORD(meta.samples_per_sec);
tmp.wBitsPerSample = WORD(meta.bits_per_sample);
tmp.nBlockAlign = WORD(meta.block_align);
tmp.nAvgBytesPerSec = DWORD(meta.samples_per_sec * meta.block_align);
data->SetNative(tmp);
wave_fmt = const_cast<WAVEFORMATEX*>(data->GetNative().CastPtr<WAVEFORMATEX>());
}
if (SUCCEEDED(hr))
@ -150,7 +151,7 @@ bool AudioModule::CreateSound(Sound& sound, const AudioMetadata& metadata)
auto callback = const_cast<VoiceCallback*>(chain->GetNative().CastPtr<VoiceCallback>());
IXAudio2SourceVoice* voice = nullptr;
hr = x_audio2_->CreateSourceVoice(&voice, wave_format_ptr, 0, XAUDIO2_DEFAULT_FREQ_RATIO, callback);
hr = x_audio2_->CreateSourceVoice(&voice, wave_fmt, 0, XAUDIO2_DEFAULT_FREQ_RATIO, callback);
if (SUCCEEDED(hr))
{
sound.Close();
@ -180,5 +181,50 @@ void AudioModule::Close()
if (x_audio2_)
x_audio2_->StopEngine();
}
void AudioModule::RegisterTranscoder(const String& ext, TranscoderPtr transcoder)
{
registered_transcoders_.insert(std::make_pair(ext, transcoder));
}
TranscoderPtr AudioModule::GetTranscoder(const String& ext)
{
auto iter = registered_transcoders_.find(ext);
if (iter != registered_transcoders_.end())
{
return iter->second;
}
return registered_transcoders_.at("*");
}
AudioDataPtr AudioModule::Decode(const String& file_path)
{
if (!FileSystem::GetInstance().IsFileExists(file_path))
{
KGE_WARNF("Media file '%s' not found", file_path.c_str());
return nullptr;
}
const auto ext = FileSystem::GetInstance().GetFileExt(file_path);
auto transcoder = GetTranscoder(ext);
if (!transcoder)
{
return nullptr;
}
String full_path = FileSystem::GetInstance().GetFullPathForFile(file_path);
return transcoder->Decode(full_path);
}
AudioDataPtr AudioModule::Decode(const Resource& res, const String& ext)
{
auto transcoder = GetTranscoder(ext);
if (!transcoder)
{
return nullptr;
}
return transcoder->Decode(res);
}
} // namespace audio
} // namespace kiwano

View File

@ -59,9 +59,30 @@ public:
/// @brief 关闭音频设备
void Close();
/// \~chinese
/// @brief 注册解码器
/// @param ext 文件类型ogg* 为默认解码器
/// @param transcoder 解码器
void RegisterTranscoder(const String& ext, TranscoderPtr transcoder);
/// \~chinese
/// @brief 获取解码器
TranscoderPtr GetTranscoder(const String& ext);
/// \~chinese
/// @brief 解码音频
/// @param file_path 本地音频文件路径
AudioDataPtr Decode(const String& file_path);
/// \~chinese
/// @brief 解码音频
/// @param res 音频资源
/// @param ext 音频类型,决定了使用何种解码器
AudioDataPtr Decode(const Resource& res, const String& ext = "");
/// \~chinese
/// @brief 创建音频
bool CreateSound(Sound& sound, const AudioMetadata& metadata);
bool CreateSound(Sound& sound, AudioDataPtr data);
public:
void SetupModule() override;
@ -76,6 +97,8 @@ private:
private:
IXAudio2* x_audio2_;
IXAudio2MasteringVoice* mastering_voice_;
UnorderedMap<String, TranscoderPtr> registered_transcoders_;
};
/** @} */

View File

@ -18,113 +18,53 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#ifndef INITGUID
#define INITGUID // MFAudioFormat_PCM, MF_MT_MAJOR_TYPE, MF_MT_SUBTYPE, MFMediaType_Audio
#endif
#include <kiwano-audio/Transcoder.h>
#include <kiwano-audio/libraries.h>
#include <kiwano-audio/MediaFoundation/Transcoder.h>
#include <kiwano-audio/MediaFoundation/mflib.h>
#include <kiwano/core/Common.h>
#include <kiwano/core/Resource.h>
#include <kiwano/utils/Logger.h>
#include <kiwano/platform/win32/ComPtr.hpp>
#include <kiwano/platform/win32/libraries.h>
#include <kiwano/platform/FileSystem.h>
#include <mfapi.h>
#include <mfidl.h>
#include <mfreadwrite.h>
namespace kiwano
{
namespace audio
{
HRESULT ReadSource(IMFSourceReader* reader, AudioMetadata& output_metadata, std::unique_ptr<uint8_t[]>& output_data,
uint32_t& output_size);
HRESULT ReadSource(AudioDataPtr& output, IMFSourceReader* reader);
Transcoder::Transcoder(const String& file_path)
MFTranscoder::MFTranscoder()
{
Load(file_path);
HRESULT hr = dlls::MediaFoundation::Get().MFStartup(MF_VERSION, MFSTARTUP_FULL);
KGE_THROW_IF_FAILED(hr, "Initialize Media Foundation failed!");
}
Transcoder::Transcoder(const Resource& res)
MFTranscoder::~MFTranscoder()
{
Load(res);
dlls::MediaFoundation::Get().MFShutdown();
}
Transcoder::Transcoder(const BinaryData& data, const AudioMetadata& metadata)
AudioDataPtr MFTranscoder::Decode(const String& file_path)
{
Load(data, metadata);
}
Transcoder::~Transcoder()
{
Clear();
}
AudioMetadata Transcoder::GetMetadata() const
{
return metadata_;
}
BinaryData Transcoder::GetData() const
{
return data_;
}
void Transcoder::Clear()
{
if (metadata_.extra_data != nullptr)
{
WAVEFORMATEX* wave_format = reinterpret_cast<WAVEFORMATEX*>(metadata_.extra_data);
::CoTaskMemFree(wave_format);
metadata_.extra_data = nullptr;
}
data_ = {};
raw_.reset();
}
bool Transcoder::Load(const BinaryData& data, const AudioMetadata& metadata)
{
data_ = data;
metadata_ = metadata;
return true;
}
bool Transcoder::Load(const String& file_path)
{
if (!FileSystem::GetInstance().IsFileExists(file_path))
{
KGE_WARNF("Media file '%s' not found", file_path.c_str());
return false;
}
String full_path = FileSystem::GetInstance().GetFullPathForFile(file_path);
WideString path = strings::NarrowToWide(full_path);
ComPtr<IMFSourceReader> reader;
HRESULT hr = dlls::MediaFoundation::Get().MFCreateSourceReaderFromURL(path.c_str(), nullptr, &reader);
WideString path = strings::NarrowToWide(file_path);
HRESULT hr = dlls::MediaFoundation::Get().MFCreateSourceReaderFromURL(path.c_str(), nullptr, &reader);
AudioDataPtr output;
if (SUCCEEDED(hr))
{
hr = ReadSource(reader.Get(), metadata_, raw_, data_.size);
if (SUCCEEDED(hr))
{
data_.buffer = raw_.get();
}
hr = ReadSource(output, reader.Get());
}
if (FAILED(hr))
{
Fail(strings::Format("%s failed (%#x): %s", __FUNCTION__, hr, "Load audio failed"));
return false;
return nullptr;
}
return true;
return output;
}
bool Transcoder::Load(const Resource& res)
AudioDataPtr MFTranscoder::Decode(const Resource& res)
{
HRESULT hr = S_OK;
@ -136,7 +76,7 @@ bool Transcoder::Load(const Resource& res)
if (!data.IsValid())
{
Fail("invalid audio data");
return false;
return nullptr;
}
stream = win32::dlls::Shlwapi::Get().SHCreateMemStream(static_cast<const BYTE*>(data.buffer),
@ -145,7 +85,7 @@ bool Transcoder::Load(const Resource& res)
if (stream == nullptr)
{
Fail("SHCreateMemStream failed");
return false;
return nullptr;
}
if (SUCCEEDED(hr))
@ -158,26 +98,51 @@ bool Transcoder::Load(const Resource& res)
hr = dlls::MediaFoundation::Get().MFCreateSourceReaderFromByteStream(byte_stream.Get(), nullptr, &reader);
}
AudioDataPtr output;
if (SUCCEEDED(hr))
{
hr = ReadSource(reader.Get(), metadata_, raw_, data_.size);
if (SUCCEEDED(hr))
{
data_.buffer = raw_.get();
}
hr = ReadSource(output, reader.Get());
}
if (FAILED(hr))
{
Fail(strings::Format("%s failed (%#x): %s", __FUNCTION__, hr, "Load audio failed"));
return false;
return nullptr;
}
return true;
return output;
}
HRESULT ReadSource(IMFSourceReader* reader, AudioMetadata& output_metadata, std::unique_ptr<uint8_t[]>& output_data,
uint32_t& output_size)
class MFAudioData : public AudioData
{
public:
MFAudioData(WAVEFORMATEX* wave_fmt, std::unique_ptr<uint8_t[]> raw, uint32_t size)
: wave_fmt_(wave_fmt)
, raw_(std::move(raw))
{
data_ = BinaryData{ raw_.get(), size };
meta_.format = AudioFormat::PCM;
meta_.channels = uint16_t(wave_fmt->nChannels);
meta_.samples_per_sec = uint32_t(wave_fmt->nSamplesPerSec);
meta_.bits_per_sample = uint16_t(wave_fmt->wBitsPerSample);
meta_.block_align = uint16_t(wave_fmt->nBlockAlign);
SetNative(wave_fmt);
}
virtual ~MFAudioData()
{
if (wave_fmt_ != nullptr)
{
::CoTaskMemFree(wave_fmt_);
wave_fmt_ = nullptr;
}
}
WAVEFORMATEX* wave_fmt_;
std::unique_ptr<uint8_t[]> raw_;
};
HRESULT ReadSource(AudioDataPtr& output, IMFSourceReader* reader)
{
HRESULT hr = S_OK;
@ -224,16 +189,6 @@ HRESULT ReadSource(IMFSourceReader* reader, AudioMetadata& output_metadata, std:
uncompressed_type.Get(), &wave_format, &size, (DWORD)MFWaveFormatExConvertFlag_Normal);
}
if (SUCCEEDED(hr))
{
output_metadata.format = AudioFormat::PCM;
output_metadata.channels = uint16_t(wave_format->nChannels);
output_metadata.samples_per_sec = uint32_t(wave_format->nSamplesPerSec);
output_metadata.bits_per_sample = uint16_t(wave_format->wBitsPerSample);
output_metadata.block_align = uint16_t(wave_format->nBlockAlign);
output_metadata.extra_data = wave_format;
}
// ¹ÀËãÒôƵÁ÷´óС
DWORD max_stream_size = 0;
if (SUCCEEDED(hr))
@ -257,7 +212,7 @@ HRESULT ReadSource(IMFSourceReader* reader, AudioMetadata& output_metadata, std:
DWORD flags = 0;
DWORD position = 0;
output_data = std::make_unique<uint8_t[]>(max_stream_size);
auto output_data = std::make_unique<uint8_t[]>(max_stream_size);
ComPtr<IMFSample> sample;
ComPtr<IMFMediaBuffer> buffer;
@ -319,12 +274,8 @@ HRESULT ReadSource(IMFSourceReader* reader, AudioMetadata& output_metadata, std:
if (SUCCEEDED(hr))
{
output_size = uint32_t(position);
}
else
{
output_data.reset();
output_size = 0;
output = new MFAudioData(wave_format, std::move(output_data), uint32_t(position));
return hr;
}
}
}

View File

@ -0,0 +1,54 @@
// Copyright (c) 2016-2018 Kiwano - 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.
#pragma once
#include <kiwano-audio/Transcoder.h>
namespace kiwano
{
namespace audio
{
/**
* \addtogroup Audio
* @{
*/
/**
* \~chinese
* @brief Media Foundation ÒôƵ½âÂëÆ÷
* @detail Ö§³Ö .wav .mp3 µÈ³£¼ûÒôƵÀàÐÍ
*/
class KGE_API MFTranscoder : public Transcoder
{
public:
MFTranscoder();
virtual ~MFTranscoder();
AudioDataPtr Decode(const String& file_path) override;
AudioDataPtr Decode(const Resource& res) override;
};
/** @} */
} // namespace audio
} // namespace kiwano

View File

@ -0,0 +1,71 @@
// Copyright (c) 2016-2018 Kiwano - 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 <kiwano-audio/MediaFoundation/mflib.h>
#include <kiwano/utils/Logger.h>
#include <kiwano/core/Exception.h>
namespace kiwano
{
namespace audio
{
namespace dlls
{
MediaFoundation::MediaFoundation()
: mfplat()
, mfreadwrite()
, MFStartup(nullptr)
, MFShutdown(nullptr)
, MFCreateMediaType(nullptr)
, MFCreateWaveFormatExFromMFMediaType(nullptr)
, MFCreateSourceReaderFromURL(nullptr)
, MFCreateSourceReaderFromByteStream(nullptr)
, MFCreateMFByteStreamOnStream(nullptr)
{
if (mfplat.Load("Mfplat.dll"))
{
MFStartup = mfplat.GetProcess<PFN_MFStartup>("MFStartup");
MFShutdown = mfplat.GetProcess<PFN_MFShutdown>("MFShutdown");
MFCreateMediaType = mfplat.GetProcess<PFN_MFCreateMediaType>("MFCreateMediaType");
MFCreateWaveFormatExFromMFMediaType =
mfplat.GetProcess<PFN_MFCreateWaveFormatExFromMFMediaType>("MFCreateWaveFormatExFromMFMediaType");
MFCreateMFByteStreamOnStream =
mfplat.GetProcess<PFN_MFCreateMFByteStreamOnStream>("MFCreateMFByteStreamOnStream");
}
else
{
KGE_THROW_SYSTEM_ERROR(HRESULT_FROM_WIN32(GetLastError()), "Load Mfplat.dll failed");
}
if (mfreadwrite.Load("Mfreadwrite.dll"))
{
MFCreateSourceReaderFromURL =
mfreadwrite.GetProcess<PFN_MFCreateSourceReaderFromURL>("MFCreateSourceReaderFromURL");
MFCreateSourceReaderFromByteStream =
mfreadwrite.GetProcess<PFN_MFCreateSourceReaderFromByteStream>("MFCreateSourceReaderFromByteStream");
}
else
{
KGE_THROW_SYSTEM_ERROR(HRESULT_FROM_WIN32(GetLastError()), "Load Mfreadwrite.dll failed");
}
}
} // namespace dlls
} // namespace audio
} // namespace kiwano

View File

@ -0,0 +1,79 @@
// Copyright (c) 2016-2018 Kiwano - 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.
#pragma once
#ifndef INITGUID
#define INITGUID // MFAudioFormat_PCM, MF_MT_MAJOR_TYPE, MF_MT_SUBTYPE, MFMediaType_Audio
#endif
#include <kiwano/core/Library.h>
#include <mfapi.h>
#include <mfidl.h>
#include <mfreadwrite.h>
#ifndef KGE_DOXYGEN_DO_NOT_INCLUDE
namespace kiwano
{
namespace audio
{
namespace dlls
{
class KGE_API MediaFoundation
{
public:
static inline MediaFoundation& Get()
{
static MediaFoundation instance;
return instance;
}
// MediaFoundation functions
typedef HRESULT(WINAPI* PFN_MFStartup)(ULONG, DWORD);
typedef HRESULT(WINAPI* PFN_MFShutdown)();
typedef HRESULT(WINAPI* PFN_MFCreateMediaType)(IMFMediaType**);
typedef HRESULT(WINAPI* PFN_MFCreateWaveFormatExFromMFMediaType)(IMFMediaType*, WAVEFORMATEX**, UINT32*, UINT32);
typedef HRESULT(WINAPI* PFN_MFCreateSourceReaderFromURL)(LPCWSTR, IMFAttributes*, IMFSourceReader**);
typedef HRESULT(WINAPI* PFN_MFCreateSourceReaderFromByteStream)(IMFByteStream*, IMFAttributes*, IMFSourceReader**);
typedef HRESULT(WINAPI* PFN_MFCreateMFByteStreamOnStream)(IStream*, IMFByteStream**);
PFN_MFStartup MFStartup;
PFN_MFShutdown MFShutdown;
PFN_MFCreateMediaType MFCreateMediaType;
PFN_MFCreateWaveFormatExFromMFMediaType MFCreateWaveFormatExFromMFMediaType;
PFN_MFCreateSourceReaderFromURL MFCreateSourceReaderFromURL;
PFN_MFCreateSourceReaderFromByteStream MFCreateSourceReaderFromByteStream;
PFN_MFCreateMFByteStreamOnStream MFCreateMFByteStreamOnStream;
private:
MediaFoundation();
MediaFoundation(const MediaFoundation&) = delete;
MediaFoundation& operator=(const MediaFoundation&) = delete;
Library mfplat;
Library mfreadwrite;
};
} // namespace dlls
} // namespace audio
} // namespace kiwano
#endif

View File

@ -21,6 +21,7 @@
#include <kiwano-audio/AudioModule.h>
#include <kiwano-audio/Sound.h>
#include <kiwano/utils/Logger.h>
#include <xaudio2.h>
namespace kiwano
{
@ -30,27 +31,19 @@ namespace audio
Sound::Sound(const String& file_path)
: Sound()
{
TranscoderPtr transcoder = MakePtr<Transcoder>(file_path);
Load(transcoder);
Load(AudioModule::GetInstance().Decode(file_path));
}
Sound::Sound(const Resource& res)
Sound::Sound(const Resource& res, const String& ext)
: Sound()
{
TranscoderPtr transcoder = MakePtr<Transcoder>(res);
Load(transcoder);
Load(AudioModule::GetInstance().Decode(res, ext));
}
Sound::Sound(const BinaryData& data, const AudioMetadata& metadata)
{
TranscoderPtr transcoder = MakePtr<Transcoder>(data, metadata);
Load(transcoder);
}
Sound::Sound(TranscoderPtr transcoder)
Sound::Sound(AudioDataPtr data)
: Sound()
{
Load(transcoder);
Load(data);
}
Sound::Sound()
@ -65,22 +58,18 @@ Sound::~Sound()
Close();
}
bool Sound::Load(TranscoderPtr transcoder)
bool Sound::Load(AudioDataPtr data)
{
if (!transcoder->IsValid())
if (!data)
{
return false;
}
BinaryData data_;
AudioMetadata metadata_;
if (opened_)
{
Close();
}
if (!AudioModule::GetInstance().CreateSound(*this, transcoder->GetMetadata()))
if (!AudioModule::GetInstance().CreateSound(*this, data))
{
return false;
}
@ -88,7 +77,7 @@ bool Sound::Load(TranscoderPtr transcoder)
// reset volume
ResetVolume();
coder_ = transcoder;
data_ = data;
opened_ = true;
return true;
}
@ -101,7 +90,7 @@ void Sound::Play(int loop_count)
return;
}
auto voice = GetNativePtr<IXAudio2SourceVoice>();
auto voice = GetNative<IXAudio2SourceVoice*>();
KGE_ASSERT(voice != nullptr && "IXAudio2SourceVoice* is NULL");
// if sound stream is not empty, stop() will clear it
@ -113,7 +102,7 @@ void Sound::Play(int loop_count)
// clamp loop count
loop_count = (loop_count < 0) ? XAUDIO2_LOOP_INFINITE : std::min(loop_count, XAUDIO2_LOOP_INFINITE - 1);
auto data = coder_->GetData();
auto data = data_->GetData();
XAUDIO2_BUFFER xaudio2_buffer = { 0 };
xaudio2_buffer.pAudioData = reinterpret_cast<BYTE*>(data.buffer);
@ -137,7 +126,7 @@ void Sound::Play(int loop_count)
void Sound::Pause()
{
auto voice = GetNativePtr<IXAudio2SourceVoice>();
auto voice = GetNative<IXAudio2SourceVoice*>();
KGE_ASSERT(voice != nullptr && "IXAudio2SourceVoice* is NULL");
HRESULT hr = voice->Stop();
@ -152,7 +141,7 @@ void Sound::Pause()
void Sound::Resume()
{
auto voice = GetNativePtr<IXAudio2SourceVoice>();
auto voice = GetNative<IXAudio2SourceVoice*>();
KGE_ASSERT(voice != nullptr && "IXAudio2SourceVoice* is NULL");
HRESULT hr = voice->Start();
@ -167,7 +156,7 @@ void Sound::Resume()
void Sound::Stop()
{
auto voice = GetNativePtr<IXAudio2SourceVoice>();
auto voice = GetNative<IXAudio2SourceVoice*>();
KGE_ASSERT(voice != nullptr && "IXAudio2SourceVoice* is NULL");
HRESULT hr = voice->Stop();
@ -189,7 +178,7 @@ void Sound::Stop()
void Sound::Close()
{
auto voice = GetNativePtr<IXAudio2SourceVoice>();
auto voice = GetNative<IXAudio2SourceVoice*>();
if (voice)
{
voice->Stop();
@ -197,7 +186,7 @@ void Sound::Close()
voice->DestroyVoice();
}
coder_ = nullptr;
data_ = nullptr;
opened_ = false;
playing_ = false;
}
@ -209,7 +198,7 @@ bool Sound::IsPlaying() const
if (!playing_)
return false;
auto voice = GetNativePtr<IXAudio2SourceVoice>();
auto voice = GetNative<IXAudio2SourceVoice*>();
if (!voice)
return false;
@ -233,7 +222,7 @@ void Sound::SetVolume(float volume)
}
volume_ = volume;
auto voice = GetNativePtr<IXAudio2SourceVoice>();
auto voice = GetNative<IXAudio2SourceVoice*>();
if (voice)
{
float actual_volume = GetCallbackChain()->OnVolumeChanged(this, volume_);

View File

@ -19,10 +19,8 @@
// THE SOFTWARE.
#pragma once
#include <kiwano-audio/Transcoder.h>
#include <kiwano/core/Resource.h>
#include <kiwano/platform/NativeObject.hpp>
#include <xaudio2.h>
#include <kiwano-audio/AudioData.h>
namespace kiwano
{
@ -94,24 +92,19 @@ class KGE_API Sound : public NativeObject
public:
/// \~chinese
/// @brief 创建音频对象
/// @param res 本地音频文件路径
/// @param file_path 本地音频文件路径
Sound(const String& file_path);
/// \~chinese
/// @brief 创建音频对象
/// @param res 音频资源
Sound(const Resource& res);
/// @param ext 音频类型,决定了使用何种解码器
Sound(const Resource& res, const String& ext = "");
/// \~chinese
/// @brief 创建音频对象
/// @param data 音频数据
/// @param metadata 音频元数据
Sound(const BinaryData& data, const AudioMetadata& metadata);
/// \~chinese
/// @brief 创建音频对象
/// @param transcoder 音频解码器
Sound(TranscoderPtr transcoder);
Sound(AudioDataPtr data);
Sound();
@ -164,20 +157,17 @@ public:
const List<SoundCallbackPtr>& GetCallbacks() const;
protected:
/// \~chinese
/// @brief 加载音频
/// @param transcoder 音频解码器
bool Load(TranscoderPtr transcoder);
bool Load(AudioDataPtr data);
SoundCallbackPtr GetCallbackChain();
void ResetVolume();
private:
bool opened_;
bool playing_;
float volume_;
TranscoderPtr coder_;
bool opened_;
bool playing_;
float volume_;
AudioDataPtr data_;
SoundCallbackPtr callback_chain_;
List<SoundCallbackPtr> callbacks_;

View File

@ -56,14 +56,14 @@ SoundPlayer::SoundPlayer()
SoundPlayer::~SoundPlayer()
{}
TranscoderPtr SoundPlayer::Preload(const String& file_path)
AudioDataPtr SoundPlayer::Preload(const String& file_path)
{
size_t hash_code = std::hash<String>{}(file_path);
if (cache_.count(hash_code))
{
return cache_.at(hash_code);
}
TranscoderPtr ptr = MakePtr<Transcoder>(file_path);
AudioDataPtr ptr = AudioModule::GetInstance().Decode(file_path);
if (ptr)
{
cache_.insert(std::make_pair(hash_code, ptr));
@ -71,14 +71,14 @@ TranscoderPtr SoundPlayer::Preload(const String& file_path)
return ptr;
}
TranscoderPtr SoundPlayer::Preload(const Resource& res)
AudioDataPtr SoundPlayer::Preload(const Resource& res, const String& ext)
{
size_t hash_code = res.GetId();
if (cache_.count(hash_code))
{
return cache_.at(hash_code);
}
TranscoderPtr ptr = MakePtr<Transcoder>(res);
AudioDataPtr ptr = AudioModule::GetInstance().Decode(res, ext);
if (ptr)
{
cache_.insert(std::make_pair(hash_code, ptr));
@ -96,28 +96,16 @@ void SoundPlayer::Play(SoundPtr sound, int loop_count)
}
}
SoundPtr SoundPlayer::Play(const String& file_path, int loop_count, std::initializer_list<SoundCallbackPtr> callbacks)
SoundPtr SoundPlayer::Play(const String& file_path, int loop_count)
{
TranscoderPtr transcoder = Preload(file_path);
SoundPtr sound = new Sound(transcoder);
for (const auto& cb : callbacks)
{
sound->AddCallback(cb);
}
SoundPtr sound = new Sound(Preload(file_path));
Play(sound, loop_count);
return sound;
}
SoundPtr SoundPlayer::Play(const Resource& res, int loop_count, std::initializer_list<SoundCallbackPtr> callbacks)
SoundPtr SoundPlayer::Play(const Resource& res, int loop_count)
{
TranscoderPtr transcoder = Preload(res);
SoundPtr sound = new Sound(transcoder);
for (const auto& cb : callbacks)
{
sound->AddCallback(cb);
}
SoundPtr sound = new Sound(Preload(res));
Play(sound, loop_count);
return sound;
}

View File

@ -48,11 +48,11 @@ public:
/// \~chinese
/// @brief 预加载音频
/// @details
TranscoderPtr Preload(const String& file_path);
AudioDataPtr Preload(const String& file_path);
/// \~chinese
/// @brief 预加载音频资源
TranscoderPtr Preload(const Resource& res);
AudioDataPtr Preload(const Resource& res, const String& ext = "");
/// \~chinese
/// @brief 播放音频
@ -64,15 +64,13 @@ public:
/// @brief 播放音频
/// @param file_path 本地音频文件路径
/// @param loop_count 播放循环次数,设置 -1 为循环播放
/// @param callbacks ×¢²á»Øµ÷
SoundPtr Play(const String& file_path, int loop_count = 0, std::initializer_list<SoundCallbackPtr> callbacks = {});
SoundPtr Play(const String& file_path, int loop_count = 0);
/// \~chinese
/// @brief 播放音频
/// @param res 音频资源
/// @param loop_count 播放循环次数,设置 -1 为循环播放
/// @param callbacks ×¢²á»Øµ÷
SoundPtr Play(const Resource& res, int loop_count = 0, std::initializer_list<SoundCallbackPtr> callbacks = {});
SoundPtr Play(const Resource& res, int loop_count = 0);
/// \~chinese
/// @brief 暂停所有音频
@ -120,7 +118,7 @@ protected:
SoundList trash_;
SoundCallbackPtr callback_;
UnorderedMap<size_t, TranscoderPtr> cache_;
UnorderedMap<size_t, AudioDataPtr> cache_;
};
/** @} */

View File

@ -20,13 +20,12 @@
#pragma once
#include <kiwano/core/Resource.h>
#include <kiwano/base/ObjectBase.h>
#include <kiwano-audio/AudioData.h>
namespace kiwano
{
namespace audio
{
class AudioModule;
KGE_DECLARE_SMART_PTR(Transcoder);
@ -35,73 +34,20 @@ KGE_DECLARE_SMART_PTR(Transcoder);
* @{
*/
/**
* \~chinese
* @brief
*/
enum class AudioFormat
{
PCM,
};
/**
* \~chinese
* @brief
*/
struct AudioMetadata
{
AudioFormat format = AudioFormat::PCM; ///< 音频格式
uint16_t channels = 2; ///< 声道单声道为1立体声为2
uint32_t samples_per_sec = 44100; ///< 采样率11025 表示 11.025kHz。PCM格式的采样率通常为44.1kHz
uint16_t bits_per_sample = 16; ///< 位深PCM格式为 8 或 16
uint16_t block_align = 4; ///< 块对齐PCM格式通常是 (channels * bits_per_sample) / 8
void* extra_data = nullptr; ///< 额外数据,不要设置这个字段
};
/**
* \~chinese
* @brief
*/
class KGE_API Transcoder : public ObjectBase
{
friend class AudioModule;
public:
Transcoder(const String& file_path);
Transcoder() = default;
Transcoder(const Resource& res);
virtual ~Transcoder() = default;
Transcoder(const BinaryData& data, const AudioMetadata& metadata);
virtual AudioDataPtr Decode(const String& file_path) = 0;
virtual ~Transcoder();
/// \~chinese
/// @brief 获取音频元数据
AudioMetadata GetMetadata() const;
/// \~chinese
/// @brief 获取数据
BinaryData GetData() const;
/// \~chinese
/// @brief 清空数据
void Clear();
private:
/// \~chinese
/// @brief 解码本地音频文件
bool Load(const String& file_path);
/// \~chinese
/// @brief 解码音频资源
bool Load(const Resource& res);
bool Load(const BinaryData& data, const AudioMetadata& metadata);
private:
std::unique_ptr<uint8_t[]> raw_;
BinaryData data_;
AudioMetadata metadata_;
virtual AudioDataPtr Decode(const Resource& res) = 0;
};
/** @} */

View File

@ -55,45 +55,6 @@ XAudio2::XAudio2()
KGE_THROW_SYSTEM_ERROR(HRESULT_FROM_WIN32(GetLastError()), "Load xaudio2.dll failed");
}
}
MediaFoundation::MediaFoundation()
: mfplat()
, mfreadwrite()
, MFStartup(nullptr)
, MFShutdown(nullptr)
, MFCreateMediaType(nullptr)
, MFCreateWaveFormatExFromMFMediaType(nullptr)
, MFCreateSourceReaderFromURL(nullptr)
, MFCreateSourceReaderFromByteStream(nullptr)
, MFCreateMFByteStreamOnStream(nullptr)
{
if (mfplat.Load("Mfplat.dll"))
{
MFStartup = mfplat.GetProcess<PFN_MFStartup>("MFStartup");
MFShutdown = mfplat.GetProcess<PFN_MFShutdown>("MFShutdown");
MFCreateMediaType = mfplat.GetProcess<PFN_MFCreateMediaType>("MFCreateMediaType");
MFCreateWaveFormatExFromMFMediaType =
mfplat.GetProcess<PFN_MFCreateWaveFormatExFromMFMediaType>("MFCreateWaveFormatExFromMFMediaType");
MFCreateMFByteStreamOnStream =
mfplat.GetProcess<PFN_MFCreateMFByteStreamOnStream>("MFCreateMFByteStreamOnStream");
}
else
{
KGE_THROW_SYSTEM_ERROR(HRESULT_FROM_WIN32(GetLastError()), "Load Mfplat.dll failed");
}
if (mfreadwrite.Load("Mfreadwrite.dll"))
{
MFCreateSourceReaderFromURL =
mfreadwrite.GetProcess<PFN_MFCreateSourceReaderFromURL>("MFCreateSourceReaderFromURL");
MFCreateSourceReaderFromByteStream =
mfreadwrite.GetProcess<PFN_MFCreateSourceReaderFromByteStream>("MFCreateSourceReaderFromByteStream");
}
else
{
KGE_THROW_SYSTEM_ERROR(HRESULT_FROM_WIN32(GetLastError()), "Load Mfreadwrite.dll failed");
}
}
} // namespace dlls
} // namespace audio
} // namespace kiwano

View File

@ -20,9 +20,6 @@
#pragma once
#include <kiwano/core/Library.h>
#include <mfapi.h>
#include <mfidl.h>
#include <mfreadwrite.h>
#include <xaudio2.h>
#ifndef KGE_DOXYGEN_DO_NOT_INCLUDE
@ -55,42 +52,6 @@ private:
Library xaudio2;
};
class KGE_API MediaFoundation
{
public:
static inline MediaFoundation& Get()
{
static MediaFoundation instance;
return instance;
}
// MediaFoundation functions
typedef HRESULT(WINAPI* PFN_MFStartup)(ULONG, DWORD);
typedef HRESULT(WINAPI* PFN_MFShutdown)();
typedef HRESULT(WINAPI* PFN_MFCreateMediaType)(IMFMediaType**);
typedef HRESULT(WINAPI* PFN_MFCreateWaveFormatExFromMFMediaType)(IMFMediaType*, WAVEFORMATEX**, UINT32*, UINT32);
typedef HRESULT(WINAPI* PFN_MFCreateSourceReaderFromURL)(LPCWSTR, IMFAttributes*, IMFSourceReader**);
typedef HRESULT(WINAPI* PFN_MFCreateSourceReaderFromByteStream)(IMFByteStream*, IMFAttributes*, IMFSourceReader**);
typedef HRESULT(WINAPI* PFN_MFCreateMFByteStreamOnStream)(IStream*, IMFByteStream**);
PFN_MFStartup MFStartup;
PFN_MFShutdown MFShutdown;
PFN_MFCreateMediaType MFCreateMediaType;
PFN_MFCreateWaveFormatExFromMFMediaType MFCreateWaveFormatExFromMFMediaType;
PFN_MFCreateSourceReaderFromURL MFCreateSourceReaderFromURL;
PFN_MFCreateSourceReaderFromByteStream MFCreateSourceReaderFromByteStream;
PFN_MFCreateMFByteStreamOnStream MFCreateMFByteStreamOnStream;
private:
MediaFoundation();
MediaFoundation(const MediaFoundation&) = delete;
MediaFoundation& operator=(const MediaFoundation&) = delete;
Library mfplat;
Library mfreadwrite;
};
} // namespace dlls
} // namespace audio
} // namespace kiwano

View File

@ -130,6 +130,16 @@ String FileSystem::GetFullPathForFile(const String& file) const
return "";
}
String FileSystem::GetFileExt(const String& file) const
{
const auto pos = file.find_last_of(".");
if (pos == String::npos)
{
return "";
}
return file.substr(pos + 1);
}
void FileSystem::AddFileLookupRule(const String& key, const String& file_path)
{
file_lookup_dict_.emplace(key, ConvertPathFormat(file_path));

View File

@ -54,6 +54,14 @@ public:
*/
String GetFullPathForFile(const String& file) const;
/**
* \~chinese
* @brief
* @param file
* @return .
*/
String GetFileExt(const String& file) const;
/**
* \~chinese
* @brief

View File

@ -40,9 +40,6 @@ public:
template <class _Ty>
_Ty GetNative() const;
template<class _Ty>
_Ty* GetNativePtr() const;
void SetNative(const Any& native);
void ResetNative();
@ -65,17 +62,7 @@ inline _Ty NativeObject::GetNative() const
{
return native_.Cast<_Ty>();
}
return nullptr;
}
template <class _Ty>
inline _Ty* NativeObject::GetNativePtr() const
{
if (native_.HasValue())
{
return native_.Cast<_Ty*>();
}
return nullptr;
return _Ty{};
}
inline void NativeObject::SetNative(const Any& native)