From deffb246aebc8505ec7e1ede001b4457b7100348 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 4 Jun 2016 14:05:34 -0700 Subject: [PATCH 1/8] Mpeg: Require less memory for older lib versions. It seems it started requiring 64k at 1.05. --- Core/HLE/sceMpeg.cpp | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/Core/HLE/sceMpeg.cpp b/Core/HLE/sceMpeg.cpp index 0b355cf24ef5..4fda18f51a67 100644 --- a/Core/HLE/sceMpeg.cpp +++ b/Core/HLE/sceMpeg.cpp @@ -68,8 +68,9 @@ static const int MPEG_DATA_STREAM = 3; // Arbitrary user defined type. Can static const int MPEG_AUDIO_STREAM = 15; static const int MPEG_AU_MODE_DECODE = 0; static const int MPEG_AU_MODE_SKIP = 1; -static const u32 MPEG_MEMSIZE = 0x10000; // 64k. -static const int MPEG_AVC_DECODE_SUCCESS = 1; // Internal value. +static const u32 MPEG_MEMSIZE_0104 = 0x0b3DB; +static const u32 MPEG_MEMSIZE_0105 = 0x10000; // 64k. +static const int MPEG_AVC_DECODE_SUCCESS = 1; // Internal value. static const int atracDecodeDelayMs = 3000; static const int avcFirstDelayMs = 3600; @@ -467,6 +468,13 @@ static u32 sceMpegRingbufferConstruct(u32 ringbufferAddr, u32 numPackets, u32 da return 0; } +static u32 MpegRequiredMem() { + if (mpegLibVersion < 0x0105) { + return MPEG_MEMSIZE_0104; + } + return MPEG_MEMSIZE_0105; +} + static u32 sceMpegCreate(u32 mpegAddr, u32 dataPtr, u32 size, u32 ringbufferAddr, u32 frameWidth, u32 mode, u32 ddrTop) { if (!Memory::IsValidAddress(mpegAddr)) { @@ -474,7 +482,7 @@ static u32 sceMpegCreate(u32 mpegAddr, u32 dataPtr, u32 size, u32 ringbufferAddr return -1; } - if (size < MPEG_MEMSIZE) { + if (size < MpegRequiredMem()) { WARN_LOG(ME, "ERROR_MPEG_NO_MEMORY=sceMpegCreate(%08x, %08x, %i, %08x, %i, %i, %i)", mpegAddr, dataPtr, size, ringbufferAddr, frameWidth, mode, ddrTop); return ERROR_MPEG_NO_MEMORY; } @@ -1537,10 +1545,8 @@ static u32 sceMpegFinish() return hleDelayResult(0, "mpeg finish", 250); } -static u32 sceMpegQueryMemSize() -{ - DEBUG_LOG(ME, "%i = sceMpegQueryMemSize()",MPEG_MEMSIZE); - return MPEG_MEMSIZE; +static u32 sceMpegQueryMemSize() { + return hleLogSuccessX(ME, MpegRequiredMem()); } static int sceMpegGetAtracAu(u32 mpeg, u32 streamId, u32 auAddr, u32 attrAddr) From 6d14c17f26694e36bc1ff6eec7d5ecddcc9afc5c Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 4 Jun 2016 14:17:29 -0700 Subject: [PATCH 2/8] Mpeg: Treat negative available packets as zero. --- Core/HLE/sceMpeg.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Core/HLE/sceMpeg.cpp b/Core/HLE/sceMpeg.cpp index 4fda18f51a67..248868a52f40 100644 --- a/Core/HLE/sceMpeg.cpp +++ b/Core/HLE/sceMpeg.cpp @@ -1414,7 +1414,7 @@ void PostPutAction::run(MipsCall &call) { // Program signals that it has written data to the ringbuffer and gets a callback ? -static u32 sceMpegRingbufferPut(u32 ringbufferAddr, u32 numPackets, u32 available) +static u32 sceMpegRingbufferPut(u32 ringbufferAddr, int numPackets, int available) { numPackets = std::min(numPackets, available); if (numPackets <= 0) { @@ -1444,7 +1444,7 @@ static u32 sceMpegRingbufferPut(u32 ringbufferAddr, u32 numPackets, u32 availabl // TODO: Should call this multiple times until we get numPackets. // Normally this would be if it did not read enough, but also if available > packets. // Should ultimately return the TOTAL number of returned packets. - u32 packetsThisRound = std::min(numPackets, (u32)ringbuffer->packets); + u32 packetsThisRound = std::min(numPackets, (s32)ringbuffer->packets); u32 args[3] = {(u32)ringbuffer->data, packetsThisRound, (u32)ringbuffer->callback_args}; __KernelDirectMipsCall(ringbuffer->callback_addr, action, args, 3, false); } else { @@ -2184,7 +2184,7 @@ const HLEFunction sceMpeg[] = {0XA11C7026, &WrapI_UU, "sceMpegAvcDecodeMode", 'i', "xx" }, {0X37295ED8, &WrapU_UUUUUU, "sceMpegRingbufferConstruct", 'x', "xxxxxx" }, {0X13407F13, &WrapU_U, "sceMpegRingbufferDestruct", 'x', "x" }, - {0XB240A59E, &WrapU_UUU, "sceMpegRingbufferPut", 'x', "xxx" }, + {0XB240A59E, &WrapU_UII, "sceMpegRingbufferPut", 'x', "xxx" }, {0XB5F6DC87, &WrapI_U, "sceMpegRingbufferAvailableSize", 'i', "x" }, {0XD7A29F46, &WrapU_I, "sceMpegRingbufferQueryMemSize", 'x', "i" }, {0X769BEBB6, &WrapI_U, "sceMpegRingbufferQueryPackNum", 'i', "x" }, From b191fd29145507cf872280efe26f00084e5516b7 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 4 Jun 2016 16:09:38 -0700 Subject: [PATCH 3/8] Mpeg: Make sure not to demux outside the buffer. --- Core/HW/MpegDemux.cpp | 4 ++-- Core/HW/MpegDemux.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Core/HW/MpegDemux.cpp b/Core/HW/MpegDemux.cpp index b42403630fea..74e39b1a997d 100644 --- a/Core/HW/MpegDemux.cpp +++ b/Core/HW/MpegDemux.cpp @@ -158,7 +158,7 @@ void MpegDemux::demux(int audioChannel) { if (audioChannel >= 0) m_audioChannel = audioChannel; - while (m_index < m_len) + while (m_index < m_readSize) { if (m_index + 2048 > m_readSize) break; @@ -202,7 +202,7 @@ void MpegDemux::demux(int audioChannel) } if (m_index < m_readSize) { int size = m_readSize - m_index; - memcpy(m_buf, m_buf + m_index, size); + memmove(m_buf, m_buf + m_index, size); m_index = 0; m_readSize = size; } else { diff --git a/Core/HW/MpegDemux.h b/Core/HW/MpegDemux.h index 1394cd860423..6e3756ad4892 100644 --- a/Core/HW/MpegDemux.h +++ b/Core/HW/MpegDemux.h @@ -53,7 +53,7 @@ class MpegDemux return (((s64) (c & 0x0E)) << 29) | ((read16() >> 1) << 15) | (read16() >> 1); } bool isEOF() const { - return m_index >= m_len; + return m_index >= m_readSize; } void skip(int n) { if (n > 0) { From 4d168359907f574771d7478ba44946f68e38a4e1 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 4 Jun 2016 16:12:23 -0700 Subject: [PATCH 4/8] Mpeg: Validate the pack header. --- Core/HW/MpegDemux.cpp | 35 +++++++++++++++++++++++++++++++++-- Core/HW/MpegDemux.h | 4 ++++ 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/Core/HW/MpegDemux.cpp b/Core/HW/MpegDemux.cpp index 74e39b1a997d..0b037305137b 100644 --- a/Core/HW/MpegDemux.cpp +++ b/Core/HW/MpegDemux.cpp @@ -154,6 +154,37 @@ int MpegDemux::demuxStream(bool bdemux, int startCode, int channel) return channel; } +bool MpegDemux::skipPackHeader() { + // MPEG version / SCR + if ((read8() & 0xC4) != 0x44) { + return false; + } + skip(1); + if ((read8() & 0x04) != 0x04) { + return false; + } + skip(1); + if ((read8() & 0x04) != 0x04) { + return false; + } + // SCR_ext + if ((read8() & 0x01) != 0x01) { + return false; + } + + int muxrate = read24(); + if ((muxrate & 3) != 3) { + return false; + } + int stuffing = read8() & 7; + while (stuffing > 0) { + if (read8() != 0xFF) { + return false; + } + } + return true; +} + void MpegDemux::demux(int audioChannel) { if (audioChannel >= 0) @@ -169,10 +200,10 @@ void MpegDemux::demux(int audioChannel) } switch (startCode) { case PACK_START_CODE: - skip(10); + skipPackHeader(); break; case SYSTEM_HEADER_START_CODE: - skip(14); + skip(read16()); break; case PADDING_STREAM: case PRIVATE_STREAM_2: diff --git a/Core/HW/MpegDemux.h b/Core/HW/MpegDemux.h index 6e3756ad4892..30f98082d883 100644 --- a/Core/HW/MpegDemux.h +++ b/Core/HW/MpegDemux.h @@ -46,6 +46,9 @@ class MpegDemux int read16() { return (read8() << 8) | read8(); } + int read24() { + return (read8() << 16) | (read8() << 8) | read8(); + } s64 readPts() { return readPts(read8()); } @@ -62,6 +65,7 @@ class MpegDemux } int readPesHeader(PesHeader &pesHeader, int length, int startCode); int demuxStream(bool bdemux, int startCode, int channel); + bool skipPackHeader(); int m_index; int m_len; From 41f2999a2b53225276d893c072a3171f5fba71cd Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 4 Jun 2016 17:34:49 -0700 Subject: [PATCH 5/8] Mpeg: Write into the ringbuffer using actual ring. This mimics behavior with clamped sizes and wrap around, and also makes it easier to implement the garbage data handling the PSP has. --- Core/HLE/sceMpeg.cpp | 16 ++++++++++------ Core/HLE/sceMpeg.h | 3 ++- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/Core/HLE/sceMpeg.cpp b/Core/HLE/sceMpeg.cpp index 248868a52f40..4baecb8e9f67 100644 --- a/Core/HLE/sceMpeg.cpp +++ b/Core/HLE/sceMpeg.cpp @@ -270,7 +270,7 @@ static MpegContext *getMpegCtx(u32 mpegAddr) { static void InitRingbuffer(SceMpegRingBuffer *buf, int packets, int data, int size, int callback_addr, int callback_args) { buf->packets = packets; buf->packetsRead = 0; - buf->packetsWritten = 0; + buf->packetsWritePos = 0; buf->packetsAvail = 0; buf->packetSize = 2048; buf->data = data; @@ -1387,8 +1387,11 @@ void PostPutAction::run(MipsCall &call) { auto ringbuffer = PSPPointer::Create(ringAddr_); MpegContext *ctx = getMpegCtx(ringbuffer->mpeg); + int writeOffset = ringbuffer->packetsWritePos % (s32)ringbuffer->packets; + const u8 *data = Memory::GetPointer(ringbuffer->data + writeOffset * 2048); int packetsAdded = currentMIPS->r[MIPS_REG_V0]; + if (ringbuffer->packetsRead == 0 && ctx->mediaengine && packetsAdded > 0) { // init mediaEngine AnalyzeMpeg(ctx->mpegheader, ctx); @@ -1399,12 +1402,12 @@ void PostPutAction::run(MipsCall &call) { WARN_LOG(ME, "sceMpegRingbufferPut clamping packetsAdded old=%i new=%i", packetsAdded, ringbuffer->packets - ringbuffer->packetsAvail); packetsAdded = ringbuffer->packets - ringbuffer->packetsAvail; } - int actuallyAdded = ctx->mediaengine == NULL ? 8 : ctx->mediaengine->addStreamData(Memory::GetPointer(ringbuffer->data), packetsAdded * 2048) / 2048; + int actuallyAdded = ctx->mediaengine == NULL ? 8 : ctx->mediaengine->addStreamData(data, packetsAdded * 2048) / 2048; if (actuallyAdded != packetsAdded) { WARN_LOG_REPORT(ME, "sceMpegRingbufferPut(): unable to enqueue all added packets, going to overwrite some frames."); } ringbuffer->packetsRead += packetsAdded; - ringbuffer->packetsWritten += packetsAdded; + ringbuffer->packetsWritePos += packetsAdded; ringbuffer->packetsAvail += packetsAdded; } DEBUG_LOG(ME, "packetAdded: %i packetsRead: %i packetsTotal: %i", packetsAdded, ringbuffer->packetsRead, ringbuffer->packets); @@ -1444,8 +1447,9 @@ static u32 sceMpegRingbufferPut(u32 ringbufferAddr, int numPackets, int availabl // TODO: Should call this multiple times until we get numPackets. // Normally this would be if it did not read enough, but also if available > packets. // Should ultimately return the TOTAL number of returned packets. - u32 packetsThisRound = std::min(numPackets, (s32)ringbuffer->packets); - u32 args[3] = {(u32)ringbuffer->data, packetsThisRound, (u32)ringbuffer->callback_args}; + int writeOffset = ringbuffer->packetsWritePos % (s32)ringbuffer->packets; + u32 packetsThisRound = std::min(numPackets, (s32)ringbuffer->packets - writeOffset); + u32 args[3] = {(u32)ringbuffer->data + (u32)writeOffset * 2048, packetsThisRound, (u32)ringbuffer->callback_args}; __KernelDirectMipsCall(ringbuffer->callback_addr, action, args, 3, false); } else { ERROR_LOG_REPORT(ME, "sceMpegRingbufferPut: callback_addr zero"); @@ -1765,7 +1769,7 @@ static u32 sceMpegFlushAllStream(u32 mpeg) if (ringbuffer.IsValid()) { ringbuffer->packetsAvail = 0; ringbuffer->packetsRead = 0; - ringbuffer->packetsWritten = 0; + ringbuffer->packetsWritePos = 0; } return 0; diff --git a/Core/HLE/sceMpeg.h b/Core/HLE/sceMpeg.h index 853de2195161..b4a173d70503 100644 --- a/Core/HLE/sceMpeg.h +++ b/Core/HLE/sceMpeg.h @@ -56,8 +56,9 @@ struct SceMpegAu { struct SceMpegRingBuffer { // PSP info s32_le packets; + // Misused: this is used as total read, but should be read offset (within ring.) s32_le packetsRead; - s32_le packetsWritten; + s32_le packetsWritePos; s32_le packetsAvail; // pspsdk: unk2, noxa: iUnk0 s32_le packetSize; // 2048 u32_le data; // address, ring buffer From c8d0270bb4fa3148b78fce76f6fe79371db2a287 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 4 Jun 2016 18:00:41 -0700 Subject: [PATCH 6/8] Mpeg: Validate data more during demux. This also parses a bit earlier, not requiring a full 2048 bytes ahead at all times. --- Core/HW/MpegDemux.cpp | 76 ++++++++++++++++++++++++++++++++++--------- Core/HW/MpegDemux.h | 4 +-- 2 files changed, 62 insertions(+), 18 deletions(-) diff --git a/Core/HW/MpegDemux.cpp b/Core/HW/MpegDemux.cpp index 0b037305137b..9620154c55a5 100644 --- a/Core/HW/MpegDemux.cpp +++ b/Core/HW/MpegDemux.cpp @@ -1,4 +1,5 @@ #include "MpegDemux.h" +#include "Core/Reporting.h" const int PACKET_START_CODE_MASK = 0xffffff00; const int PACKET_START_CODE_PREFIX = 0x00000100; @@ -137,9 +138,8 @@ int MpegDemux::readPesHeader(PesHeader &pesHeader, int length, int startCode) { return length; } -int MpegDemux::demuxStream(bool bdemux, int startCode, int channel) +int MpegDemux::demuxStream(bool bdemux, int startCode, int length, int channel) { - int length = read16(); if (bdemux) { PesHeader pesHeader(channel); length = readPesHeader(pesHeader, length, startCode); @@ -185,49 +185,91 @@ bool MpegDemux::skipPackHeader() { return true; } -void MpegDemux::demux(int audioChannel) +bool MpegDemux::demux(int audioChannel) { if (audioChannel >= 0) m_audioChannel = audioChannel; - while (m_index < m_readSize) + + bool looksValid = false; + bool needMore = false; + while (m_index < m_readSize && !needMore) { - if (m_index + 2048 > m_readSize) - break; // Search for start code int startCode = 0xFF; while ((startCode & PACKET_START_CODE_MASK) != PACKET_START_CODE_PREFIX && !isEOF()) { startCode = (startCode << 8) | read8(); } + // Not enough data available yet. + if (m_readSize - m_index < 16) { + m_index -= 4; + break; + } + switch (startCode) { case PACK_START_CODE: - skipPackHeader(); + if (skipPackHeader()) { + looksValid = true; + } break; - case SYSTEM_HEADER_START_CODE: - skip(read16()); + case SYSTEM_HEADER_START_CODE: { + looksValid = true; + int length = read16(); + if (m_readSize - m_index < length) { + m_index -= 4 + 2; + needMore = true; + break; + } + skip(length); break; + } case PADDING_STREAM: - case PRIVATE_STREAM_2: - { - int length = read16(); - skip(length); + case PRIVATE_STREAM_2: { + looksValid = true; + int length = read16(); + if (m_readSize - m_index < length) { + m_index -= 4 + 2; + needMore = true; break; } + skip(length); + break; + } case PRIVATE_STREAM_1: { // Audio stream - m_audioChannel = demuxStream(true, startCode, m_audioChannel); + int length = read16(); + // Check for PES header marker. + looksValid = (m_buf[m_index] & 0xC0) == 0x80; + if (m_readSize - m_index < length) { + m_index -= 4 + 2; + needMore = true; + break; + } + m_audioChannel = demuxStream(true, startCode, length, m_audioChannel); + looksValid = true; break; } case 0x1E0: case 0x1E1: case 0x1E2: case 0x1E3: case 0x1E4: case 0x1E5: case 0x1E6: case 0x1E7: case 0x1E8: case 0x1E9: case 0x1EA: case 0x1EB: - case 0x1EC: case 0x1ED: case 0x1EE: case 0x1EF: + case 0x1EC: case 0x1ED: case 0x1EE: case 0x1EF: { // Video Stream - demuxStream(false, startCode, -1); + int length = read16(); + // Check for PES header marker. + looksValid = (m_buf[m_index] & 0xC0) == 0x80; + if (m_readSize - m_index < length) { + m_index -= 4 + 2; + needMore = true; + break; + } + demuxStream(false, startCode, length, -1); break; + } case USER_DATA_START_CODE: // User data, probably same as queried by sceMpegGetUserdataAu. // Not sure what exactly to do or how much to read. // TODO: implement properly. + WARN_LOG_REPORT_ONCE(mpeguserdata, ME, "MPEG user data found"); + looksValid = true; break; } } @@ -240,6 +282,8 @@ void MpegDemux::demux(int audioChannel) m_index = 0; m_readSize = 0; } + + return looksValid; } static bool isHeader(u8* audioStream, int offset) diff --git a/Core/HW/MpegDemux.h b/Core/HW/MpegDemux.h index 30f98082d883..0805891b8efa 100644 --- a/Core/HW/MpegDemux.h +++ b/Core/HW/MpegDemux.h @@ -15,7 +15,7 @@ class MpegDemux ~MpegDemux(); bool addStreamData(const u8 *buf, int addSize); - void demux(int audioChannel); + bool demux(int audioChannel); // return its framesize int getNextAudioFrame(u8 **buf, int *headerCode1, int *headerCode2, s64 *pts = NULL); @@ -64,7 +64,7 @@ class MpegDemux } } int readPesHeader(PesHeader &pesHeader, int length, int startCode); - int demuxStream(bool bdemux, int startCode, int channel); + int demuxStream(bool bdemux, int startCode, int length, int channel); bool skipPackHeader(); int m_index; From 4ee39f82e4ab78b8745161c736a2a384487a008a Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 4 Jun 2016 18:01:47 -0700 Subject: [PATCH 7/8] Mpeg: Validate added packets. Older libraries only, but this will cause it to reject packets that don't make sense. So far, this seems to mirror the behavior of various garbage packets sent to the real firmware. --- Core/HLE/sceMpeg.cpp | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/Core/HLE/sceMpeg.cpp b/Core/HLE/sceMpeg.cpp index 4baecb8e9f67..7eaa56b38a80 100644 --- a/Core/HLE/sceMpeg.cpp +++ b/Core/HLE/sceMpeg.cpp @@ -1392,6 +1392,35 @@ void PostPutAction::run(MipsCall &call) { int packetsAdded = currentMIPS->r[MIPS_REG_V0]; + // It seems validation is done only by older mpeg libs. + if (mpegLibVersion < 0x0105 && packetsAdded > 0) { + // TODO: Faster / less wasteful validation. + MpegDemux *demuxer = new MpegDemux(packetsAdded * 2048, 0); + int readOffset = ringbuffer->packetsRead % (s32)ringbuffer->packets; + const u8 *buf = Memory::GetPointer(ringbuffer->data + readOffset * 2048); + bool invalid = false; + for (int i = 0; i < packetsAdded; ++i) { + demuxer->addStreamData(buf, 2048); + buf += 2048; + + if (!demuxer->demux(0xFFFF)) { + invalid = true; + } + } + if (invalid) { + // Bail out early - don't accept any of the packets, even the good ones. + ERROR_LOG_REPORT(ME, "sceMpegRingbufferPut(): invalid mpeg data"); + call.setReturnValue(ERROR_MPEG_INVALID_VALUE); + + if (mpegLibVersion <= 0x0103) { + // Act like they were actually added, but don't increment read pos. + ringbuffer->packetsWritePos += packetsAdded; + ringbuffer->packetsAvail += packetsAdded; + } + return; + } + } + if (ringbuffer->packetsRead == 0 && ctx->mediaengine && packetsAdded > 0) { // init mediaEngine AnalyzeMpeg(ctx->mpegheader, ctx); From 04dcaeae1d4cf44d474faede3ab70ea76e23a481 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 4 Jun 2016 18:02:55 -0700 Subject: [PATCH 8/8] Mpeg: Move, rather than resetting, packetsAvail. When we've got garbage data, this has to stay incorrect. Without this, Megaman X gets confused when playing its video (because it enqueues garbage packets.) See #3318. --- Core/HLE/sceMpeg.cpp | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/Core/HLE/sceMpeg.cpp b/Core/HLE/sceMpeg.cpp index 7eaa56b38a80..76378048fed8 100644 --- a/Core/HLE/sceMpeg.cpp +++ b/Core/HLE/sceMpeg.cpp @@ -1078,6 +1078,8 @@ static u32 sceMpegAvcDecode(u32 mpeg, u32 auAddr, u32 frameWidth, u32 bufferAddr return hleDelayResult(ERROR_MPEG_AVC_DECODE_FATAL, "mpeg buffer empty", avcEmptyDelayMs); } + s32 beforeAvail = ringbuffer->packets - ctx->mediaengine->getRemainSize() / 2048; + // We stored the video stream id here in sceMpegGetAvcAu(). ctx->mediaengine->setVideoStream(avcAu.esBuffer); @@ -1103,7 +1105,14 @@ static u32 sceMpegAvcDecode(u32 mpeg, u32 auAddr, u32 frameWidth, u32 bufferAddr } else { ctx->avc.avcFrameStatus = 0; } - ringbuffer->packetsAvail = ringbuffer->packets - ctx->mediaengine->getRemainSize() / 2048; + s32 afterAvail = ringbuffer->packets - ctx->mediaengine->getRemainSize() / 2048; + // Don't actually reset avail, we only change it by what was decoded. + // Garbage frames can cause this to be incorrect, but some games expect that. + if (mpegLibVersion <= 0x0103) { + ringbuffer->packetsAvail += afterAvail - beforeAvail; + } else { + ringbuffer->packetsAvail = afterAvail; + } avcAu.pts = ctx->mediaengine->getVideoTimeStamp() + ctx->mpegFirstTimestamp; @@ -1247,6 +1256,8 @@ static int sceMpegAvcDecodeYCbCr(u32 mpeg, u32 auAddr, u32 bufferAddr, u32 initA return hleDelayResult(ERROR_MPEG_AVC_DECODE_FATAL, "mpeg buffer empty", avcEmptyDelayMs); } + s32 beforeAvail = ringbuffer->packets - ctx->mediaengine->getRemainSize() / 2048; + // We stored the video stream id here in sceMpegGetAvcAu(). ctx->mediaengine->setVideoStream(avcAu.esBuffer); @@ -1258,10 +1269,17 @@ static int sceMpegAvcDecodeYCbCr(u32 mpeg, u32 auAddr, u32 bufferAddr, u32 initA // Don't draw here, we'll draw in the Csc func. ctx->avc.avcFrameStatus = 1; ctx->videoFrameCount++; - }else { + } else { ctx->avc.avcFrameStatus = 0; } - ringbuffer->packetsAvail = ringbuffer->packets - ctx->mediaengine->getRemainSize() / 2048; + s32 afterAvail = ringbuffer->packets - ctx->mediaengine->getRemainSize() / 2048; + // Don't actually reset avail, we only change it by what was decoded. + // Garbage frames can cause this to be incorrect, but some games expect that. + if (mpegLibVersion <= 0x0103) { + ringbuffer->packetsAvail += afterAvail - beforeAvail; + } else { + ringbuffer->packetsAvail = afterAvail; + } avcAu.pts = ctx->mediaengine->getVideoTimeStamp() + ctx->mpegFirstTimestamp;