sceVideocodec/sceMpegbase: stop holding on to contexts and payloads that are done

Decode looked its context up with operator[], so a game that never opened one
would still get an entry and a decoder, and only Delete ever removes those.
Require the context to exist instead.

The gathered PES payloads were likewise kept for the whole boot, so the first
decode of a movie could be handed the last packet of the previous one if the
address came round again. Hand each payload out once.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
This commit is contained in:
Henrik RydgårdandClaude Opus 5 authored and Henrik Rydgård committed 2026-09-12 12:53:13 -06:00
1 parent 779f9eb811
commit 435d446c37
3 files changed
+22 -13

No files matched your search

+10 -4
View File
@@ -36,7 +36,6 @@
// copied to. It carries audio as well as video - the destination is what tells them apart - so
// sceVideocodec has to ask for the one matching the address it was handed.
static std::map<u32, std::vector<u8>> g_pesPackets;
// Set by sceMpegBaseCscInit / sceMpegBaseCscSetPixelMode, and used when the caller passes 0.
static int g_mpegBaseBufferWidth = 512;
static int g_mpegBasePixelMode = GE_CMODE_32BIT_ABGR8888;
@@ -66,7 +65,7 @@ static u32 sceMpegBasePESpacketCopy(u32 p)
// On hardware this is the DMA that moves the PES payload into the Media Engine's own memory,
// after which mpeg.prx hands sceVideocodecDecode an ME-side address we have no way to read.
// Since the copy is ours, gather the blocks here instead and let sceVideocodec decode from
// this - see MpegBaseGetPESPacket.
// this - see MpegBaseTakePESPacket.
lli = PSPPointer<SceMpegLLI>::Create(p);
u32 dest = 0;
std::vector<u8> gathered;
@@ -100,9 +99,16 @@ static u32 sceMpegBasePESpacketCopy(u32 p)
return 0;
}
const std::vector<u8> *MpegBaseGetPESPacket(u32 dest) {
std::vector<u8> MpegBaseTakePESPacket(u32 dest) {
auto it = g_pesPackets.find(dest);
return it == g_pesPackets.end() ? nullptr : &it->second;
if (it == g_pesPackets.end()) {
return std::vector<u8>();
}
// Handed over, not lent: leaving it behind meant the first decode of the next movie could pick
// up the last packet of the previous one, if the address came round again.
std::vector<u8> packet = std::move(it->second);
g_pesPackets.erase(it);
return packet;
}
+5 -4
View File
@@ -26,7 +26,8 @@ void Register_sceMpegbase();
// Called per boot, from __MpegInit.
void __MpegBaseInit();
// The PES payload sceMpegBasePESpacketCopy gathered for a given destination. On hardware that copy
// lands in Media Engine memory, which sceVideocodec would then read back; we keep it here instead.
// Returns nullptr if nothing was copied to that address.
const std::vector<u8> *MpegBaseGetPESPacket(u32 dest);
// Takes the PES payload sceMpegBasePESpacketCopy gathered for a given destination. On hardware that
// copy lands in Media Engine memory, which sceVideocodec would then read back; we keep it here
// instead. The payload is moved out and dropped from the table, so each one is decoded once.
// Empty if nothing was copied to that address.
std::vector<u8> MpegBaseTakePESPacket(u32 dest);
+7 -5
View File
@@ -500,15 +500,17 @@ static int sceVideocodecDecode(u32 ctxAddr, int type) {
bool gotFrame = false;
const u8 *au = nullptr;
int auBytes = 0;
// Owns the gathered payload for as long as au points into it.
std::vector<u8> pes;
if (auSize > 0 && Memory::IsValidRange(auAddr, auSize)) {
au = Memory::GetTypedPointerRange<u8>(auAddr, auSize);
auBytes = auSize;
} else {
// Ask for the payload copied to this exact address - the same call carries audio too.
const std::vector<u8> *pes = MpegBaseGetPESPacket(auAddr);
if (pes && !pes->empty()) {
au = pes->data();
auBytes = (int)pes->size();
// Take the payload copied to this exact address - the same call carries audio too.
pes = MpegBaseTakePESPacket(auAddr);
if (!pes.empty()) {
au = pes.data();
auBytes = (int)pes.size();
}
}
if (au && auBytes > 0) {