Merge pull request #22371 from hrydgard/audiocodec-fixes

sceAudiocodec and sceVideocodec timing and Atrac3+ fixes
This commit is contained in:
Henrik Rydgård authored and GitHub committed 2026-09-28 17:26:04 -06:00
commit eab0b53a7a
6 files changed
+329 -156

No files matched your search

+6 -5
View File
@@ -32,7 +32,7 @@
struct AT3BitrateMeta {
u16 sampleSize;
u8 dataByte;
u8 jointStereo; // I think?
u8 jointStereo;
};
static const AT3BitrateMeta g_at3BitrateMeta[5] = {
@@ -242,12 +242,13 @@ int InitContextFromTrackInfo(SceAtracContext *ctx, const TrackInfo *wave, u32 bu
(ctx->codec).fmt.at3.formatByte2 = wave->tailFlag;
return 0;
}
// At3. Set up the hardware codec (hopefully we can correctly support this in sceAudiocodec and thus sceAtrac LLE in the future)
// This is not actually necessary since we don't use the actual hardware codec.
// At3. Set up the hardware codec parameter as libatrac3plus.prx's SetData (0880645c) does:
// keyed by frame size and the joint-stereo flag (sampleSizeMaybe, for Atrac3), it stores the
// data byte, as a word. We don't decode through it, but it's what the game sees.
for (int counter = 4; counter >= 0; counter--) {
if ((g_at3BitrateMeta[counter].sampleSize == (ctx->info).sampleSize) &&
((int)g_at3BitrateMeta[counter].dataByte == wave->sampleSizeMaybe)) {
(ctx->codec).fmt.at3.formatByte1 = (char)g_at3BitrateMeta[counter].jointStereo;
((int)g_at3BitrateMeta[counter].jointStereo == wave->sampleSizeMaybe)) {
(ctx->codec).fmt.at3.formatByte1 = g_at3BitrateMeta[counter].dataByte;
(ctx->codec).fmt.at3.formatByte2 = 0;
(ctx->codec).fmt.at3.unk2a = 0;
(ctx->codec).fmt.at3.unk2b = 0;
+2
View File
@@ -500,6 +500,8 @@ enum PSPErrorCode : u32 {
SCE_SAS_ERROR_NOT_INIT = 0x80420100,
SCE_AVCODEC_ERROR_INVALID_DATA = 0x807f00fd,
// Name inferred: sceAudiocodec returns this for an unsupported codec or AAC sample rate.
SCE_AVCODEC_ERROR_UNSUPPORTED = 0x807f00ff,
// These are inferred from behavior.
SCE_REG_ERROR_MALLOC_FAILURE = 0x80082712,
+262 -131
View File
@@ -18,6 +18,7 @@
#include "Common/Serialize/Serializer.h"
#include "Common/Serialize/SerializeFuncs.h"
#include "Common/Serialize/SerializeMap.h"
#include "Core/HLE/HLE.h"
#include "Core/HLE/FunctionWrappers.h"
#include "Core/HLE/sceAudiocodec.h"
@@ -38,6 +39,9 @@ std::map<u32, AudioDecoder *> g_audioDecoderContexts;
// frame size in the context, and at init time the input buffer is still empty, so for that path we
// only learn it from the first frame - at which point the decoder has to be rebuilt to match.
static std::map<u32, int> g_at3PlusFrameBytes;
// How many more successfully decoded frames will produce no output, per context (see the notes on
// Atrac3+ below).
static std::map<u32, int> g_primingFrames;
static bool oldStateLoaded = false;
@@ -66,42 +70,49 @@ static_assert(offsetof(SceAudiocodecCodec, allocMem) == 0x68);
//
// Bitrate Frame Size Byte 1 Byte 2 Channels
// -----------------------------------------------------
// 48kbps 0x118 0x24 0x22 1? // This hits "Frame data doesn't match channel configuration".
// 64kbps 0x178 (0x2e implied by the formula below)
// 48kbps 0x118 0x24 0x22 1
// 64kbps 0x178 0x28 0x2e 2
// 96kbps? 0x230 0x28 0x45 2
// 128kbps 0x2E8 0x28 0x5c 2
//
// The frame size really is "Byte 2" * 8 + 8 - it holds for all three rows we have both numbers
// for, and libatrac3plus.prx writes 0x28/0x5c (the 128kbps row) into these two bytes at init as
// the worst case it sizes its EDRAM allocation against. So byte 2 is the hardware's own frame
// descriptor and reading it, as we do, is the right thing.
// The frame size is ((byte 1 & 3) << 8 | byte 2) * 8 + 8, and the channel count is bits 2-4 of
// byte 1 (AtracTrack checks the same bits). The 0x0FD0-sync header on PSMF frames carries the same
// two bytes. On hardware (pspautotests audio/audiocodec), a header claiming byte 1 = 0x29 made the
// decoder read 0x800 bytes more, so the two low bits of byte 1 really are size bits; a context of
// 28 5c on a 28 2e stream decodes fine and reports 744 bytes read, since the decoder only reads
// what the frame needs; 28 22 (too small) fails with err 0x214; and byte 1 = 0x20 (no channels)
// fails Init with err 0x202.
//
// The channel guess below (bit 3 of byte 1) fits both data points we have and nothing else.
// at3Related (0x30) says whether frames carry that 8-byte header. mpeg.prx sets it and passes
// headered frames, libatrac3plus.prx clears it and strips the header. With it set, a frame without
// the 0F D0 sync fails with err 0x211, and one whose header disagrees with the context's channel
// count with err 0x213; with it clear, a header is taken for audio data, which fails to decode.
//
// Bitstream errors are 0x20a or 0x208 (junk and zeros give 0x20a; a real frame read from 4 bytes
// in can parse further and give 0x208). Neither is sticky: the next good frame decodes normally.
// Every failure returns SCE_AVCODEC_ERROR_INVALID_DATA with 0 bytes read and written.
//
// The first frame decoded successfully produces no output (dstBytesWritten 0); from then on
// frame n gives the samples ffmpeg's decoder gives for frame n, so it's dropped, not delayed.
// AAC drops two frames the same way. Atrac3 and MP3 drop none.
// Atrac3 (0x1001)
//
// The Media Engine only ever sees the first 0x68 bytes of this structure - me_wrapper.prx does
// sceKernelDcacheWritebackInvalidateRange(ctx, 0x68) before handing it over, and avcodec.prx
// validates the same range - so whatever the hardware uses to size an Atrac3 frame has to be in
// there. The only Atrac3-specific thing anything writes is the joint-stereo flag in byte 1.
// validates the same range.
//
// Frame size data byte JointStereo?
// -------------------------------------------------
// 0x180 0x04 0
// 0x130 0x06 0
// 0x0C0 0x0B 1
// 0x0C0 0x0E 0
// 0x098 0x0F 0
//
// NOTE: sceAudiocodecDecode below hardcodes 384 (0x180) bytes per frame for Atrac3, which is only
// the first row. If a game ever drives Atrac3 through sceAudiocodec at one of the other sizes we
// will decode garbage.
// The parameter at 0x28 (a word) selects the frame layout. libatrac3plus.prx's SetData (0880645c)
// looks it up in a table keyed by frame size and the stream's joint-stereo flag, and the ME decodes
// by it alone. See at3Params below. Anything above 0x0F fails Init with err 0x186; a bad frame fails
// with err 0x182 and isn't sticky either.
// AAC (0x1003)
// ------------------------------------------------
// Sample rate is at offset 0x28. The input frame size is 0x609 when the byte at 0x2c is nonzero
// and 0x600 when it is zero (avcodec.prx decodeUtility, case 0x1003); the output size comes from
// the byte at 0x2d and is 0x1000 or 0x2000.
// the byte at 0x2d and is 0x1000 or 0x2000. Setting the byte at 0x2c alone (to 1) fails Init on
// hardware, so something else has to go with it.
// MP3 (0x1002)
// ------------------------------------------------
@@ -113,51 +124,39 @@ static_assert(offsetof(SceAudiocodecCodec, allocMem) == 0x68);
// 0x28 is not this frame's size - libmp3.prx writes 0x5A1 there once, the largest an MP3 frame
// can ever be. Output is 0x1200 bytes for MPEG1 (1152 samples) and 0x900 otherwise (576).
void CalculateInputBytesAndChannelsAt3Plus(const SceAudiocodecCodec *ctx, int *inputBytes, int *channels, int *headerBytes = nullptr) {
*inputBytes = 0;
*channels = 2;
if (headerBytes) {
*headerBytes = 0;
}
u8 formatByte1 = ctx->fmt.at3.formatByte1;
u8 formatByte2 = ctx->fmt.at3.formatByte2;
// Atrac3+ frames inside a PSMF still carry their 8-byte header, starting with the 0x0FD0 sync
// word; libatrac3plus.prx strips it before handing the frame over, mpeg.prx leaves it on for
// the hardware to parse. So when the sync word is still there, take the size from the frame
// and step over the header, exactly as MpegDemux does on the HLE path. Bytes 2 and 3 are the
// same pair that ends up in the context, but with two more size bits in the first of them.
const u8 *frame = Memory::IsValidRange(ctx->inBuf, 4) ? Memory::GetPointerUnchecked(ctx->inBuf) : nullptr;
if (frame && frame[0] == 0x0F && frame[1] == 0xD0) {
formatByte1 = frame[2];
formatByte2 = frame[3];
if (headerBytes) {
*headerBytes = 8;
}
// The full size, unlike the context's, has two more high bits in the first byte. The
// 0x10 is the header plus the 8 the context's own formula adds.
*channels = (formatByte1 & 8) ? 2 : 1;
*inputBytes = (((formatByte1 & 0x03) << 8) | (formatByte2 * 8)) + 0x10 - 8;
return;
}
// bit 3 of the first byte is the channel count. This is a guess, but it fits the data we have.
*channels = (formatByte1 & 8) ? 2 : 1;
// formatByte2 * 8 + 8 gives the frame size for every bitrate we have data for (0x118, 0x178,
// 0x230, 0x2E8), so use it for any other value as well rather than leaving inputBytes at 0,
// which the firmware never does and which would fail the decode outright.
*inputBytes = formatByte2 * 8 + 8;
// Atrac3+ frame size and channel count from a pair of format bytes (from the context, or from a
// PSMF frame header).
static void At3PlusFormat(u8 formatByte1, u8 formatByte2, int *inputBytes, int *channels) {
*channels = (formatByte1 >> 2) & 7;
*inputBytes = (((formatByte1 & 3) << 8) | formatByte2) * 8 + 8;
}
// Atrac3 (0x1001). Unlike Atrac3+, the context doesn't carry a frame size - libatrac3plus.prx
// (and our mirror of it in AtracCtx2) writes only the joint-stereo flag into formatByte1 for
// Atrac3, so in general the size can't be recovered from the context alone.
//
// One case is unambiguous, though. Of the five frame sizes the hardware supports, exactly one is
// joint stereo (66kbps stereo, 0xC0 bytes), so that flag pins the size down by itself. Everything
// else falls back to the 132kbps stereo frame, which is what the old hardcoded 384 was.
static int Atrac3BytesPerFrameFromContext(const SceAudiocodecCodec *ctx, int *channels);
// For a decode (headerBytes non-null), also handles the frame header, and returns the ME error
// code for a frame that doesn't match the context, or 0.
static int CalculateInputBytesAndChannelsAt3Plus(const SceAudiocodecCodec *ctx, int *inputBytes, int *channels, int *headerBytes = nullptr) {
At3PlusFormat(ctx->fmt.at3.formatByte1, ctx->fmt.at3.formatByte2, inputBytes, channels);
if (!headerBytes) {
return 0;
}
*headerBytes = 0;
if (ctx->fmt.at3.at3Related == 0) {
return 0;
}
const u8 *frame = Memory::IsValidRange(ctx->inBuf, 8) ? Memory::GetPointerUnchecked(ctx->inBuf) : nullptr;
if (!frame || frame[0] != 0x0F || frame[1] != 0xD0) {
return 0x211;
}
int frameChannels;
At3PlusFormat(frame[2], frame[3], inputBytes, &frameChannels);
if (frameChannels != *channels) {
return 0x213;
}
*headerBytes = 8;
return 0;
}
static bool Atrac3LayoutFromContext(const SceAudiocodecCodec *ctx, int *bytesPerFrame, int *channels, bool *jointStereo);
// The MPEG sample rates, indexed by [version][sampleRateIndex] exactly as the hardware's own
// table in avcodec.prx does. Version is 0 = MPEG2, 1 = MPEG1, 2 = MPEG2.5.
@@ -199,6 +198,7 @@ static bool removeDecoder(u32 ctxPtr) {
delete it->second;
g_audioDecoderContexts.erase(it);
g_at3PlusFrameBytes.erase(ctxPtr);
g_primingFrames.erase(ctxPtr);
return true;
}
return false;
@@ -210,6 +210,7 @@ static void clearDecoders() {
}
g_audioDecoderContexts.clear();
g_at3PlusFrameBytes.clear();
g_primingFrames.clear();
}
void __AudioCodecInit() {
@@ -224,6 +225,41 @@ void __AudioCodecShutdown() {
clearDecoders();
}
// Everything below that reaches the ME blocks the caller while it answers. Measured at 222MHz
// (pspautotests audio/audiocodec/timing):
// CheckNeedMem GetEDRAM Init ReleaseEDRAM GetInfo
// Atrac3+ 147 56 646 55 82
// Atrac3 80 56 210 56 3
// MP3 79 57 517 57 82
// AAC 57 57 230 55 3
// A GetInfo of 3us didn't reach the ME. Initializing a mono Atrac3+ decoder (as InitMono does for
// libatrac3plus.prx's MOut functions) takes 524us. Failed decodes take 214us for an Atrac3+
// bitstream error, 142us for a bad Atrac3+ frame header, and 169us for an Atrac3 bitstream error.
static int MECall(int result, int us) {
return hleDelayResult(result, "audiocodec", MEScheduleJob(PowerScaleFromDefaultClock(us)));
}
static int InitUs(int codec, const SceAudiocodecCodec *ctx) {
switch (codec) {
case PSP_CODEC_AT3PLUS: return ((ctx->fmt.at3.formatByte1 >> 2) & 7) == 1 ? 524 : 646;
case PSP_CODEC_AT3: return 210;
case PSP_CODEC_MP3: return 517;
default: return 230;
}
}
// libmp4.prx puts the sample rate here. On hardware 22050 and 44100 are accepted, 0 and 12345
// aren't; that the rest of the standard AAC rates are accepted is an assumption. 0 if not valid.
static int AacSampleRateFromContext(const SceAudiocodecCodec *ctx) {
static const int aacRates[] = { 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350 };
for (int rate : aacRates) {
if ((int)ctx->fmt.aac.sampleRate == rate) {
return rate;
}
}
return 0;
}
// Creates a context's decoder from what the context holds. Init does this, and so does a state
// load, since the decoders themselves aren't saved.
static AudioDecoder *CreateDecoderForContext(u32 ctxPtr, PSPAudioType audioType) {
@@ -231,6 +267,7 @@ static AudioDecoder *CreateDecoderForContext(u32 ctxPtr, PSPAudioType audioType)
int bytesPerFrame = 0;
int channels = 2;
int sampleRate = 44100;
uint8_t extraData[14]{};
@@ -240,10 +277,8 @@ static AudioDecoder *CreateDecoderForContext(u32 ctxPtr, PSPAudioType audioType)
break;
case PSP_CODEC_AT3:
{
// See AtracBase::CreateDecoder. The context only tells us whether the stream is joint
// stereo, which pins the frame size down in that one case - see the function.
bytesPerFrame = Atrac3BytesPerFrameFromContext(ctx, &channels);
bool jointStereo = IsAtrac3StreamJointStereo(PSP_CODEC_AT3, bytesPerFrame, channels);
bool jointStereo;
Atrac3LayoutFromContext(ctx, &bytesPerFrame, &channels, &jointStereo);
// The only thing that changes are the jointStereo_ values.
extraData[0] = 1;
extraData[3] = channels << 3;
@@ -252,6 +287,11 @@ static AudioDecoder *CreateDecoderForContext(u32 ctxPtr, PSPAudioType audioType)
extraData[10] = 1;
break;
}
case PSP_CODEC_AAC:
if (AacSampleRateFromContext(ctx)) {
sampleRate = AacSampleRateFromContext(ctx);
}
break;
default:
break;
}
@@ -259,7 +299,7 @@ static AudioDecoder *CreateDecoderForContext(u32 ctxPtr, PSPAudioType audioType)
// Create audio decoder for given audio codec and push it into AudioList
INFO_LOG(Log::ME, "sceAudioDecoder: Creating codec with %04x frame size and %d channels, codec %04x", bytesPerFrame, channels, (int)audioType);
// We send in extra data with all codec, most ignore it.
AudioDecoder *decoder = CreateAudioDecoder(audioType, 44100, channels, bytesPerFrame, extraData, sizeof(extraData));
AudioDecoder *decoder = CreateAudioDecoder(audioType, sampleRate, channels, bytesPerFrame, extraData, sizeof(extraData));
decoder->SetCtxPtr(ctxPtr);
g_audioDecoderContexts[ctxPtr] = decoder;
g_at3PlusFrameBytes[ctxPtr] = bytesPerFrame;
@@ -286,7 +326,9 @@ static int __AudioCodecInitCommon(u32 ctxPtr, int codec, bool mono) {
ctx->magic = 0x5100601;
ctx->err = 0;
// Special actions for some codecs.
int primingFrames = 0;
// Special actions for some codecs, and what the hardware rejects.
switch (audioType) {
case PSP_CODEC_MP3:
// Not seeing inited in Kurok (homebrew)
@@ -294,16 +336,63 @@ static int __AudioCodecInitCommon(u32 ctxPtr, int codec, bool mono) {
ctx->fmt.mp3.version = 9999;
break;
case PSP_CODEC_AAC:
// AAC / mp4
// offsets 40-42 are a 24-bit LE number specifying the sample rate. It's 32000, 44100 or 48000.
// neededMem has been set to 0x18f20.
if (!AacSampleRateFromContext(ctx)) {
return MECall(hleLogError(Log::ME, SCE_AVCODEC_ERROR_UNSUPPORTED, "bad AAC sample rate %d", ctx->fmt.aac.sampleRate), InitUs(codec, ctx));
}
primingFrames = 2;
break;
case PSP_CODEC_AT3PLUS:
{
int bytesPerFrame, channels;
CalculateInputBytesAndChannelsAt3Plus(ctx, &bytesPerFrame, &channels);
if (channels == 0) {
ctx->err = 0x202;
return MECall(hleLogError(Log::ME, SCE_AVCODEC_ERROR_INVALID_DATA, "bad Atrac3+ format byte %02x", ctx->fmt.at3.formatByte1), InitUs(codec, ctx));
}
primingFrames = 1;
break;
}
case PSP_CODEC_AT3:
{
int bytesPerFrame, channels;
bool jointStereo;
if (!Atrac3LayoutFromContext(ctx, &bytesPerFrame, &channels, &jointStereo)) {
ctx->err = 0x186;
return MECall(hleLogError(Log::ME, SCE_AVCODEC_ERROR_INVALID_DATA, "bad Atrac3 parameter %08x", *(const u32_le *)ctx->fmt.raw), InitUs(codec, ctx));
}
break;
}
default:
break;
}
CreateDecoderForContext(ctxPtr, audioType);
return hleLogDebug(Log::ME, 0);
// Not in CreateDecoderForContext: a state load restores what's left of it instead.
g_primingFrames[ctxPtr] = primingFrames;
return MECall(hleLogDebug(Log::ME, 0), InitUs(codec, ctx));
}
// How long the ME takes over one frame, in microseconds at the default 222MHz clock. Fitted to
// sceAudiocodecDecode timed on a PSP (pspautotests audio/audiocodec/timing), which at 222MHz gave:
// Atrac3+ stereo: 2410us at 376 bytes/frame, 2990 at 744. Mono: 2126 at 744.
// Atrac3 stereo: 1138 at 0x180, 1063 at 0xC0 joint stereo. Mono: 685 at 0x98.
// MP3 MPEG1 (1152 samples): 2575 at 418 bytes, 2698 at 1045. MPEG2 (576): 1411 at 104, 1464 at 209.
// AAC-LC stereo 44.1kHz: 1651 at ~190 bytes, 1990 at ~373, 2042 at ~559.
// The mono Atrac3+ slope is a guess from its one data point. Content matters as well as size: the
// synthetic two-tone AAC in video/mp4 decodes about 15% faster than music at the same bitrate.
static int EstimateDecodeUs(int codec, int channels, int frameBytes, const SceAudiocodecCodec *ctx) {
switch (codec) {
case PSP_CODEC_AT3PLUS:
return channels == 1 ? 1500 + frameBytes * 84 / 100 : 1815 + frameBytes * 158 / 100;
case PSP_CODEC_AT3:
return channels == 1 ? 685 : 970 + frameBytes * 44 / 100;
case PSP_CODEC_MP3:
return ctx->fmt.mp3.version == 1 ? 2492 + frameBytes / 5 : 1358 + frameBytes / 2;
case PSP_CODEC_AAC:
return 1400 + std::min(frameBytes, 400) * 16 / 10;
default:
return 0;
}
}
static int sceAudiocodecInit(u32 ctxPtr, int codec) {
@@ -336,8 +425,16 @@ static int sceAudiocodecDecode(u32 ctxPtr, int codec) {
switch (codec) {
case PSP_CODEC_AT3PLUS:
CalculateInputBytesAndChannelsAt3Plus(ctx, &bytesPerFrame, &channels, &headerBytes);
{
const int frameError = CalculateInputBytesAndChannelsAt3Plus(ctx, &bytesPerFrame, &channels, &headerBytes);
if (frameError) {
ctx->err = frameError;
ctx->srcBytesRead = 0;
ctx->dstBytesWritten = 0;
return MECall(hleLogWarning(Log::ME, SCE_AVCODEC_ERROR_INVALID_DATA, "Atrac3+ frame doesn't match the context: err %03x", frameError), 142);
}
break;
}
case PSP_CODEC_MP3:
// Not srcBytesRead - that's an output field holding what the *previous* call consumed.
// The hardware uses the bound at 0x28, which the caller also guarantees is readable at
@@ -359,9 +456,12 @@ static int sceAudiocodecDecode(u32 ctxPtr, int codec) {
sampleRate = ctx->fmt.aac.sampleRate;
break;
case PSP_CODEC_AT3:
bytesPerFrame = Atrac3BytesPerFrameFromContext(ctx, &channels);
{
bool jointStereo;
Atrac3LayoutFromContext(ctx, &bytesPerFrame, &channels, &jointStereo);
break;
}
}
// find a decoder in audioList
auto decoder = findDecoder(ctxPtr);
@@ -377,14 +477,18 @@ static int sceAudiocodecDecode(u32 ctxPtr, int codec) {
if (it == g_at3PlusFrameBytes.end() || it->second != bytesPerFrame) {
// Only reachable when the context didn't carry a frame size at init - mpeg.prx.
INFO_LOG(Log::ME, "sceAudiocodecDecode: Atrac3+ frame is %04x bytes, rebuilding decoder", bytesPerFrame);
// The hardware has one decoder throughout, so this doesn't restart the priming.
const int primingFrames = g_primingFrames[ctxPtr];
removeDecoder(ctxPtr);
decoder = CreateAudioDecoder(audioType, 44100, channels, bytesPerFrame);
decoder->SetCtxPtr(ctxPtr);
g_audioDecoderContexts[ctxPtr] = decoder;
g_at3PlusFrameBytes[ctxPtr] = bytesPerFrame;
g_primingFrames[ctxPtr] = primingFrames;
}
}
int decodeUs = 0;
if (decoder) {
// Use SimpleAudioDec to decode audio
// Decode audio
@@ -409,9 +513,24 @@ static int sceAudiocodecDecode(u32 ctxPtr, int codec) {
}
bool result = decoder->Decode(inBuf, bytesPerFrame, &inDataConsumed, 2, outBuf, &outSamples);
if (!result && (codec == PSP_CODEC_AT3PLUS || codec == PSP_CODEC_AT3)) {
// What the hardware reports for a frame that doesn't decode (0x208 is also possible for
// Atrac3+, depending on how far into the frame the problem is).
ctx->err = codec == PSP_CODEC_AT3PLUS ? 0x20a : 0x182;
ctx->srcBytesRead = 0;
ctx->dstBytesWritten = 0;
return MECall(hleLogWarning(Log::ME, SCE_AVCODEC_ERROR_INVALID_DATA, "%s frame failed to decode", GetCodecName(codec)), codec == PSP_CODEC_AT3PLUS ? 214 : 169);
}
if (!result) {
ctx->err = 0x20b;
ERROR_LOG(Log::ME, "AudioCodec decode failed. Setting error to %08x", ctx->err);
} else {
ctx->err = 0;
auto priming = g_primingFrames.find(ctxPtr);
if (priming != g_primingFrames.end() && priming->second > 0) {
priming->second--;
outSamples = 0;
}
}
ctx->srcBytesRead = inDataConsumed + headerBytes;
@@ -420,17 +539,8 @@ static int sceAudiocodecDecode(u32 ctxPtr, int codec) {
// reporting the sample count instead gave it a quarter of every frame, which played back
// fast and metallic. The decoder always writes stereo 16-bit, whatever the source is.
ctx->dstBytesWritten = outSamples * 2 * (int)sizeof(int16_t);
}
// The decode runs on the ME and takes real time. Measured on a PSP through the libraries that
// call this, so each includes their own work around the call: sceMpegAtracDecode of one ATRAC3+
// frame (2048 samples) takes about 2.5ms (pspautotests video/mpeg/playertiming), and
// sceMp4AacDecode of one AAC-LC stereo frame (1024 samples) about 1.7ms (video/mp4/mp4timing).
// Other codecs aren't measured yet.
int decodeUs = 0;
if (audioType == PSP_CODEC_AT3PLUS) {
decodeUs = 2500;
} else if (audioType == PSP_CODEC_AAC) {
decodeUs = 1700;
decodeUs = EstimateDecodeUs(codec, channels, inDataConsumed, ctx);
}
if (decodeUs > 0) {
decodeUs = MEScheduleJob(PowerScaleFromDefaultClock(decodeUs));
@@ -493,6 +603,9 @@ static int sceAudiocodecGetInfo(u32 ctxPtr, int codec) {
}
}
if (codec == PSP_CODEC_MP3 || codec == PSP_CODEC_AT3PLUS) {
return MECall(hleLogInfo(Log::ME, 0, "codec=%s", GetCodecName(codec)), 82);
}
return hleLogInfo(Log::ME, 0, "codec=%s", GetCodecName(codec));
}
@@ -508,17 +621,16 @@ static int sceAudiocodecCheckNeedMem(u32 ctxPtr, int codec) {
// Check for expected values.
auto ctx = PSPPointer<SceAudiocodecCodec>::Create(ctxPtr); // On game-owned heap, no need to allocate.
// The sizes are fixed per codec: on hardware they don't depend on the Atrac3+ format bytes,
// inited, or the AAC sample rate.
switch (codec) {
case 0x1000:
ctx->neededMem = 0x7bc0;
// avcodec.prx does no format check here at all, it just forwards to the ME.
// libatrac3plus writes 28 5c (the worst case it sizes EDRAM against).
// mpeg.prx writes the real frame's own header bytes. Let's just log if we find
// something unusual here, it might mean something.
if (ctx->fmt.at3.formatByte1 != 0x28 && ctx->fmt.at3.formatByte1 != 0x24) {
INFO_LOG(Log::ME, "sceAudiocodecCheckNeedMem: unfamiliar Atrac3+ format bytes %02x %02x",
ctx->fmt.at3.formatByte1, ctx->fmt.at3.formatByte2);
// The ME does look at the format bytes, though: both zero fails.
if (ctx->fmt.at3.formatByte1 == 0 && ctx->fmt.at3.formatByte2 == 0) {
ctx->err = 0x20f;
return MECall(hleLogError(Log::ME, SCE_AVCODEC_ERROR_INVALID_DATA, "no Atrac3+ format"), 147);
}
ctx->neededMem = 0x7bc0;
break;
case 0x1001:
ctx->neededMem = 0x3de0;
@@ -528,15 +640,22 @@ static int sceAudiocodecCheckNeedMem(u32 ctxPtr, int codec) {
break;
case 0x1003:
// Kosmodrones uses sceAudiocodec directly (no intermediate library).
ctx->neededMem = 0x18f20;
INFO_LOG(Log::ME, "CheckNeedMem for codec %04x: format %02x %02x", codec, ctx->fmt.at3.formatByte1, ctx->fmt.at3.formatByte2);
ctx->neededMem = 0x658c;
break;
case 0x1004:
return hleLogError(Log::ME, SCE_AVCODEC_ERROR_UNSUPPORTED, "codec 1004");
case 0x1005:
// What the hardware does, without asking the ME. Looks like an error code in the size field.
ctx->neededMem = 0x80000003;
ctx->err = 0;
return hleLogWarning(Log::ME, 0, "codec 1005");
}
ctx->err = 0;
ctx->magic = 0x5100601;
return hleLogInfo(Log::ME, 0, "%s: %x", GetCodecName(codec), ctx->neededMem);
static const int needMemUs[4] = { 147, 80, 79, 57 };
return MECall(hleLogInfo(Log::ME, 0, "%s: %x", GetCodecName(codec), ctx->neededMem), needMemUs[codec - 0x1000]);
}
static int sceAudiocodecGetEDRAM(u32 ctxPtr, int codec) {
@@ -552,7 +671,7 @@ static int sceAudiocodecGetEDRAM(u32 ctxPtr, int codec) {
break;
}
ctx->edramAddr = (ctx->allocMem + 0x3f) & ~0x3f; // round up to 64 bytes.
return hleLogInfo(Log::ME, 0, "edram address set to %08x", ctx->edramAddr);
return MECall(hleLogInfo(Log::ME, 0, "edram address set to %08x", ctx->edramAddr), 56);
}
// One parameter, not two: the real module (audiocodec_260.prx, 080007c0) reads only a0 and sets
@@ -562,10 +681,13 @@ static int sceAudiocodecGetEDRAM(u32 ctxPtr, int codec) {
// CheckNeedMem/GetEDRAM to size the allocation and only creates a decoder if the stream turns out
// to need one, so there is often nothing here to drop.
static int sceAudiocodecReleaseEDRAM(u32 ctxPtr) {
if (removeDecoder(ctxPtr)) {
return hleLogInfo(Log::ME, 0);
if (Memory::IsValidRange(ctxPtr, sizeof(SceAudiocodecCodec))) {
PSPPointer<SceAudiocodecCodec>::Create(ctxPtr)->edramAddr = 0;
}
return hleLogDebug(Log::ME, 0, "no decoder for this context");
if (removeDecoder(ctxPtr)) {
return MECall(hleLogInfo(Log::ME, 0), 56);
}
return MECall(hleLogDebug(Log::ME, 0, "no decoder for this context"), 56);
}
static int sceAudiocodecGetOutputBytes(u32 ctxPtr, int codec, u32 outBytesAddr) {
@@ -587,40 +709,43 @@ static int sceAudiocodecGetOutputBytes(u32 ctxPtr, int codec, u32 outBytesAddr)
return hleLogInfo(Log::ME, 0);
}
struct At3HeaderMap {
// The Atrac3 parameter at 0x28, as libatrac3plus.prx's table at 08807f08 maps it from frame size
// and joint stereo. The hardware decoded 0x04, 0x0B and 0x0F streams as listed.
struct At3Param {
u8 param;
u16 bytes;
u16 channels;
u8 channels;
u8 jointStereo;
bool Matches(int bytesPerFrame, int encodedChannels) const {
return this->bytes == bytesPerFrame && this->channels == encodedChannels;
}
};
// These should represent all possible supported bitrates (66, 104, and 132 for stereo.)
static const At3HeaderMap at3HeaderMap[] = {
{ 0x00C0, 1, 0 }, // 132/2 (66) kbps mono
{ 0x0098, 1, 0 }, // 105/2 (52.5) kbps mono
{ 0x0180, 2, 0 }, // 132 kbps stereo
{ 0x0130, 2, 0 }, // 105 kbps stereo
// At this size, stereo can only use joint stereo.
{ 0x00C0, 2, 1 }, // 66 kbps stereo
static const At3Param at3Params[] = {
{ 0x04, 0x0180, 2, 0 },
{ 0x06, 0x0130, 2, 0 },
{ 0x0B, 0x00C0, 2, 1 },
{ 0x0E, 0x00C0, 1, 0 },
{ 0x0F, 0x0098, 1, 0 },
};
static int Atrac3BytesPerFrameFromContext(const SceAudiocodecCodec *ctx, int *channels) {
// AtracCtx2 puts the joint-stereo flag here for Atrac3, mirroring libatrac3plus.
const bool jointStereo = (ctx->fmt.at3.formatByte1 & 1) != 0;
if (jointStereo) {
for (size_t i = 0; i < ARRAY_SIZE(at3HeaderMap); ++i) {
if (at3HeaderMap[i].jointStereo) {
*channels = at3HeaderMap[i].channels;
return at3HeaderMap[i].bytes;
}
// Returns false for a parameter the hardware rejects.
static bool Atrac3LayoutFromContext(const SceAudiocodecCodec *ctx, int *bytesPerFrame, int *channels, bool *jointStereo) {
const u32 param = *(const u32_le *)ctx->fmt.raw;
for (const At3Param &p : at3Params) {
if (p.param == param) {
*bytesPerFrame = p.bytes;
*channels = p.channels;
*jointStereo = p.jointStereo != 0;
return true;
}
}
// 132kbps stereo - by far the most common, and what we assumed unconditionally before.
// Not in libatrac3plus.prx's table. Fall back to 132kbps stereo, the most common.
*bytesPerFrame = 0x180;
*channels = 2;
return 0x180;
*jointStereo = false;
if (param > 0x0F) {
return false;
}
WARN_LOG(Log::ME, "Unknown Atrac3 parameter %02x", param);
return true;
}
bool IsAtrac3StreamJointStereo(int codecType, int bytesPerFrame, int channels) {
@@ -629,9 +754,9 @@ bool IsAtrac3StreamJointStereo(int codecType, int bytesPerFrame, int channels) {
return false;
}
for (size_t i = 0; i < ARRAY_SIZE(at3HeaderMap); ++i) {
if (at3HeaderMap[i].Matches(bytesPerFrame, channels)) {
return at3HeaderMap[i].jointStereo;
for (const At3Param &p : at3Params) {
if (p.bytes == bytesPerFrame && p.channels == channels) {
return p.jointStereo != 0;
}
}
@@ -656,7 +781,7 @@ void Register_sceAudiocodec() {
}
void __sceAudiocodecDoState(PointerWrap &p){
auto s = p.Section("AudioList", 0, 2);
auto s = p.Section("AudioList", 0, 3);
if (!s) {
if (p.mode == PointerWrap::MODE_READ) {
clearDecoders();
@@ -727,4 +852,10 @@ void __sceAudiocodecDoState(PointerWrap &p){
delete[] ctxPtr_;
}
}
if (s >= 3) {
Do(p, g_primingFrames);
} else if (p.mode == PointerWrap::MODE_READ) {
g_primingFrames.clear();
}
}
+55 -19
View File
@@ -436,6 +436,34 @@ static void WriteTiledYCbCr(const u32 *buffers, const AvcDecoder &dec, int width
}
}
// Every call here that reaches the ME blocks while it answers. Measured at 222MHz in pspautotests
// video/mp4/mp4timing: Open, GetEDRAM, GetVersion and ReleaseEDRAM called directly, the rest through
// mpeg.prx's thin wrappers. sceMpegCreate (Open, Init, GetVersion, SetMemory) took 26.9-28.0ms,
// nearly all of it Init and SetMemory, which are charged together to Init here. sceMpegDelete took
// 21.2ms, and 35.9ms in a run that had stopped the decoder twice first. An sceMpegAvcDecodeStop with
// nothing held back took 132us (with three pictures to hand over, 4.9ms, the rest being their output).
static const int openUs = 96;
static const int getEdramUs = 146;
static const int getVersionUs = 94;
static const int releaseEdramUs = 66;
static const int initUs = 26600;
static const int stopUs = 132;
static const int deleteUs = 21000;
static int MECall(int result, const char *reason, int us) {
return hleDelayResult(result, reason, MEScheduleJob(PowerScaleFromDefaultClock(us)));
}
// Init and Delete take tens of milliseconds for the caller, but they don't hold the ME for that
// long: queueing them there stalled the SAS mix behind them, and with it Jak and Daxter's sound
// threads, whose last wake to video_sound_thread then came after the game had deleted it
// (NOT_DORMANT, then the orphan reads a freed context). On hardware SAS keeps mixing through them:
// pspautotests audio/timing/meshare saw ~1.3ms SAS calls throughout a 39ms sceMpegCreate and a 32ms
// sceMpegDelete, the longest 2.2ms.
static int CallerWait(int result, const char *reason, int us) {
return hleDelayResult(result, reason, PowerScaleFromDefaultClock(us));
}
static int sceVideocodecOpen(u32 ctxAddr, int type) {
if (!Memory::IsValidRange(ctxAddr, 96)) {
return hleLogError(Log::ME, -1, "bad context pointer");
@@ -445,7 +473,11 @@ static int sceVideocodecOpen(u32 ctxAddr, int type) {
return hleLogError(Log::ME, -1, "built without ffmpeg, can't decode video");
}
g_videocodecCtxs[ctxAddr].type = type;
return hleLogInfo(Log::ME, 0, "type %d", type);
if (type == 0) {
// The EDRAM the decoder needs, which mpeg.prx reads back and passes to GetEDRAM.
Memory::WriteUnchecked_U32(0x3c2c, ctxAddr + CTX_EDRAM_SIZE);
}
return MECall(hleLogInfo(Log::ME, 0, "type %d", type), "videocodec open", openUs);
}
static int sceVideocodecInit(u32 ctxAddr, int type) {
@@ -458,7 +490,7 @@ static int sceVideocodecInit(u32 ctxAddr, int type) {
vctx.decoder = new AvcDecoder();
vctx.frameCount = 0;
vctx.type = type;
return hleLogInfo(Log::ME, 0, "type %d", type);
return CallerWait(hleLogInfo(Log::ME, 0, "type %d", type), "videocodec init", initUs);
}
// See g_meRam for why this doesn't come out of the game's memory.
@@ -483,7 +515,7 @@ static int sceVideocodecGetEDRAM(u32 ctxAddr, int type) {
// works in 64-byte grains, so the two only differ in what they mean, not in value.
Memory::WriteUnchecked_U32(addr, ctxAddr + CTX_EDRAM);
Memory::WriteUnchecked_U32(addr, ctxAddr + CTX_EDRAM_RAW);
return hleLogInfo(Log::ME, 0, "%u bytes at %08x in ME memory", size, addr);
return MECall(hleLogInfo(Log::ME, 0, "%u bytes at %08x in ME memory", size, addr), "videocodec getedram", getEdramUs);
}
static int sceVideocodecReleaseEDRAM(u32 ctxAddr) {
@@ -502,7 +534,7 @@ static int sceVideocodecReleaseEDRAM(u32 ctxAddr) {
}
Memory::WriteUnchecked_U32(0, ctxAddr + CTX_EDRAM);
Memory::WriteUnchecked_U32(0, ctxAddr + CTX_EDRAM_RAW);
return hleLogInfo(Log::ME, 0, "released %08x", token);
return MECall(hleLogInfo(Log::ME, 0, "released %08x", token), "videocodec releaseedram", releaseEdramUs);
}
static int sceVideocodecDecode(u32 ctxAddr, int type) {
@@ -616,34 +648,38 @@ static int sceVideocodecDecode(u32 ctxAddr, int type) {
// sceMpegAvcDecode (5.8ms) less sceMpegAvcCsc alone (2.4ms), in pspautotests
// video/mpeg/playertiming. Movie players that present every decoded frame after a single
// vblank wait rely on decode, colour conversion and blit adding up to more than a vblank.
// It takes as long when the decoder holds the picture back: the first sceMpegAvcDecode calls of
// a stream, which return none, took 4.2-5.4ms in video/mp4/mp4timing. Until a picture has told
// us the size, assume full screen.
int delayUs = 0;
if (gotFrame && width > 0 && height > 0) {
delayUs = MEScheduleJob(PowerScaleFromDefaultClock((int)(3400LL * width * height / (480 * 272))));
if (auBytes > 0) {
const int w = vctx.decoder->Width() > 0 ? vctx.decoder->Width() : 480;
const int h = vctx.decoder->Height() > 0 ? vctx.decoder->Height() : 272;
delayUs = MEScheduleJob(PowerScaleFromDefaultClock((int)(3400LL * w * h / (480 * 272))));
}
if (delayUs > 0) {
return hleDelayResult(hleLogDebug(Log::ME, 0, "type %d, %d bytes -> frame %dx%d",
type, auBytes, width, height), "videocodec decode", delayUs);
return hleDelayResult(hleLogDebug(Log::ME, 0, "type %d, %d bytes -> %s %dx%d",
type, auBytes, gotFrame ? "frame" : "no frame yet", width, height), "videocodec decode", delayUs);
}
return hleLogDebug(Log::ME, 0, "type %d, %d bytes -> %s %dx%d",
type, auBytes, gotFrame ? "frame" : "no frame yet", width, height);
}
// Stopping or deleting the decoder is an ME round-trip and takes real time on hardware. Returning
// immediately is not correct, because a game can be relying on a thread of its own getting to run
// once more before it tears things down. Jak and Daxter deletes its video_sound_thread straight
// after sceVideocodecDelete without waiting for it to exit, and with no time passing here the audio
// thread never gets to deliver the wake that would let it exit - so the delete fails with
// NOT_DORMANT and the thread lives on, reading a context the game has already freed.
// One audio mix block is 64 samples at 44100Hz, about 1.45ms, so stay above that.
static const int videocodecTeardownDelayUs = 2000;
// Returning immediately from Stop or Delete is not correct, because a game can be relying on a
// thread of its own getting to run once more before it tears things down. Jak and Daxter deletes
// its video_sound_thread straight after sceVideocodecDelete without waiting for it to exit, and
// with no time passing here the audio thread never gets to deliver the wake that would let it exit
// - so the delete fails with NOT_DORMANT and the thread lives on, reading a context the game has
// already freed. One audio mix block is 64 samples at 44100Hz, about 1.45ms, which Delete's
// measured time is well above.
static int sceVideocodecStop(u32 ctxAddr, int type) {
auto it = g_videocodecCtxs.find(ctxAddr);
if (it != g_videocodecCtxs.end() && it->second.decoder) {
it->second.decoder->Flush();
}
return hleDelayResult(hleLogInfo(Log::ME, 0), "videocodec stop", videocodecTeardownDelayUs);
return MECall(hleLogInfo(Log::ME, 0), "videocodec stop", stopUs);
}
static int sceVideocodecDelete(u32 ctxAddr, int type) {
@@ -652,7 +688,7 @@ static int sceVideocodecDelete(u32 ctxAddr, int type) {
FreeContext(it->second);
g_videocodecCtxs.erase(it);
}
return hleDelayResult(hleLogInfo(Log::ME, 0), "videocodec delete", videocodecTeardownDelayUs);
return CallerWait(hleLogInfo(Log::ME, 0), "videocodec delete", deleteUs);
}
static int sceVideocodecGetVersion(u32 ctxAddr, int type) {
@@ -661,7 +697,7 @@ static int sceVideocodecGetVersion(u32 ctxAddr, int type) {
}
// The value a real PSP returns, read with JpcspTrace.
Memory::WriteUnchecked_U32(0x78, ctxAddr + CTX_VERSION);
return hleLogInfo(Log::ME, 0);
return MECall(hleLogInfo(Log::ME, 0), "videocodec getversion", getVersionUs);
}
static int sceVideocodecGetSEI(u32 ctxAddr, int type) {