diff --git a/core/libs/prx/libSceAudiodec/src/Codecs.cpp b/core/libs/prx/libSceAudiodec/src/Codecs.cpp index cb91a87bdf..0f96e7b599 100644 --- a/core/libs/prx/libSceAudiodec/src/Codecs.cpp +++ b/core/libs/prx/libSceAudiodec/src/Codecs.cpp @@ -1,4 +1,5 @@ #include "prx/libSceAudiodec/src/Codecs.hpp" +#include "prx/libc/include/General.hpp" #include "libatrac9.h" extern "C" { @@ -8,6 +9,7 @@ extern "C" { #include #include +#include #include #include #include @@ -47,10 +49,13 @@ public: ~FfmpegDecoder() override { avcodec_free_context(&context); } - void Reset() override { avcodec_flush_buffers(context); } + void Reset() override { + if (context) avcodec_flush_buffers(context); + } protected: DecodeResult decodePacket(const std::uint8_t* data, std::size_t size, std::uint8_t* pcm, std::size_t pcmSize) { + if (!context) return {DecodeStatus::InvalidData}; const std::unique_ptr packet(av_packet_alloc()); const std::unique_ptr frame(av_frame_alloc()); if (!packet || !frame) throw std::bad_alloc(); @@ -85,10 +90,16 @@ protected: return result; } - void reopen(std::vector replacement) { + bool tryReopen(std::vector replacement) { avcodec_free_context(&context); extradata = std::move(replacement); - open(); + try { + open(); + return true; + } catch (const std::runtime_error& error) { + APS5_LOG_ERR("%s", error.what()); + return false; + } } private: @@ -102,7 +113,18 @@ private: std::memcpy(context->extradata, extradata.data(), extradata.size()); context->extradata_size = static_cast(extradata.size()); } - if (avcodec_open2(context, codec, nullptr) < 0) throw std::runtime_error("libSceAudiodec: cannot open an FFmpeg decoder"); + if (const int status = avcodec_open2(context, codec, nullptr); status < 0) { + avcodec_free_context(&context); + char reason[AV_ERROR_MAX_STRING_SIZE]{}; + av_strerror(status, reason, sizeof(reason)); + std::string config; + for (const auto byte : extradata) { + char hex[4]; + std::snprintf(hex, sizeof(hex), "%02x", byte); + config += hex; + } + throw std::runtime_error("libSceAudiodec: cannot open the FFmpeg " + std::string(avcodec_get_name(id)) + " decoder" + (config.empty() ? "" : " (config " + config + ")") + ": " + reason); + } } std::int32_t wordSize; @@ -186,14 +208,14 @@ public: if (layout.channelConfiguration == 0) { if (!layouts.empty()) { layouts.clear(); - reopen({}); + if (!tryReopen({})) return {DecodeStatus::InvalidData}; } return decodePacket(data, frameBytes, pcm, pcmSize); } if (layouts.empty() || layouts.front() != layout) { layouts = {layout}; sbr = highEfficiency ? Sbr::Implicit : Sbr::Absent; - reopen(audioSpecificConfig(layout, sbr, 0)); + if (!tryReopen(audioSpecificConfig(layout, sbr, 0))) return {DecodeStatus::InvalidData}; } DecodeResult result = decodeAccessUnit(data + headerBytes, frameBytes - headerBytes, pcm, pcmSize); result.consumed = frameBytes; @@ -203,18 +225,18 @@ public: private: DecodeResult decodeAccessUnit(const std::uint8_t* data, std::size_t size, std::uint8_t* pcm, std::size_t pcmSize) { for (std::size_t index = 0; index < layouts.size(); ++index) { - if (index != 0) reopen(audioSpecificConfig(layouts[index], sbr, 0)); + if (index != 0 && !tryReopen(audioSpecificConfig(layouts[index], sbr, 0))) continue; DecodeResult result = decodePacket(data, size, pcm, pcmSize); if (result.status == DecodeStatus::InvalidData) continue; layouts = {layouts[index]}; if (sbr == Sbr::Implicit && layouts.front().channelConfiguration == 1 && result.channels > 1) { sbr = Sbr::WithoutParametricStereo; - reopen(audioSpecificConfig(layouts.front(), sbr, result.sampleRate)); + if (!tryReopen(audioSpecificConfig(layouts.front(), sbr, result.sampleRate))) return {DecodeStatus::InvalidData}; result = decodePacket(data, size, pcm, pcmSize); } return result; } - if (layouts.size() > 1) reopen(audioSpecificConfig(layouts.front(), sbr, 0)); + if (layouts.size() > 1) tryReopen(audioSpecificConfig(layouts.front(), sbr, 0)); return {DecodeStatus::InvalidData}; } diff --git a/core/libs/tests/GuestAudiodec.cpp b/core/libs/tests/GuestAudiodec.cpp index 6461dd9bf4..2249cd0610 100644 --- a/core/libs/tests/GuestAudiodec.cpp +++ b/core/libs/tests/GuestAudiodec.cpp @@ -259,6 +259,25 @@ void TestAac(bool adts, std::uint32_t maxChannels = 2) { Require(sceAudiodecDeleteDecoder(handle) == 0); } +void TestAacUnopenableLayout() { + AudiodecParamM4aac param{sizeof(param), 1, 1, 3, 2, 0}; + AudiodecM4aacInfo info{sizeof(info), 0, 0, 0, 0}; + AudiodecCtrl ctrl{¶m, &info, nullptr, nullptr}; + const std::int32_t handle = sceAudiodecCreateDecoder(&ctrl, 3); + Require(handle > 0); + Streams streams; + const std::size_t length = AdtsLength(AAC_ADTS); + std::vector reserved(AAC_ADTS, AAC_ADTS + length); + reserved[2] = static_cast((reserved[2] & 0xC3) | 13 << 2); + Require(Decode(handle, ctrl, streams, reserved.data(), reserved.size()) == errorApiFail && info.i_result == -4); + Require(streams.au.ui_au_size == 0 && streams.pcm.ui_pcm_size == 0); + Require(Decode(handle, ctrl, streams, reserved.data(), reserved.size()) == errorApiFail && info.i_result == -4); + Require(sceAudiodecClearContext(handle) == 0); + Require(Decode(handle, ctrl, streams, AAC_ADTS, length) == 0); + Require(streams.au.ui_au_size == length && streams.pcm.ui_pcm_size == 1024 * 2 * 2 && info.i_result == 0); + Require(sceAudiodecDeleteDecoder(handle) == 0); +} + void TestAacRaw51(std::uint32_t maxChannels) { AudiodecParamM4aac param{sizeof(param), 1, 2, 3, maxChannels, 0}; AudiodecM4aacInfo info{sizeof(info), 0, 0, 0, 0}; @@ -469,6 +488,7 @@ int main() { TestHeAac(HEAAC_STEREO, sizeof(HEAAC_STEREO), 2, adts, highEfficiency); } } + TestAacUnopenableLayout(); TestMp3(); TestAt9(); for (std::uint32_t type = 1; type <= 3; ++type) Require(sceAudiodecTermLibrary(type) == 0);