audio_core: hle: mf: multiple fixes...
... more smart pointers and re-arrange code
This commit is contained in:
parent
4bc6bfd51f
commit
be764e4f88
|
@ -3,7 +3,6 @@
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <array>
|
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
|
|
||||||
struct ADTSData {
|
struct ADTSData {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
// Copyright 2019 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.
|
||||||
|
#include <array>
|
||||||
#include "adts.h"
|
#include "adts.h"
|
||||||
|
|
||||||
constexpr std::array<u32, 16> freq_table = {96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050,
|
constexpr std::array<u32, 16> freq_table = {96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050,
|
||||||
|
|
|
@ -20,7 +20,7 @@ private:
|
||||||
|
|
||||||
std::optional<BinaryResponse> Decode(const BinaryRequest& request);
|
std::optional<BinaryResponse> Decode(const BinaryRequest& request);
|
||||||
|
|
||||||
int DecodingLoop(ADTSData adts_header, std::array<std::vector<u8>, 2>& out_streams);
|
MFOutputState DecodingLoop(ADTSData adts_header, std::array<std::vector<u8>, 2>& out_streams);
|
||||||
|
|
||||||
bool initalized = false;
|
bool initalized = false;
|
||||||
bool selected = false;
|
bool selected = false;
|
||||||
|
@ -103,7 +103,7 @@ void WMFDecoder::Impl::Clear() {
|
||||||
selected = false;
|
selected = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
int WMFDecoder::Impl::DecodingLoop(ADTSData adts_header,
|
MFOutputState WMFDecoder::Impl::DecodingLoop(ADTSData adts_header,
|
||||||
std::array<std::vector<u8>, 2>& out_streams) {
|
std::array<std::vector<u8>, 2>& out_streams) {
|
||||||
MFOutputState output_status = OK;
|
MFOutputState output_status = OK;
|
||||||
char* output_buffer = nullptr;
|
char* output_buffer = nullptr;
|
||||||
|
@ -138,12 +138,12 @@ int WMFDecoder::Impl::DecodingLoop(ADTSData adts_header,
|
||||||
|
|
||||||
// in case of "ok" only, just return quickly
|
// in case of "ok" only, just return quickly
|
||||||
if (output_status == OK)
|
if (output_status == OK)
|
||||||
return 0;
|
return OK;
|
||||||
|
|
||||||
// for status = 2, reset MF
|
// for status = 2, reset MF
|
||||||
if (output_status == NEED_RECONFIG) {
|
if (output_status == NEED_RECONFIG) {
|
||||||
Clear();
|
Clear();
|
||||||
return -1;
|
return FATAL_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
// for status = 3, try again with new buffer
|
// for status = 3, try again with new buffer
|
||||||
|
@ -151,12 +151,12 @@ int WMFDecoder::Impl::DecodingLoop(ADTSData adts_header,
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (output_status == NEED_MORE_INPUT) // according to MS document, this is not an error (?!)
|
if (output_status == NEED_MORE_INPUT) // according to MS document, this is not an error (?!)
|
||||||
return 1;
|
return NEED_MORE_INPUT;
|
||||||
|
|
||||||
return -1; // return on other status
|
return FATAL_ERROR; // return on other status
|
||||||
}
|
}
|
||||||
|
|
||||||
return -1;
|
return FATAL_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<BinaryResponse> WMFDecoder::Impl::Decode(const BinaryRequest& request) {
|
std::optional<BinaryResponse> WMFDecoder::Impl::Decode(const BinaryRequest& request) {
|
||||||
|
@ -205,13 +205,13 @@ std::optional<BinaryResponse> WMFDecoder::Impl::Decode(const BinaryRequest& requ
|
||||||
selected = true;
|
selected = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
sample.reset(CreateSample((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 = SendSample(transform.get(), in_stream_id, sample.get());
|
input_status = SendSample(transform.get(), in_stream_id, sample.get());
|
||||||
|
|
||||||
if (DecodingLoop(adts_header, out_streams) < 0) {
|
if (DecodingLoop(adts_header, out_streams) == FATAL_ERROR) {
|
||||||
// if the decode issues are 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
|
||||||
|
|
|
@ -77,17 +77,19 @@ void MFDeInit(IMFTransform* transform) {
|
||||||
CoUninitialize();
|
CoUninitialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
IMFSample* CreateSample(void* data, DWORD len, DWORD alignment, LONGLONG duration) {
|
unique_mfptr<IMFSample> CreateSample(void* data, DWORD len, DWORD alignment, LONGLONG duration) {
|
||||||
HRESULT hr = S_OK;
|
HRESULT hr = S_OK;
|
||||||
IMFMediaBuffer* buf_tmp = nullptr;
|
IMFMediaBuffer* buf_tmp = nullptr;
|
||||||
unique_mfptr<IMFMediaBuffer> buf;
|
unique_mfptr<IMFMediaBuffer> buf;
|
||||||
IMFSample* sample = nullptr;
|
IMFSample* sample_tmp = nullptr;
|
||||||
|
unique_mfptr<IMFSample> sample;
|
||||||
|
|
||||||
hr = MFCreateSample(&sample);
|
hr = MFCreateSample(&sample_tmp);
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
ReportError("Unable to allocate a sample", hr);
|
ReportError("Unable to allocate a sample", hr);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
sample.reset(sample_tmp);
|
||||||
// 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_tmp);
|
hr = MFCreateAlignedMemoryBuffer(len, alignment - 1, &buf_tmp);
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
|
@ -101,12 +103,11 @@ IMFSample* CreateSample(void* data, DWORD len, DWORD alignment, LONGLONG duratio
|
||||||
// this is actually not a thread-safe lock
|
// this is actually not a thread-safe lock
|
||||||
hr = buf->Lock(&buffer, nullptr, nullptr);
|
hr = buf->Lock(&buffer, nullptr, nullptr);
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
SafeRelease(&sample);
|
ReportError("Unable to lock down MediaBuffer", hr);
|
||||||
buf.reset();
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
memcpy(buffer, data, len);
|
std::memcpy(buffer, data, len);
|
||||||
|
|
||||||
buf->SetCurrentLength(len);
|
buf->SetCurrentLength(len);
|
||||||
buf->Unlock();
|
buf->Unlock();
|
||||||
|
@ -114,7 +115,11 @@ IMFSample* CreateSample(void* data, DWORD len, DWORD alignment, LONGLONG duratio
|
||||||
|
|
||||||
sample->AddBuffer(buf.get());
|
sample->AddBuffer(buf.get());
|
||||||
hr = sample->SetSampleDuration(duration);
|
hr = sample->SetSampleDuration(duration);
|
||||||
return sample;
|
if (FAILED(hr)) {
|
||||||
|
ReportError("Unable to set sample duration, but continuing anyway", hr);
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::move(sample);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SelectInputMediaType(IMFTransform* transform, int in_stream_id, const ADTSData& adts,
|
bool SelectInputMediaType(IMFTransform* transform, int in_stream_id, const ADTSData& adts,
|
||||||
|
@ -153,13 +158,15 @@ bool SelectInputMediaType(IMFTransform* transform, int in_stream_id, const ADTSD
|
||||||
bool SelectOutputMediaType(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* type;
|
||||||
|
unique_mfptr<IMFMediaType> t;
|
||||||
|
|
||||||
// If you know what you need and what you are doing, you can specify the condition instead of
|
// If you know what you need and what you are doing, you can specify the condition instead of
|
||||||
// searching but it's better to use search since MFT may or may not support your output
|
// searching but it's better to use search since MFT may or may not support your output
|
||||||
// parameters
|
// parameters
|
||||||
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, &type);
|
||||||
|
t.reset(type);
|
||||||
if (hr == MF_E_NO_MORE_TYPES || hr == E_NOTIMPL) {
|
if (hr == MF_E_NO_MORE_TYPES || hr == E_NOTIMPL) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -180,7 +187,7 @@ bool SelectOutputMediaType(IMFTransform* transform, int out_stream_id, GUID audi
|
||||||
hr);
|
hr);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
hr = transform->SetOutputType(out_stream_id, t, 0);
|
hr = transform->SetOutputType(out_stream_id, t.get(), 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 false;
|
return false;
|
||||||
|
@ -221,8 +228,8 @@ int DetectMediaType(char* buffer, size_t len, ADTSData* output, char** aac_tag)
|
||||||
tag = MFGetAACTag(tmp);
|
tag = MFGetAACTag(tmp);
|
||||||
aac_tmp[12] |= (tag & 0xff00) >> 8;
|
aac_tmp[12] |= (tag & 0xff00) >> 8;
|
||||||
aac_tmp[13] |= (tag & 0x00ff);
|
aac_tmp[13] |= (tag & 0x00ff);
|
||||||
memcpy(*aac_tag, aac_tmp, 14);
|
std::memcpy(*aac_tag, aac_tmp, 14);
|
||||||
memcpy(output, &tmp, sizeof(ADTSData));
|
std::memcpy(output, &tmp, sizeof(ADTSData));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -250,8 +257,6 @@ int SendSample(IMFTransform* transform, DWORD in_stream_id, IMFSample* in_sample
|
||||||
} // FAILED(hr)
|
} // FAILED(hr)
|
||||||
} else {
|
} else {
|
||||||
hr = transform->ProcessMessage(MFT_MESSAGE_COMMAND_DRAIN, 0);
|
hr = transform->ProcessMessage(MFT_MESSAGE_COMMAND_DRAIN, 0);
|
||||||
// ffmpeg: Some MFTs (AC3) will send a frame after each drain command (???), so
|
|
||||||
// ffmpeg: this is required to make draining actually terminate.
|
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
ReportError("MFT: Failed to drain when processing input", hr);
|
ReportError("MFT: Failed to drain when processing input", hr);
|
||||||
}
|
}
|
||||||
|
@ -264,7 +269,6 @@ std::tuple<MFOutputState, unique_mfptr<IMFSample>> ReceiveSample(IMFTransform* t
|
||||||
DWORD out_stream_id) {
|
DWORD out_stream_id) {
|
||||||
HRESULT hr;
|
HRESULT hr;
|
||||||
MFT_OUTPUT_DATA_BUFFER out_buffers;
|
MFT_OUTPUT_DATA_BUFFER out_buffers;
|
||||||
IMFSample* sample_tmp = nullptr;
|
|
||||||
MFT_OUTPUT_STREAM_INFO out_info;
|
MFT_OUTPUT_STREAM_INFO out_info;
|
||||||
DWORD status = 0;
|
DWORD status = 0;
|
||||||
unique_mfptr<IMFSample> sample;
|
unique_mfptr<IMFSample> sample;
|
||||||
|
@ -280,16 +284,14 @@ std::tuple<MFOutputState, unique_mfptr<IMFSample>> ReceiveSample(IMFTransform* t
|
||||||
(out_info.dwFlags & MFT_OUTPUT_STREAM_CAN_PROVIDE_SAMPLES);
|
(out_info.dwFlags & MFT_OUTPUT_STREAM_CAN_PROVIDE_SAMPLES);
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
sample = nullptr;
|
|
||||||
status = 0;
|
status = 0;
|
||||||
|
|
||||||
if (!mft_create_sample) {
|
if (!mft_create_sample) {
|
||||||
sample_tmp = CreateSample(nullptr, out_info.cbSize, out_info.cbAlignment);
|
sample = CreateSample(nullptr, out_info.cbSize, out_info.cbAlignment);
|
||||||
if (!sample_tmp) {
|
if (!sample.get()) {
|
||||||
ReportError("MFT: Unable to allocate memory for samples", hr);
|
ReportError("MFT: Unable to allocate memory for samples", hr);
|
||||||
return std::make_tuple(FATAL_ERROR, std::move(sample));
|
return std::make_tuple(FATAL_ERROR, std::move(sample));
|
||||||
}
|
}
|
||||||
sample.reset(sample_tmp);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
out_buffers.dwStreamID = out_stream_id;
|
out_buffers.dwStreamID = out_stream_id;
|
||||||
|
@ -353,7 +355,7 @@ int CopySampleToBuffer(IMFSample* sample, void** output, DWORD* len) {
|
||||||
}
|
}
|
||||||
|
|
||||||
*output = malloc(*len);
|
*output = malloc(*len);
|
||||||
memcpy(*output, data, *len);
|
std::memcpy(*output, data, *len);
|
||||||
|
|
||||||
// if buffer unlock fails, then... whatever, we have already got data
|
// if buffer unlock fails, then... whatever, we have already got data
|
||||||
buffer->Unlock();
|
buffer->Unlock();
|
||||||
|
|
|
@ -20,14 +20,6 @@
|
||||||
enum MFOutputState { FATAL_ERROR = -1, OK = 0, NEED_MORE_INPUT, NEED_RECONFIG, HAVE_MORE_DATA };
|
enum MFOutputState { FATAL_ERROR = -1, OK = 0, NEED_MORE_INPUT, NEED_RECONFIG, HAVE_MORE_DATA };
|
||||||
|
|
||||||
// utility functions
|
// utility functions
|
||||||
template <class T>
|
|
||||||
void SafeRelease(T** ppT) {
|
|
||||||
if (*ppT) {
|
|
||||||
(*ppT)->Release();
|
|
||||||
*ppT = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
struct MFRelease {
|
struct MFRelease {
|
||||||
void operator()(T* pointer) const {
|
void operator()(T* pointer) const {
|
||||||
|
@ -44,7 +36,7 @@ void ReportError(std::string msg, HRESULT hr);
|
||||||
bool MFCoInit();
|
bool MFCoInit();
|
||||||
bool MFDecoderInit(IMFTransform** transform, GUID audio_format = MFAudioFormat_AAC);
|
bool MFDecoderInit(IMFTransform** transform, GUID audio_format = MFAudioFormat_AAC);
|
||||||
void MFDeInit(IMFTransform* transform);
|
void MFDeInit(IMFTransform* transform);
|
||||||
IMFSample* CreateSample(void* data, DWORD len, DWORD alignment = 1, LONGLONG duration = 0);
|
unique_mfptr<IMFSample> CreateSample(void* data, DWORD len, DWORD alignment = 1, LONGLONG duration = 0);
|
||||||
bool SelectInputMediaType(IMFTransform* transform, int in_stream_id, const ADTSData& adts,
|
bool SelectInputMediaType(IMFTransform* transform, int in_stream_id, const 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);
|
||||||
|
|
Reference in New Issue