From 7b722253a4649cce4a153033dd6e4e81c5b662f2 Mon Sep 17 00:00:00 2001 From: Pacuka <69674426+Pacuka@users.noreply.github.com> Date: Tue, 6 Oct 2026 18:10:23 +0200 Subject: [PATCH] fix(audiodec): take the M4AAC raw channel layout from the stream For a raw M4AAC stream the maximum channel count was written into the AudioSpecificConfig as the channel configuration. The maximum only bounds the stream: The Smurfs Dreams creates its decoder with 8 channels at most and sends stereo, so FFmpeg refused configuration 8 (reserved) and a stereo stream under configuration 6 or 7 decodes no frame. The decoder now starts from the mono/stereo configuration and on the first access unit that it cannot decode moves on to the configurations with more channels, up to the maximum, keeping the first one that decodes. Co-Authored-By: Claude Opus 5.5 --- core/libs/prx/libSceAudiodec/src/Codecs.cpp | 36 +++++++++++++++++---- core/libs/tests/GuestAudiodec.cpp | 8 +++-- 2 files changed, 35 insertions(+), 9 deletions(-) diff --git a/core/libs/prx/libSceAudiodec/src/Codecs.cpp b/core/libs/prx/libSceAudiodec/src/Codecs.cpp index dfa428f9d..aca46d88d 100644 --- a/core/libs/prx/libSceAudiodec/src/Codecs.cpp +++ b/core/libs/prx/libSceAudiodec/src/Codecs.cpp @@ -82,6 +82,12 @@ protected: return result; } + void reopen(std::vector replacement) { + avcodec_free_context(&context); + extradata = std::move(replacement); + open(); + } + private: void open() { const AVCodec* codec = avcodec_find_decoder(id); @@ -118,7 +124,8 @@ public: class AacDecoder final : public FfmpegDecoder { public: - AacDecoder(std::int32_t wordSize, bool adts, std::vector extradata) : FfmpegDecoder(AV_CODEC_ID_AAC, wordSize, std::move(extradata)), adts(adts) {} + AacDecoder(std::int32_t wordSize, bool adts, std::vector> configs) + : FfmpegDecoder(AV_CODEC_ID_AAC, wordSize, configs.empty() ? std::vector{} : configs.front()), adts(adts), configs(std::move(configs)) {} DecodeResult Decode(const std::uint8_t* data, std::size_t size, std::uint8_t* pcm, std::size_t pcmSize) override { std::size_t frameBytes = size; @@ -128,11 +135,22 @@ public: if (frameBytes < 7) return {DecodeStatus::InvalidData}; if (frameBytes > size) return {DecodeStatus::PartialInput}; } - return decodePacket(data, frameBytes, pcm, pcmSize); + if (configs.size() <= 1) return decodePacket(data, frameBytes, pcm, pcmSize); + for (std::size_t index = 0; index < configs.size(); ++index) { + if (index != 0) reopen(configs[index]); + const DecodeResult result = decodePacket(data, frameBytes, pcm, pcmSize); + if (result.status != DecodeStatus::InvalidData) { + configs = {configs[index]}; + return result; + } + } + reopen(configs.front()); + return {DecodeStatus::InvalidData}; } private: bool adts; + std::vector> configs; }; class At9Decoder final : public Decoder { @@ -222,13 +240,19 @@ std::unique_ptr CreateMp3(std::int32_t wordSize) { } std::unique_ptr CreateAac(std::int32_t wordSize, bool adts, std::uint32_t samplingFreqIndex, std::uint32_t channels) { - std::vector config; + std::vector> configs; if (!adts) { const std::uint32_t lowComplexity = 2; - config = {static_cast(lowComplexity << 3 | samplingFreqIndex >> 1), - static_cast((samplingFreqIndex & 1) << 7 | channels << 3)}; + const auto config = [&](std::uint32_t channelConfiguration) { + return std::vector{static_cast(lowComplexity << 3 | samplingFreqIndex >> 1), + static_cast((samplingFreqIndex & 1) << 7 | channelConfiguration << 3)}; + }; + configs.push_back(config(channels < 2 ? 1 : 2)); + for (std::uint32_t channelConfiguration = 3; channelConfiguration <= 7; ++channelConfiguration) { + if ((channelConfiguration == 7 ? 8 : channelConfiguration) <= channels) configs.push_back(config(channelConfiguration)); + } } - return std::make_unique(wordSize, adts, std::move(config)); + return std::make_unique(wordSize, adts, std::move(configs)); } std::unique_ptr CreateAt9(std::int32_t wordSize, const std::uint8_t (&config)[4], At9Format& format) { diff --git a/core/libs/tests/GuestAudiodec.cpp b/core/libs/tests/GuestAudiodec.cpp index fb9a4b3e5..414b6d3e9 100644 --- a/core/libs/tests/GuestAudiodec.cpp +++ b/core/libs/tests/GuestAudiodec.cpp @@ -115,12 +115,12 @@ std::size_t AdtsLength(const std::uint8_t* frame) { return static_cast((frame[3] & 3) << 11 | frame[4] << 3 | frame[5] >> 5); } -void TestAac(bool adts) { - AudiodecParamM4aac param{sizeof(param), 1, adts ? 1u : 2u, 3, 2, 0}; +void TestAac(bool adts, std::uint32_t maxChannels = 2) { + AudiodecParamM4aac param{sizeof(param), 1, adts ? 1u : 2u, 3, maxChannels, 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 && info.ui_sampling_freq == 48000 && info.ui_number_of_channels == 2); + Require(handle > 0 && info.ui_sampling_freq == 48000 && info.ui_number_of_channels == maxChannels); Streams streams; int crossings = 0; for (std::size_t offset = 0, frame = 0; offset < sizeof(AAC_ADTS); offset += AdtsLength(AAC_ADTS + offset), ++frame) { @@ -229,6 +229,8 @@ int main() { TestAac(true); TestAac(false); + TestAac(false, 6); + TestAac(false, 8); TestMp3(); TestAt9(); for (std::uint32_t type = 1; type <= 3; ++type) Require(sceAudiodecTermLibrary(type) == 0);