Implement OpenHardwareOpusDecoderEx and GetWorkBufferSizeEx

Implements these 2 functions which were introduced in HOS 12.0.0. Fixes a crash in Xenoblade Chronicles 3.
This commit is contained in:
TheASVigilante 2022-09-10 23:04:15 +02:00 committed by GitHub
parent 34bd16426c
commit a1ff4e1777
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 43 additions and 5 deletions

View File

@ -11,12 +11,12 @@ namespace skyline::service::codec {
return util::AlignUp(static_cast<u32>(frameSize * channelCount / (OpusFullbandSampleRate / sampleRate)), 0x40);
}
IHardwareOpusDecoder::IHardwareOpusDecoder(const DeviceState &state, ServiceManager &manager, i32 sampleRate, i32 channelCount, u32 workBufferSize, KHandle workBufferHandle)
IHardwareOpusDecoder::IHardwareOpusDecoder(const DeviceState &state, ServiceManager &manager, i32 sampleRate, i32 channelCount, u32 workBufferSize, KHandle workBufferHandle, bool isIsLargerSize)
: BaseService(state, manager),
sampleRate(sampleRate),
channelCount(channelCount),
workBuffer(state.process->GetHandle<kernel::type::KTransferMemory>(workBufferHandle)),
decoderOutputBufferSize(CalculateOutBufferSize(sampleRate, channelCount, MaxFrameSizeNormal)) {
decoderOutputBufferSize(CalculateOutBufferSize(sampleRate, channelCount, isIsLargerSize ? MaxFrameSizeEx : MaxFrameSizeNormal)) {
if (workBufferSize < decoderOutputBufferSize)
throw exception("Work Buffer doesn't have adequate space for Opus Decoder: 0x{:X} (Required: 0x{:X})", workBufferSize, decoderOutputBufferSize);

View File

@ -58,7 +58,7 @@ namespace skyline::service::codec {
Result DecodeInterleavedImpl(ipc::IpcRequest &request, ipc::IpcResponse &response, bool writeDecodeTime = false);
public:
IHardwareOpusDecoder(const DeviceState &state, ServiceManager &manager, i32 sampleRate, i32 channelCount, u32 workBufferSize, KHandle workBufferHandle);
IHardwareOpusDecoder(const DeviceState &state, ServiceManager &manager, i32 sampleRate, i32 channelCount, u32 workBufferSize, KHandle workBufferHandle, bool isIsLargerSize = false);
/**
* @brief Decodes the Opus source data, returns decoded data size and decoded sample count

View File

@ -7,9 +7,9 @@
#include "IHardwareOpusDecoder.h"
namespace skyline::service::codec {
static u32 CalculateBufferSize(i32 sampleRate, i32 channelCount) {
static u32 CalculateBufferSize(i32 sampleRate, i32 channelCount, i32 useLargerFrameSize = 0) {
u32 requiredSize{static_cast<u32>(opus_decoder_get_size(channelCount))};
requiredSize += MaxInputBufferSize + CalculateOutBufferSize(sampleRate, channelCount, MaxFrameSizeNormal);
requiredSize += MaxInputBufferSize + CalculateOutBufferSize(sampleRate, channelCount, useLargerFrameSize ? MaxFrameSizeEx : MaxFrameSizeNormal);
return requiredSize;
}
@ -32,4 +32,28 @@ namespace skyline::service::codec {
response.Push<u32>(CalculateBufferSize(sampleRate, channelCount));
return {};
}
Result IHardwareOpusDecoderManager::OpenHardwareOpusDecoderEx(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
i32 sampleRate{request.Pop<i32>()};
i32 channelCount{request.Pop<i32>()};
i32 useLargerFrameSize{request.Pop<i32>()};
request.Pop<i32>(); // Just padding
u32 workBufferSize{request.Pop<u32>()};
KHandle workBuffer{request.copyHandles.at(0)};
Logger::Debug("Creating Opus decoder: Sample rate: {}, Channel count: {}, Work buffer handle: 0x{:X} (Size: 0x{:X})", sampleRate, channelCount, workBuffer, workBufferSize);
manager.RegisterService(std::make_shared<IHardwareOpusDecoder>(state, manager, sampleRate, channelCount, workBufferSize, workBuffer, useLargerFrameSize), session, response);
return {};
}
Result IHardwareOpusDecoderManager::GetWorkBufferSizeEx(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
i32 sampleRate{request.Pop<i32>()};
i32 channelCount{request.Pop<i32>()};
i32 useLargerFrameSize{request.Pop<i32>()};
request.Pop<i32>(); // Just padding
response.Push<u32>(CalculateBufferSize(sampleRate, channelCount, useLargerFrameSize));
return {};
}
}

View File

@ -39,9 +39,23 @@ namespace skyline::service::codec {
*/
Result GetWorkBufferSize(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
/**
* @brief Returns an IHardwareOpusDecoder object [12.0.0+]
* @url https://switchbrew.org/wiki/Audio_services#OpenHardwareOpusDecoder
*/
Result OpenHardwareOpusDecoderEx(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
/**
* @brief Returns the required size for the decoder's work buffer [12.0.0+]
* @url https://switchbrew.org/wiki/Audio_services#GetWorkBufferSizeEx
*/
Result GetWorkBufferSizeEx(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
SERVICE_DECL(
SFUNC(0x0, IHardwareOpusDecoderManager, OpenHardwareOpusDecoder),
SFUNC(0x1, IHardwareOpusDecoderManager, GetWorkBufferSize),
SFUNC(0x4, IHardwareOpusDecoderManager, OpenHardwareOpusDecoderEx),
SFUNC(0x5, IHardwareOpusDecoderManager, GetWorkBufferSizeEx),
)
};
}