2018-11-08 21:39:26 +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.
|
|
|
|
|
|
|
|
|
|
|
|
#include "Transcoder.h"
|
2018-11-16 15:53:39 +08:00
|
|
|
|
#include "../base/base.hpp"
|
2018-11-08 21:39:26 +08:00
|
|
|
|
#include "../base/modules.h"
|
|
|
|
|
|
#include "../base/logs.h"
|
|
|
|
|
|
#include <shlwapi.h>
|
|
|
|
|
|
|
|
|
|
|
|
namespace easy2d
|
|
|
|
|
|
{
|
|
|
|
|
|
Transcoder::Transcoder()
|
|
|
|
|
|
: wave_format_(nullptr)
|
|
|
|
|
|
{
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Transcoder::~Transcoder()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (wave_format_)
|
|
|
|
|
|
{
|
|
|
|
|
|
::CoTaskMemFree(wave_format_);
|
|
|
|
|
|
wave_format_ = nullptr;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
WAVEFORMATEX* Transcoder::GetWaveFormatEx() const
|
|
|
|
|
|
{
|
|
|
|
|
|
return wave_format_;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-11-12 20:46:54 +08:00
|
|
|
|
HRESULT Transcoder::LoadMediaFile(LPCWSTR file_path, BYTE** wave_data, UINT32* wave_data_size)
|
2018-11-08 21:39:26 +08:00
|
|
|
|
{
|
|
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
|
|
|
|
|
|
|
|
IMFSourceReader* reader = nullptr;
|
|
|
|
|
|
|
|
|
|
|
|
hr = modules::MediaFoundation.MFCreateSourceReaderFromURL(
|
|
|
|
|
|
file_path,
|
|
|
|
|
|
nullptr,
|
|
|
|
|
|
&reader
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
if (SUCCEEDED(hr))
|
|
|
|
|
|
{
|
|
|
|
|
|
hr = ReadSource(reader, wave_data, wave_data_size);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
SafeRelease(reader);
|
|
|
|
|
|
|
2018-11-12 20:46:54 +08:00
|
|
|
|
return hr;
|
2018-11-08 21:39:26 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2018-11-12 23:32:50 +08:00
|
|
|
|
HRESULT Transcoder::LoadMediaResource(const Resource& res, BYTE** wave_data, UINT32* wave_data_size)
|
2018-11-08 21:39:26 +08:00
|
|
|
|
{
|
|
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
|
|
|
|
|
|
|
|
HINSTANCE hinstance = GetModuleHandle(nullptr);
|
|
|
|
|
|
IStream* stream = nullptr;
|
|
|
|
|
|
IMFByteStream* byte_stream = nullptr;
|
|
|
|
|
|
IMFSourceReader* reader = nullptr;
|
|
|
|
|
|
|
2018-11-12 23:32:50 +08:00
|
|
|
|
ResourceData buffer;
|
|
|
|
|
|
if (!res.Load(&buffer)) { return false; }
|
2018-11-08 21:39:26 +08:00
|
|
|
|
|
|
|
|
|
|
stream = SHCreateMemStream(
|
2018-11-12 23:32:50 +08:00
|
|
|
|
static_cast<const BYTE*>(buffer.buffer),
|
|
|
|
|
|
static_cast<UINT>(buffer.buffer_size)
|
2018-11-08 21:39:26 +08:00
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
if (stream == nullptr)
|
|
|
|
|
|
{
|
2018-11-15 17:59:18 +08:00
|
|
|
|
logs::Errorln("SHCreateMemStream");
|
2018-11-12 20:46:54 +08:00
|
|
|
|
return E_OUTOFMEMORY;
|
2018-11-08 21:39:26 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (SUCCEEDED(hr))
|
|
|
|
|
|
{
|
|
|
|
|
|
hr = modules::MediaFoundation.MFCreateMFByteStreamOnStream(stream, &byte_stream);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (SUCCEEDED(hr))
|
|
|
|
|
|
{
|
|
|
|
|
|
hr = modules::MediaFoundation.MFCreateSourceReaderFromByteStream(
|
|
|
|
|
|
byte_stream,
|
|
|
|
|
|
nullptr,
|
|
|
|
|
|
&reader
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (SUCCEEDED(hr))
|
|
|
|
|
|
{
|
|
|
|
|
|
hr = ReadSource(reader, wave_data, wave_data_size);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
SafeRelease(stream);
|
|
|
|
|
|
SafeRelease(byte_stream);
|
|
|
|
|
|
SafeRelease(reader);
|
|
|
|
|
|
|
2018-11-12 20:46:54 +08:00
|
|
|
|
return hr;
|
2018-11-08 21:39:26 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
HRESULT Transcoder::ReadSource(IMFSourceReader* reader, BYTE** wave_data, UINT32* wave_data_size)
|
|
|
|
|
|
{
|
|
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
|
|
DWORD max_stream_size = 0;
|
|
|
|
|
|
|
|
|
|
|
|
IMFMediaType* partial_type = nullptr;
|
|
|
|
|
|
IMFMediaType* uncompressed_type = nullptr;
|
|
|
|
|
|
|
|
|
|
|
|
hr = modules::MediaFoundation.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(
|
|
|
|
|
|
MF_SOURCE_READER_FIRST_AUDIO_STREAM,
|
|
|
|
|
|
0,
|
|
|
|
|
|
partial_type
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// <20><> IMFMediaType <20>л<EFBFBD>ȡ WAVEFORMAT <20>ṹ
|
|
|
|
|
|
if (SUCCEEDED(hr))
|
|
|
|
|
|
{
|
|
|
|
|
|
hr = reader->GetCurrentMediaType(
|
|
|
|
|
|
MF_SOURCE_READER_FIRST_AUDIO_STREAM,
|
|
|
|
|
|
&uncompressed_type
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ָ<><D6B8><EFBFBD><EFBFBD>Ƶ<EFBFBD><C6B5>
|
|
|
|
|
|
if (SUCCEEDED(hr))
|
|
|
|
|
|
{
|
|
|
|
|
|
hr = reader->SetStreamSelection(
|
|
|
|
|
|
MF_SOURCE_READER_FIRST_AUDIO_STREAM,
|
|
|
|
|
|
true
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// <20><>ȡ WAVEFORMAT <20><><EFBFBD><EFBFBD>
|
|
|
|
|
|
if (SUCCEEDED(hr))
|
|
|
|
|
|
{
|
|
|
|
|
|
UINT32 size = 0;
|
|
|
|
|
|
hr = modules::MediaFoundation.MFCreateWaveFormatExFromMFMediaType(
|
|
|
|
|
|
uncompressed_type,
|
|
|
|
|
|
&wave_format_,
|
|
|
|
|
|
&size,
|
|
|
|
|
|
MFWaveFormatExConvertFlag_Normal
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ƶ<EFBFBD><C6B5><EFBFBD><EFBFBD>С
|
|
|
|
|
|
if (SUCCEEDED(hr))
|
|
|
|
|
|
{
|
|
|
|
|
|
PROPVARIANT prop;
|
|
|
|
|
|
PropVariantInit(&prop);
|
|
|
|
|
|
|
|
|
|
|
|
hr = reader->GetPresentationAttribute(
|
|
|
|
|
|
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
|
|
|
|
|
|
);
|
|
|
|
|
|
PropVariantClear(&prop);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// <20><>ȡ<EFBFBD><C8A1>Ƶ<EFBFBD><C6B5><EFBFBD><EFBFBD>
|
|
|
|
|
|
if (SUCCEEDED(hr))
|
|
|
|
|
|
{
|
|
|
|
|
|
DWORD flags = 0;
|
|
|
|
|
|
DWORD position = 0;
|
|
|
|
|
|
|
|
|
|
|
|
IMFSample* sample = nullptr;
|
|
|
|
|
|
IMFMediaBuffer* buffer = nullptr;
|
|
|
|
|
|
BYTE* data = new (std::nothrow) BYTE[max_stream_size];
|
|
|
|
|
|
|
|
|
|
|
|
if (data == nullptr)
|
|
|
|
|
|
{
|
2018-11-15 17:59:18 +08:00
|
|
|
|
logs::Errorln("Low memory");
|
2018-11-08 21:39:26 +08:00
|
|
|
|
hr = E_OUTOFMEMORY;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
while (true)
|
|
|
|
|
|
{
|
|
|
|
|
|
hr = reader->ReadSample(
|
|
|
|
|
|
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))
|
|
|
|
|
|
{
|
|
|
|
|
|
BYTE *audio_data = nullptr;
|
|
|
|
|
|
DWORD sample_buffer_length = 0;
|
|
|
|
|
|
|
|
|
|
|
|
hr = buffer->Lock(
|
|
|
|
|
|
&audio_data,
|
|
|
|
|
|
nullptr,
|
|
|
|
|
|
&sample_buffer_length
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
if (SUCCEEDED(hr))
|
|
|
|
|
|
{
|
|
|
|
|
|
for (DWORD i = 0; i < sample_buffer_length; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
data[position++] = audio_data[i];
|
|
|
|
|
|
}
|
|
|
|
|
|
hr = buffer->Unlock();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
SafeRelease(buffer);
|
|
|
|
|
|
}
|
|
|
|
|
|
SafeRelease(sample);
|
|
|
|
|
|
|
|
|
|
|
|
if (FAILED(hr)) { break; }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (SUCCEEDED(hr))
|
|
|
|
|
|
{
|
|
|
|
|
|
*wave_data = data;
|
|
|
|
|
|
*wave_data_size = position;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
SafeRelease(partial_type);
|
|
|
|
|
|
SafeRelease(uncompressed_type);
|
|
|
|
|
|
|
|
|
|
|
|
return hr;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|