Magic_Game/src/kiwano-audio/Transcoder.cpp

299 lines
6.4 KiB
C++
Raw Normal View History

2019-07-30 10:46:01 +08:00
// 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.
#ifndef INITGUID
# define INITGUID // MFAudioFormat_PCM, MF_MT_MAJOR_TYPE, MF_MT_SUBTYPE, MFMediaType_Audio
#endif
#include <kiwano/macros.h>
2019-12-23 18:05:08 +08:00
#include <kiwano/core/common.h>
2019-11-13 14:33:15 +08:00
#include <kiwano/core/Resource.h>
#include <kiwano/core/Logger.h>
#include <kiwano/core/win32/ComPtr.hpp>
#include <kiwano/platform/modules.h>
2019-10-11 21:55:29 +08:00
#include <kiwano-audio/audio-modules.h>
#include <kiwano-audio/Transcoder.h>
2019-07-30 10:46:01 +08:00
namespace kiwano
{
namespace audio
{
Transcoder::Transcoder()
: wave_format_(nullptr)
2019-08-19 09:28:59 +08:00
, wave_data_(nullptr)
, wave_size_(0)
2019-07-30 10:46:01 +08:00
{
}
Transcoder::~Transcoder()
2019-08-19 09:28:59 +08:00
{
ClearBuffer();
}
Transcoder::Buffer Transcoder::GetBuffer() const
{
return Buffer{ wave_data_, wave_size_, wave_format_ };
}
void Transcoder::ClearBuffer()
2019-07-30 10:46:01 +08:00
{
if (wave_format_)
{
::CoTaskMemFree(wave_format_);
wave_format_ = nullptr;
}
2019-08-19 09:28:59 +08:00
if (wave_data_)
{
delete[] wave_data_;
wave_data_ = nullptr;
}
wave_size_ = 0;
2019-07-30 10:46:01 +08:00
}
2019-08-19 09:28:59 +08:00
HRESULT Transcoder::LoadMediaFile(String const& file_path)
2019-07-30 10:46:01 +08:00
{
HRESULT hr = S_OK;
ComPtr<IMFSourceReader> reader;
hr = modules::MediaFoundation::Get().MFCreateSourceReaderFromURL(
file_path.c_str(),
nullptr,
&reader
);
if (SUCCEEDED(hr))
{
2019-08-19 09:28:59 +08:00
hr = ReadSource(reader.get());
2019-07-30 10:46:01 +08:00
}
return hr;
}
2019-08-19 09:28:59 +08:00
HRESULT Transcoder::LoadMediaResource(Resource const& res)
2019-07-30 10:46:01 +08:00
{
HRESULT hr = S_OK;
ComPtr<IStream> stream;
ComPtr<IMFByteStream> byte_stream;
ComPtr<IMFSourceReader> reader;
2019-08-18 17:49:13 +08:00
Resource::Data data = res.GetData();
if (!data) { return E_FAIL; }
2019-07-30 10:46:01 +08:00
stream = kiwano::modules::Shlwapi::Get().SHCreateMemStream(
2019-09-29 22:23:13 +08:00
static_cast<const BYTE*>(data.buffer),
2019-10-12 11:26:41 +08:00
static_cast<uint32_t>(data.size)
2019-07-30 10:46:01 +08:00
);
if (stream == nullptr)
{
2019-12-17 20:47:55 +08:00
KGE_ERROR(L"SHCreateMemStream failed");
2019-07-30 10:46:01 +08:00
return E_OUTOFMEMORY;
}
if (SUCCEEDED(hr))
{
2019-08-13 21:16:38 +08:00
hr = modules::MediaFoundation::Get().MFCreateMFByteStreamOnStream(stream.get(), &byte_stream);
2019-07-30 10:46:01 +08:00
}
if (SUCCEEDED(hr))
{
hr = modules::MediaFoundation::Get().MFCreateSourceReaderFromByteStream(
2019-08-13 21:16:38 +08:00
byte_stream.get(),
2019-07-30 10:46:01 +08:00
nullptr,
&reader
);
}
if (SUCCEEDED(hr))
{
2019-08-19 09:28:59 +08:00
hr = ReadSource(reader.get());
2019-07-30 10:46:01 +08:00
}
return hr;
}
2019-08-19 09:28:59 +08:00
HRESULT Transcoder::ReadSource(IMFSourceReader* reader)
2019-07-30 10:46:01 +08:00
{
HRESULT hr = S_OK;
DWORD max_stream_size = 0;
ComPtr<IMFMediaType> partial_type;
ComPtr<IMFMediaType> uncompressed_type;
hr = modules::MediaFoundation::Get().MFCreateMediaType(&partial_type);
if (SUCCEEDED(hr))
{
hr = partial_type->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio);
}
if (SUCCEEDED(hr))
{
hr = partial_type->SetGUID(MF_MT_SUBTYPE, MFAudioFormat_PCM);
}
// <20><><EFBFBD><EFBFBD> source reader <20><>ý<EFBFBD><C3BD><EFBFBD><EFBFBD><EFBFBD>ͣ<EFBFBD><CDA3><EFBFBD><EFBFBD><EFBFBD>ʹ<EFBFBD>ú<EFBFBD><C3BA>ʵĽ<CAB5><C4BD><EFBFBD><EFBFBD><EFBFBD>ȥ<EFBFBD><C8A5><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ƶ
if (SUCCEEDED(hr))
{
hr = reader->SetCurrentMediaType(
(DWORD)MF_SOURCE_READER_FIRST_AUDIO_STREAM,
0,
2019-08-13 21:16:38 +08:00
partial_type.get()
2019-07-30 10:46:01 +08:00
);
}
// <20><> IMFMediaType <20>л<EFBFBD>ȡ WAVEFORMAT <20>
if (SUCCEEDED(hr))
{
hr = reader->GetCurrentMediaType(
(DWORD)MF_SOURCE_READER_FIRST_AUDIO_STREAM,
&uncompressed_type
);
}
// ָ<><D6B8><EFBFBD><EFBFBD>Ƶ<EFBFBD><C6B5>
if (SUCCEEDED(hr))
{
hr = reader->SetStreamSelection(
(DWORD)MF_SOURCE_READER_FIRST_AUDIO_STREAM,
true
);
}
// <20><>ȡ WAVEFORMAT <20><><EFBFBD><EFBFBD>
if (SUCCEEDED(hr))
{
2019-10-12 11:26:41 +08:00
uint32_t size = 0;
2019-07-30 10:46:01 +08:00
hr = modules::MediaFoundation::Get().MFCreateWaveFormatExFromMFMediaType(
2019-08-13 21:16:38 +08:00
uncompressed_type.get(),
2019-07-30 10:46:01 +08:00
&wave_format_,
&size,
(DWORD)MFWaveFormatExConvertFlag_Normal
);
}
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ƶ<EFBFBD><C6B5><EFBFBD><EFBFBD>С
if (SUCCEEDED(hr))
{
PROPVARIANT prop;
PropVariantInit(&prop);
hr = reader->GetPresentationAttribute(
(DWORD)MF_SOURCE_READER_MEDIASOURCE,
MF_PD_DURATION,
&prop
);
LONGLONG duration = prop.uhVal.QuadPart;
max_stream_size = static_cast<DWORD>(
(duration * wave_format_->nAvgBytesPerSec) / 10000000 + 1
2019-08-19 09:28:59 +08:00
);
2019-07-30 10:46:01 +08:00
PropVariantClear(&prop);
}
// <20><>ȡ<EFBFBD><C8A1>Ƶ<EFBFBD><C6B5><EFBFBD><EFBFBD>
if (SUCCEEDED(hr))
{
DWORD flags = 0;
DWORD position = 0;
2019-09-29 22:23:13 +08:00
BYTE* data = new (std::nothrow) BYTE[max_stream_size];
2019-07-30 10:46:01 +08:00
ComPtr<IMFSample> sample;
ComPtr<IMFMediaBuffer> buffer;
if (data == nullptr)
{
2019-12-17 20:47:55 +08:00
KGE_ERROR(L"Low memory");
2019-07-30 10:46:01 +08:00
hr = E_OUTOFMEMORY;
}
else
{
while (true)
{
hr = reader->ReadSample(
(DWORD)MF_SOURCE_READER_FIRST_AUDIO_STREAM,
0,
nullptr,
&flags,
nullptr,
&sample
);
if (flags & MF_SOURCE_READERF_ENDOFSTREAM) { break; }
if (sample == nullptr) { continue; }
if (SUCCEEDED(hr))
{
hr = sample->ConvertToContiguousBuffer(&buffer);
if (SUCCEEDED(hr))
{
2019-09-29 22:23:13 +08:00
BYTE* audio_data = nullptr;
2019-07-30 10:46:01 +08:00
DWORD sample_buffer_length = 0;
hr = buffer->Lock(
&audio_data,
nullptr,
&sample_buffer_length
);
2019-09-26 15:10:28 +08:00
if (position + sample_buffer_length >= max_stream_size)
2019-07-30 10:46:01 +08:00
{
2019-09-26 15:10:28 +08:00
hr = E_FAIL;
}
if (SUCCEEDED(hr))
{
::memcpy(data + position, audio_data, sample_buffer_length);
position += sample_buffer_length;
2019-07-30 10:46:01 +08:00
hr = buffer->Unlock();
}
}
buffer = nullptr;
}
sample = nullptr;
if (FAILED(hr)) { break; }
}
if (SUCCEEDED(hr))
{
2019-08-19 09:28:59 +08:00
wave_data_ = data;
wave_size_ = position;
2019-07-30 10:46:01 +08:00
}
2019-09-26 15:10:28 +08:00
else
{
delete[] data;
data = nullptr;
}
2019-07-30 10:46:01 +08:00
}
}
return hr;
}
}
}