audio_core: hle: mf: address reviews from @B3N30
This commit is contained in:
parent
11e277149c
commit
7f5b54fda4
|
@ -30,7 +30,7 @@ add_library(audio_core STATIC
|
||||||
$<$<BOOL:${SDL2_FOUND}>:sdl2_sink.cpp sdl2_sink.h>
|
$<$<BOOL:${SDL2_FOUND}>:sdl2_sink.cpp sdl2_sink.h>
|
||||||
$<$<BOOL:${ENABLE_CUBEB}>:cubeb_sink.cpp cubeb_sink.h>
|
$<$<BOOL:${ENABLE_CUBEB}>:cubeb_sink.cpp cubeb_sink.h>
|
||||||
$<$<BOOL:${FFMPEG_FOUND}>:hle/ffmpeg_decoder.cpp hle/ffmpeg_decoder.h hle/ffmpeg_dl.cpp hle/ffmpeg_dl.h>
|
$<$<BOOL:${FFMPEG_FOUND}>:hle/ffmpeg_decoder.cpp hle/ffmpeg_decoder.h hle/ffmpeg_dl.cpp hle/ffmpeg_dl.h>
|
||||||
$<$<BOOL:${ENABLE_MF}>:hle/wmf_decoder.cpp hle/wmf_decoder.h hle/wmf_decoder_utils.cpp hle/wmf_decoder_utils.h hle/adts_reader.c>
|
$<$<BOOL:${ENABLE_MF}>:hle/wmf_decoder.cpp hle/wmf_decoder.h hle/wmf_decoder_utils.cpp hle/wmf_decoder_utils.h hle/adts_reader.cpp>
|
||||||
)
|
)
|
||||||
|
|
||||||
create_target_directory_groups(audio_core)
|
create_target_directory_groups(audio_core)
|
||||||
|
|
|
@ -1,20 +1,20 @@
|
||||||
|
// Copyright 2019 Citra Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
#pragma once
|
#pragma once
|
||||||
#ifndef ADTS_ADT
|
|
||||||
#define ADTS_ADT
|
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <array>
|
||||||
#include <stdint.h>
|
#include "common/common_types.h"
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
struct ADTSData {
|
struct ADTSData {
|
||||||
bool MPEG2;
|
bool MPEG2;
|
||||||
uint8_t profile;
|
u8 profile;
|
||||||
uint8_t channels;
|
u8 channels;
|
||||||
uint8_t channel_idx;
|
u8 channel_idx;
|
||||||
uint8_t framecount;
|
u8 framecount;
|
||||||
uint8_t samplerate_idx;
|
u8 samplerate_idx;
|
||||||
uint32_t length;
|
u32 length;
|
||||||
uint32_t samplerate;
|
u32 samplerate;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct ADTSData ADTSData;
|
typedef struct ADTSData ADTSData;
|
||||||
|
@ -22,10 +22,9 @@ typedef struct ADTSData ADTSData;
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif // __cplusplus
|
#endif // __cplusplus
|
||||||
uint32_t parse_adts(char* buffer, struct ADTSData* out);
|
u32 parse_adts(char* buffer, struct ADTSData* out);
|
||||||
// last two bytes of MF AAC decoder user data
|
// last two bytes of MF AAC decoder user data
|
||||||
uint16_t mf_get_aac_tag(struct ADTSData input);
|
u16 mf_get_aac_tag(struct ADTSData input);
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif // __cplusplus
|
#endif // __cplusplus
|
||||||
#endif // ADTS_ADT
|
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
|
// Copyright 2019 Citra Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
#include "adts.h"
|
#include "adts.h"
|
||||||
|
|
||||||
const uint32_t freq_table[16] = {96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050,
|
constexpr std::array<u32, 16> freq_table = {96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050,
|
||||||
16000, 12000, 11025, 8000, 7350, 0, 0, 0};
|
16000, 12000, 11025, 8000, 7350, 0, 0, 0};
|
||||||
const short channel_table[8] = {0, 1, 2, 3, 4, 5, 6, 8};
|
constexpr std::array<u8, 8> channel_table = {0, 1, 2, 3, 4, 5, 6, 8};
|
||||||
|
|
||||||
uint32_t parse_adts(char* buffer, struct ADTSData* out) {
|
u32 parse_adts(char* buffer, struct ADTSData* out) {
|
||||||
uint32_t tmp = 0;
|
u32 tmp = 0;
|
||||||
|
|
||||||
// sync word 0xfff
|
// sync word 0xfff
|
||||||
tmp = (buffer[0] << 8) | (buffer[1] & 0xf0);
|
tmp = (buffer[0] << 8) | (buffer[1] & 0xf0);
|
||||||
|
@ -38,8 +40,8 @@ uint32_t parse_adts(char* buffer, struct ADTSData* out) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// last two bytes of MF AAC decoder user data
|
// last two bytes of MF AAC decoder user data
|
||||||
uint16_t mf_get_aac_tag(struct ADTSData input) {
|
u16 mf_get_aac_tag(struct ADTSData input) {
|
||||||
uint16_t tag = 0;
|
u16 tag = 0;
|
||||||
|
|
||||||
tag |= input.profile << 11;
|
tag |= input.profile << 11;
|
||||||
tag |= input.samplerate_idx << 7;
|
tag |= input.samplerate_idx << 7;
|
|
@ -27,13 +27,13 @@ private:
|
||||||
|
|
||||||
Memory::MemorySystem& memory;
|
Memory::MemorySystem& memory;
|
||||||
|
|
||||||
IMFTransform* transform = NULL;
|
IMFTransform* transform = nullptr;
|
||||||
DWORD in_stream_id = 0;
|
DWORD in_stream_id = 0;
|
||||||
DWORD out_stream_id = 0;
|
DWORD out_stream_id = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
WMFDecoder::Impl::Impl(Memory::MemorySystem& memory) : memory(memory) {
|
WMFDecoder::Impl::Impl(Memory::MemorySystem& memory) : memory(memory) {
|
||||||
mf_coinit();
|
MFCoInit();
|
||||||
}
|
}
|
||||||
|
|
||||||
WMFDecoder::Impl::~Impl() = default;
|
WMFDecoder::Impl::~Impl() = default;
|
||||||
|
@ -46,7 +46,7 @@ std::optional<BinaryResponse> WMFDecoder::Impl::ProcessRequest(const BinaryReque
|
||||||
|
|
||||||
switch (request.cmd) {
|
switch (request.cmd) {
|
||||||
case DecoderCommand::Init: {
|
case DecoderCommand::Init: {
|
||||||
LOG_INFO(Audio_DSP, "AACDecoder initializing");
|
LOG_INFO(Audio_DSP, "WMFDecoder initializing");
|
||||||
return Initalize(request);
|
return Initalize(request);
|
||||||
}
|
}
|
||||||
case DecoderCommand::Decode: {
|
case DecoderCommand::Decode: {
|
||||||
|
@ -73,7 +73,7 @@ std::optional<BinaryResponse> WMFDecoder::Impl::Initalize(const BinaryRequest& r
|
||||||
std::memcpy(&response, &request, sizeof(response));
|
std::memcpy(&response, &request, sizeof(response));
|
||||||
response.unknown1 = 0x0;
|
response.unknown1 = 0x0;
|
||||||
|
|
||||||
if (mf_decoder_init(&transform) != 0) {
|
if (MFDecoderInit(&transform) != 0) {
|
||||||
LOG_CRITICAL(Audio_DSP, "Can't init decoder");
|
LOG_CRITICAL(Audio_DSP, "Can't init decoder");
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
@ -95,8 +95,8 @@ std::optional<BinaryResponse> WMFDecoder::Impl::Initalize(const BinaryRequest& r
|
||||||
|
|
||||||
void WMFDecoder::Impl::Clear() {
|
void WMFDecoder::Impl::Clear() {
|
||||||
if (initalized) {
|
if (initalized) {
|
||||||
mf_flush(&transform);
|
MFFlush(&transform);
|
||||||
mf_deinit(&transform);
|
MFDeInit(&transform);
|
||||||
}
|
}
|
||||||
initalized = false;
|
initalized = false;
|
||||||
selected = false;
|
selected = false;
|
||||||
|
@ -105,16 +105,16 @@ void WMFDecoder::Impl::Clear() {
|
||||||
int WMFDecoder::Impl::DecodingLoop(ADTSData adts_header,
|
int WMFDecoder::Impl::DecodingLoop(ADTSData adts_header,
|
||||||
std::array<std::vector<u8>, 2>& out_streams) {
|
std::array<std::vector<u8>, 2>& out_streams) {
|
||||||
int output_status = 0;
|
int output_status = 0;
|
||||||
char* output_buffer = NULL;
|
char* output_buffer = nullptr;
|
||||||
DWORD output_len = 0;
|
DWORD output_len = 0;
|
||||||
IMFSample* output = NULL;
|
IMFSample* output = nullptr;
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
output_status = receive_sample(transform, out_stream_id, &output);
|
output_status = ReceiveSample(transform, out_stream_id, &output);
|
||||||
|
|
||||||
// 0 -> okay; 3 -> okay but more data available (buffer too small)
|
// 0 -> okay; 3 -> okay but more data available (buffer too small)
|
||||||
if (output_status == 0 || output_status == 3) {
|
if (output_status == 0 || output_status == 3) {
|
||||||
copy_sample_to_buffer(output, (void**)&output_buffer, &output_len);
|
CopySampleToBuffer(output, (void**)&output_buffer, &output_len);
|
||||||
|
|
||||||
// the following was taken from ffmpeg version of the decoder
|
// the following was taken from ffmpeg version of the decoder
|
||||||
f32 val_f32;
|
f32 val_f32;
|
||||||
|
@ -174,12 +174,12 @@ std::optional<BinaryResponse> WMFDecoder::Impl::Decode(const BinaryRequest& requ
|
||||||
u8* data = memory.GetFCRAMPointer(request.src_addr - Memory::FCRAM_PADDR);
|
u8* data = memory.GetFCRAMPointer(request.src_addr - Memory::FCRAM_PADDR);
|
||||||
|
|
||||||
std::array<std::vector<u8>, 2> out_streams;
|
std::array<std::vector<u8>, 2> out_streams;
|
||||||
IMFSample* sample = NULL;
|
IMFSample* sample = nullptr;
|
||||||
ADTSData adts_header;
|
ADTSData adts_header;
|
||||||
char* aac_tag = (char*)calloc(1, 14);
|
char* aac_tag = (char*)calloc(1, 14);
|
||||||
int input_status = 0;
|
int input_status = 0;
|
||||||
|
|
||||||
if (detect_mediatype((char*)data, request.size, &adts_header, &aac_tag) != 0) {
|
if (DetectMediaType((char*)data, request.size, &adts_header, &aac_tag) != 0) {
|
||||||
LOG_ERROR(Audio_DSP, "Unable to deduce decoding parameters from ADTS stream");
|
LOG_ERROR(Audio_DSP, "Unable to deduce decoding parameters from ADTS stream");
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
@ -187,23 +187,23 @@ std::optional<BinaryResponse> WMFDecoder::Impl::Decode(const BinaryRequest& requ
|
||||||
if (!selected) {
|
if (!selected) {
|
||||||
LOG_DEBUG(Audio_DSP, "New ADTS stream: channels = {}, sample rate = {}",
|
LOG_DEBUG(Audio_DSP, "New ADTS stream: channels = {}, sample rate = {}",
|
||||||
adts_header.channels, adts_header.samplerate);
|
adts_header.channels, adts_header.samplerate);
|
||||||
select_input_mediatype(transform, in_stream_id, adts_header, (UINT8*)aac_tag, 14);
|
SelectInputMediaType(transform, in_stream_id, adts_header, (UINT8*)aac_tag, 14);
|
||||||
select_output_mediatype(transform, out_stream_id);
|
SelectOutputMediaType(transform, out_stream_id);
|
||||||
send_sample(transform, in_stream_id, NULL);
|
SendSample(transform, in_stream_id, nullptr);
|
||||||
// cache the result from detect_mediatype and call select_*_mediatype only once
|
// cache the result from detect_mediatype and call select_*_mediatype only once
|
||||||
// This could increase performance very slightly
|
// This could increase performance very slightly
|
||||||
transform->ProcessMessage(MFT_MESSAGE_NOTIFY_BEGIN_STREAMING, 0);
|
transform->ProcessMessage(MFT_MESSAGE_NOTIFY_BEGIN_STREAMING, 0);
|
||||||
selected = true;
|
selected = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
sample = create_sample((void*)data, request.size, 1, 0);
|
sample = CreateSample((void*)data, request.size, 1, 0);
|
||||||
sample->SetUINT32(MFSampleExtension_CleanPoint, 1);
|
sample->SetUINT32(MFSampleExtension_CleanPoint, 1);
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
input_status = send_sample(transform, in_stream_id, sample);
|
input_status = SendSample(transform, in_stream_id, sample);
|
||||||
|
|
||||||
if (DecodingLoop(adts_header, out_streams) < 0) {
|
if (DecodingLoop(adts_header, out_streams) < 0) {
|
||||||
// if the decode issues is caused by MFT not accepting new samples, try again
|
// if the decode issues are caused by MFT not accepting new samples, try again
|
||||||
// NOTICE: you are required to check the output even if you already knew/guessed
|
// NOTICE: you are required to check the output even if you already knew/guessed
|
||||||
// MFT didn't accept the input sample
|
// MFT didn't accept the input sample
|
||||||
if (input_status == 1) {
|
if (input_status == 1) {
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
// Copyright 2019 Citra Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
#include "wmf_decoder_utils.h"
|
#include "wmf_decoder_utils.h"
|
||||||
|
|
||||||
|
@ -9,17 +12,17 @@ void ReportError(std::string msg, HRESULT hr) {
|
||||||
LPSTR err;
|
LPSTR err;
|
||||||
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
||||||
FORMAT_MESSAGE_IGNORE_INSERTS,
|
FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||||
NULL, hr,
|
nullptr, hr,
|
||||||
// hardcode to use en_US because if any user had problems with this
|
// hardcode to use en_US because if any user had problems with this
|
||||||
// we can help them w/o translating anything
|
// we can help them w/o translating anything
|
||||||
MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), (LPSTR)&err, 0, NULL);
|
MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), (LPSTR)&err, 0, nullptr);
|
||||||
if (err != NULL) {
|
if (err != nullptr) {
|
||||||
LOG_CRITICAL(Audio_DSP, "{}: {}", msg, err);
|
LOG_CRITICAL(Audio_DSP, "{}: {}", msg, err);
|
||||||
}
|
}
|
||||||
LOG_CRITICAL(Audio_DSP, "{}: {:08x}", msg, hr);
|
LOG_CRITICAL(Audio_DSP, "{}: {:08x}", msg, hr);
|
||||||
}
|
}
|
||||||
|
|
||||||
int mf_coinit() {
|
int MFCoInit() {
|
||||||
HRESULT hr = S_OK;
|
HRESULT hr = S_OK;
|
||||||
|
|
||||||
// lite startup is faster and all what we need is included
|
// lite startup is faster and all what we need is included
|
||||||
|
@ -35,7 +38,7 @@ int mf_coinit() {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int mf_decoder_init(IMFTransform** transform, GUID audio_format) {
|
int MFDecoderInit(IMFTransform** transform, GUID audio_format) {
|
||||||
HRESULT hr = S_OK;
|
HRESULT hr = S_OK;
|
||||||
MFT_REGISTER_TYPE_INFO reg = {0};
|
MFT_REGISTER_TYPE_INFO reg = {0};
|
||||||
GUID category = MFT_CATEGORY_AUDIO_DECODER;
|
GUID category = MFT_CATEGORY_AUDIO_DECODER;
|
||||||
|
@ -47,7 +50,7 @@ int mf_decoder_init(IMFTransform** transform, GUID audio_format) {
|
||||||
|
|
||||||
hr = MFTEnumEx(category,
|
hr = MFTEnumEx(category,
|
||||||
MFT_ENUM_FLAG_SYNCMFT | MFT_ENUM_FLAG_LOCALMFT | MFT_ENUM_FLAG_SORTANDFILTER,
|
MFT_ENUM_FLAG_SYNCMFT | MFT_ENUM_FLAG_LOCALMFT | MFT_ENUM_FLAG_SORTANDFILTER,
|
||||||
®, NULL, &activate, &num_activate);
|
®, nullptr, &activate, &num_activate);
|
||||||
if (FAILED(hr) || num_activate < 1) {
|
if (FAILED(hr) || num_activate < 1) {
|
||||||
ReportError("Failed to enumerate decoders", hr);
|
ReportError("Failed to enumerate decoders", hr);
|
||||||
CoTaskMemFree(activate);
|
CoTaskMemFree(activate);
|
||||||
|
@ -57,10 +60,10 @@ int mf_decoder_init(IMFTransform** transform, GUID audio_format) {
|
||||||
for (unsigned int n = 0; n < num_activate; n++) {
|
for (unsigned int n = 0; n < num_activate; n++) {
|
||||||
hr = activate[n]->ActivateObject(IID_IMFTransform, (void**)transform);
|
hr = activate[n]->ActivateObject(IID_IMFTransform, (void**)transform);
|
||||||
if (FAILED(hr))
|
if (FAILED(hr))
|
||||||
*transform = NULL;
|
*transform = nullptr;
|
||||||
activate[n]->Release();
|
activate[n]->Release();
|
||||||
}
|
}
|
||||||
if (*transform == NULL) {
|
if (*transform == nullptr) {
|
||||||
ReportError("Failed to initialize MFT", hr);
|
ReportError("Failed to initialize MFT", hr);
|
||||||
CoTaskMemFree(activate);
|
CoTaskMemFree(activate);
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -69,37 +72,37 @@ int mf_decoder_init(IMFTransform** transform, GUID audio_format) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void mf_deinit(IMFTransform** transform) {
|
void MFDeInit(IMFTransform** transform) {
|
||||||
MFShutdownObject(*transform);
|
MFShutdownObject(*transform);
|
||||||
SafeRelease(transform);
|
SafeRelease(transform);
|
||||||
CoUninitialize();
|
CoUninitialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
IMFSample* create_sample(void* data, DWORD len, DWORD alignment, LONGLONG duration) {
|
IMFSample* CreateSample(void* data, DWORD len, DWORD alignment, LONGLONG duration) {
|
||||||
HRESULT hr = S_OK;
|
HRESULT hr = S_OK;
|
||||||
IMFMediaBuffer* buf = NULL;
|
IMFMediaBuffer* buf = nullptr;
|
||||||
IMFSample* sample = NULL;
|
IMFSample* sample = nullptr;
|
||||||
|
|
||||||
hr = MFCreateSample(&sample);
|
hr = MFCreateSample(&sample);
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
ReportError("Unable to allocate a sample", hr);
|
ReportError("Unable to allocate a sample", hr);
|
||||||
return NULL;
|
return nullptr;
|
||||||
}
|
}
|
||||||
// Yes, the argument for alignment is the actual alignment - 1
|
// Yes, the argument for alignment is the actual alignment - 1
|
||||||
hr = MFCreateAlignedMemoryBuffer(len, alignment - 1, &buf);
|
hr = MFCreateAlignedMemoryBuffer(len, alignment - 1, &buf);
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
ReportError("Unable to allocate a memory buffer for sample", hr);
|
ReportError("Unable to allocate a memory buffer for sample", hr);
|
||||||
return NULL;
|
return nullptr;
|
||||||
}
|
}
|
||||||
if (data) {
|
if (data) {
|
||||||
BYTE* buffer;
|
BYTE* buffer;
|
||||||
// lock the MediaBuffer
|
// lock the MediaBuffer
|
||||||
// this is actually not a thread-safe lock
|
// this is actually not a thread-safe lock
|
||||||
hr = buf->Lock(&buffer, NULL, NULL);
|
hr = buf->Lock(&buffer, nullptr, nullptr);
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
SafeRelease(&sample);
|
SafeRelease(&sample);
|
||||||
SafeRelease(&buf);
|
SafeRelease(&buf);
|
||||||
return NULL;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
memcpy(buffer, data, len);
|
memcpy(buffer, data, len);
|
||||||
|
@ -114,7 +117,7 @@ IMFSample* create_sample(void* data, DWORD len, DWORD alignment, LONGLONG durati
|
||||||
return sample;
|
return sample;
|
||||||
}
|
}
|
||||||
|
|
||||||
int select_input_mediatype(IMFTransform* transform, int in_stream_id, ADTSData adts,
|
bool SelectInputMediaType(IMFTransform* transform, int in_stream_id, ADTSData adts,
|
||||||
UINT8* user_data, UINT32 user_data_len, GUID audio_format) {
|
UINT8* user_data, UINT32 user_data_len, GUID audio_format) {
|
||||||
HRESULT hr = S_OK;
|
HRESULT hr = S_OK;
|
||||||
IMFMediaType* t;
|
IMFMediaType* t;
|
||||||
|
@ -124,7 +127,7 @@ int select_input_mediatype(IMFTransform* transform, int in_stream_id, ADTSData a
|
||||||
hr = MFCreateMediaType(&t);
|
hr = MFCreateMediaType(&t);
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
ReportError("Unable to create an empty MediaType", hr);
|
ReportError("Unable to create an empty MediaType", hr);
|
||||||
return -1;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// basic definition
|
// basic definition
|
||||||
|
@ -149,13 +152,13 @@ int select_input_mediatype(IMFTransform* transform, int in_stream_id, ADTSData a
|
||||||
hr = transform->SetInputType(in_stream_id, t, 0);
|
hr = transform->SetInputType(in_stream_id, t, 0);
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
ReportError("failed to select input types for MFT", hr);
|
ReportError("failed to select input types for MFT", hr);
|
||||||
return -1;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
int select_output_mediatype(IMFTransform* transform, int out_stream_id, GUID audio_format) {
|
bool SelectOutputMediaType(IMFTransform* transform, int out_stream_id, GUID audio_format) {
|
||||||
HRESULT hr = S_OK;
|
HRESULT hr = S_OK;
|
||||||
UINT32 tmp;
|
UINT32 tmp;
|
||||||
IMFMediaType* t;
|
IMFMediaType* t;
|
||||||
|
@ -166,11 +169,11 @@ int select_output_mediatype(IMFTransform* transform, int out_stream_id, GUID aud
|
||||||
for (DWORD i = 0;; i++) {
|
for (DWORD i = 0;; i++) {
|
||||||
hr = transform->GetOutputAvailableType(out_stream_id, i, &t);
|
hr = transform->GetOutputAvailableType(out_stream_id, i, &t);
|
||||||
if (hr == MF_E_NO_MORE_TYPES || hr == E_NOTIMPL) {
|
if (hr == MF_E_NO_MORE_TYPES || hr == E_NOTIMPL) {
|
||||||
return 0;
|
return true;
|
||||||
}
|
}
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
ReportError("failed to get output types for MFT", hr);
|
ReportError("failed to get output types for MFT", hr);
|
||||||
return -1;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
hr = t->GetUINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, &tmp);
|
hr = t->GetUINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, &tmp);
|
||||||
|
@ -183,26 +186,26 @@ int select_output_mediatype(IMFTransform* transform, int out_stream_id, GUID aud
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
ReportError("failed to set MF_MT_AUDIO_BLOCK_ALIGNMENT for MFT on output stream",
|
ReportError("failed to set MF_MT_AUDIO_BLOCK_ALIGNMENT for MFT on output stream",
|
||||||
hr);
|
hr);
|
||||||
return -1;
|
return false;
|
||||||
}
|
}
|
||||||
hr = transform->SetOutputType(out_stream_id, t, 0);
|
hr = transform->SetOutputType(out_stream_id, t, 0);
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
ReportError("failed to select output types for MFT", hr);
|
ReportError("failed to select output types for MFT", hr);
|
||||||
return -1;
|
return false;
|
||||||
}
|
}
|
||||||
return 0;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
return -1;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ReportError("MFT: Unable to find preferred output format", E_NOTIMPL);
|
ReportError("MFT: Unable to find preferred output format", E_NOTIMPL);
|
||||||
return -1;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
int detect_mediatype(char* buffer, size_t len, ADTSData* output, char** aac_tag) {
|
int DetectMediaType(char* buffer, size_t len, ADTSData* output, char** aac_tag) {
|
||||||
if (len < 7) {
|
if (len < 7) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
@ -224,7 +227,7 @@ int detect_mediatype(char* buffer, size_t len, ADTSData* output, char** aac_tag)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int mf_flush(IMFTransform** transform) {
|
void MFFlush(IMFTransform** transform) {
|
||||||
HRESULT hr = (*transform)->ProcessMessage(MFT_MESSAGE_COMMAND_FLUSH, 0);
|
HRESULT hr = (*transform)->ProcessMessage(MFT_MESSAGE_COMMAND_FLUSH, 0);
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
ReportError("MFT: Flush command failed", hr);
|
ReportError("MFT: Flush command failed", hr);
|
||||||
|
@ -233,11 +236,9 @@ int mf_flush(IMFTransform** transform) {
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
ReportError("Failed to end streaming for MFT", hr);
|
ReportError("Failed to end streaming for MFT", hr);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int send_sample(IMFTransform* transform, DWORD in_stream_id, IMFSample* in_sample) {
|
int SendSample(IMFTransform* transform, DWORD in_stream_id, IMFSample* in_sample) {
|
||||||
HRESULT hr = S_OK;
|
HRESULT hr = S_OK;
|
||||||
|
|
||||||
if (in_sample) {
|
if (in_sample) {
|
||||||
|
@ -261,16 +262,16 @@ int send_sample(IMFTransform* transform, DWORD in_stream_id, IMFSample* in_sampl
|
||||||
}
|
}
|
||||||
|
|
||||||
// return: 0: okay; 1: needs more sample; 2: needs reconfiguring; 3: more data available
|
// return: 0: okay; 1: needs more sample; 2: needs reconfiguring; 3: more data available
|
||||||
int receive_sample(IMFTransform* transform, DWORD out_stream_id, IMFSample** out_sample) {
|
int ReceiveSample(IMFTransform* transform, DWORD out_stream_id, IMFSample** out_sample) {
|
||||||
HRESULT hr;
|
HRESULT hr;
|
||||||
MFT_OUTPUT_DATA_BUFFER out_buffers;
|
MFT_OUTPUT_DATA_BUFFER out_buffers;
|
||||||
IMFSample* sample = NULL;
|
IMFSample* sample = nullptr;
|
||||||
MFT_OUTPUT_STREAM_INFO out_info;
|
MFT_OUTPUT_STREAM_INFO out_info;
|
||||||
DWORD status = 0;
|
DWORD status = 0;
|
||||||
bool mft_create_sample = false;
|
bool mft_create_sample = false;
|
||||||
|
|
||||||
if (!out_sample) {
|
if (!out_sample) {
|
||||||
ReportError("NULL pointer passed to receive_sample()", MF_E_SAMPLE_NOT_WRITABLE);
|
ReportError("nullptr pointer passed to receive_sample()", MF_E_SAMPLE_NOT_WRITABLE);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -284,12 +285,12 @@ int receive_sample(IMFTransform* transform, DWORD out_stream_id, IMFSample** out
|
||||||
(out_info.dwFlags & MFT_OUTPUT_STREAM_CAN_PROVIDE_SAMPLES);
|
(out_info.dwFlags & MFT_OUTPUT_STREAM_CAN_PROVIDE_SAMPLES);
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
sample = NULL;
|
sample = nullptr;
|
||||||
*out_sample = NULL;
|
*out_sample = nullptr;
|
||||||
status = 0;
|
status = 0;
|
||||||
|
|
||||||
if (!mft_create_sample) {
|
if (!mft_create_sample) {
|
||||||
sample = create_sample(NULL, out_info.cbSize, out_info.cbAlignment);
|
sample = CreateSample(nullptr, out_info.cbSize, out_info.cbAlignment);
|
||||||
if (!sample) {
|
if (!sample) {
|
||||||
ReportError("MFT: Unable to allocate memory for samples", hr);
|
ReportError("MFT: Unable to allocate memory for samples", hr);
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -307,7 +308,7 @@ int receive_sample(IMFTransform* transform, DWORD out_stream_id, IMFSample** out
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT) {
|
if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT) {
|
||||||
// TODO: better handling try again and EOF cases using drain value
|
// Most likely reasons: data corrupted; your actions not expected by MFT
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -320,11 +321,11 @@ int receive_sample(IMFTransform* transform, DWORD out_stream_id, IMFSample** out
|
||||||
}
|
}
|
||||||
|
|
||||||
if (out_buffers.dwStatus & MFT_OUTPUT_DATA_BUFFER_INCOMPLETE) {
|
if (out_buffers.dwStatus & MFT_OUTPUT_DATA_BUFFER_INCOMPLETE) {
|
||||||
|
// this status is also unreliable but whatever
|
||||||
return 3;
|
return 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: better handling try again and EOF cases using drain value
|
if (*out_sample == nullptr) {
|
||||||
if (*out_sample == NULL) {
|
|
||||||
ReportError("MFT: decoding failure", hr);
|
ReportError("MFT: decoding failure", hr);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
@ -332,7 +333,7 @@ int receive_sample(IMFTransform* transform, DWORD out_stream_id, IMFSample** out
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int copy_sample_to_buffer(IMFSample* sample, void** output, DWORD* len) {
|
int CopySampleToBuffer(IMFSample* sample, void** output, DWORD* len) {
|
||||||
IMFMediaBuffer* buffer;
|
IMFMediaBuffer* buffer;
|
||||||
HRESULT hr = S_OK;
|
HRESULT hr = S_OK;
|
||||||
BYTE* data;
|
BYTE* data;
|
||||||
|
@ -349,7 +350,7 @@ int copy_sample_to_buffer(IMFSample* sample, void** output, DWORD* len) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
hr = buffer->Lock(&data, NULL, NULL);
|
hr = buffer->Lock(&data, nullptr, nullptr);
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
ReportError("Failed to lock the buffer", hr);
|
ReportError("Failed to lock the buffer", hr);
|
||||||
SafeRelease(&buffer);
|
SafeRelease(&buffer);
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
|
// Copyright 2019 Citra Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#ifndef MF_DECODER
|
// AAC decoder related APIs are only available with WIN7+
|
||||||
#define MF_DECODER
|
|
||||||
|
|
||||||
#define WINVER _WIN32_WINNT_WIN7
|
#define WINVER _WIN32_WINNT_WIN7
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
@ -23,26 +24,24 @@ template <class T>
|
||||||
void SafeRelease(T** ppT) {
|
void SafeRelease(T** ppT) {
|
||||||
if (*ppT) {
|
if (*ppT) {
|
||||||
(*ppT)->Release();
|
(*ppT)->Release();
|
||||||
*ppT = NULL;
|
*ppT = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ReportError(std::string msg, HRESULT hr);
|
void ReportError(std::string msg, HRESULT hr);
|
||||||
|
|
||||||
// exported functions
|
// exported functions
|
||||||
int mf_coinit();
|
int MFCoInit();
|
||||||
int mf_decoder_init(IMFTransform** transform, GUID audio_format = MFAudioFormat_AAC);
|
int MFDecoderInit(IMFTransform** transform, GUID audio_format = MFAudioFormat_AAC);
|
||||||
void mf_deinit(IMFTransform** transform);
|
void MFDeInit(IMFTransform** transform);
|
||||||
IMFSample* create_sample(void* data, DWORD len, DWORD alignment = 1, LONGLONG duration = 0);
|
IMFSample* CreateSample(void* data, DWORD len, DWORD alignment = 1, LONGLONG duration = 0);
|
||||||
int select_input_mediatype(IMFTransform* transform, int in_stream_id, ADTSData adts,
|
bool SelectInputMediaType(IMFTransform* transform, int in_stream_id, ADTSData adts,
|
||||||
UINT8* user_data, UINT32 user_data_len,
|
UINT8* user_data, UINT32 user_data_len,
|
||||||
GUID audio_format = MFAudioFormat_AAC);
|
GUID audio_format = MFAudioFormat_AAC);
|
||||||
int detect_mediatype(char* buffer, size_t len, ADTSData* output, char** aac_tag);
|
int DetectMediaType(char* buffer, size_t len, ADTSData* output, char** aac_tag);
|
||||||
int select_output_mediatype(IMFTransform* transform, int out_stream_id,
|
bool SelectOutputMediaType(IMFTransform* transform, int out_stream_id,
|
||||||
GUID audio_format = MFAudioFormat_PCM);
|
GUID audio_format = MFAudioFormat_PCM);
|
||||||
int mf_flush(IMFTransform** transform);
|
void MFFlush(IMFTransform** transform);
|
||||||
int send_sample(IMFTransform* transform, DWORD in_stream_id, IMFSample* in_sample);
|
int SendSample(IMFTransform* transform, DWORD in_stream_id, IMFSample* in_sample);
|
||||||
int receive_sample(IMFTransform* transform, DWORD out_stream_id, IMFSample** out_sample);
|
int ReceiveSample(IMFTransform* transform, DWORD out_stream_id, IMFSample** out_sample);
|
||||||
int copy_sample_to_buffer(IMFSample* sample, void** output, DWORD* len);
|
int CopySampleToBuffer(IMFSample* sample, void** output, DWORD* len);
|
||||||
|
|
||||||
#endif // MF_DECODER
|
|
||||||
|
|
|
@ -1,5 +1,12 @@
|
||||||
const int fixure_buffer_size = 41;
|
// Copyright 2019 Citra Emulator Project
|
||||||
const unsigned char fixure_buffer[41] = {
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <array>
|
||||||
|
|
||||||
|
constexpr int fixure_buffer_size = 41;
|
||||||
|
constexpr std::array<u8, 41> fixure_buffer[41] = {
|
||||||
0xff, 0xf1, 0x4c, 0x80, 0x05, 0x3f, 0xfc, 0x21, 0x1a, 0x4e, 0xb0, 0x00, 0x00, 0x00,
|
0xff, 0xf1, 0x4c, 0x80, 0x05, 0x3f, 0xfc, 0x21, 0x1a, 0x4e, 0xb0, 0x00, 0x00, 0x00,
|
||||||
0x05, 0xfc, 0x4e, 0x1f, 0x08, 0x88, 0x00, 0x00, 0x00, 0xc4, 0x1a, 0x03, 0xfc, 0x9c,
|
0x05, 0xfc, 0x4e, 0x1f, 0x08, 0x88, 0x00, 0x00, 0x00, 0xc4, 0x1a, 0x03, 0xfc, 0x9c,
|
||||||
0x3e, 0x1d, 0x08, 0x84, 0x03, 0xd8, 0x3f, 0xe4, 0xe1, 0x20, 0x00, 0x0b, 0x38};
|
0x3e, 0x1d, 0x08, 0x84, 0x03, 0xd8, 0x3f, 0xe4, 0xe1, 0x20, 0x00, 0x0b, 0x38};
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright 2017 Citra Emulator Project
|
// Copyright 2019 Citra Emulator Project
|
||||||
// Licensed under GPLv2 or any later version
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
#if defined(HAVE_MF) || defined(HAVE_FFMPEG)
|
#if defined(HAVE_MF) || defined(HAVE_FFMPEG)
|
||||||
|
|
Reference in New Issue