mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-11-12 17:19:28 +01:00
Media: Fix size math, Atrac decoder state and the SAS race
- The ringbuffer's packets * packetSize went straight into the media engine's allocations. It's clamped to what a savestate can hold, and the put callback's return value is clamped before it sizes the validation demuxer. A ringbuffer with no packets no longer divides by zero. - sceMpegAvcCsc with a range starting past the frame gave a negative width. sceMpegBaseCsc* reject a stride that overflows the size math, and the MJPEG output size could overflow 32 bits. - MpegDemux clamps the header's stream offset, and doesn't push PES payload past what's been read. read16/read24/readPts relied on the unspecified evaluation order of | for their byte order. A negative stream offset made every later savestate fail to load. - Atrac: DecodeForSas clamps the split packet so the second copy stays in the stack buffer. A zero frame size from the file is rejected before it's divided by. decodeTemp_ is reallocated when SetData changes the channel count. The standalone decoder only has buffers for two channels, so it clamps the count it's given. AtracSasBindContextAndGetID indexed with a negative ID and dereferenced a missing context. - Psmf's player tempbuf path could read past the 64KB buffer from the stream table or EP map offset. - sceMpegDelete didn't forget the context's PMP state, so a new context at the same address skipped the PMP codec setup and crashed. Releasing an Atrac ID or setting its data now waits for a SAS mix in progress, which can be decoding from it on the SAS thread. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
fd42bb6dcb
commit
1c980fc104
11 files changed
+118
-30
No files matched your search
+21
-4
@@ -201,6 +201,10 @@ static int ComputeAtracStateAndInitSecondBuffer(SceAtracIdInfo *info, u32 readSi
|
||||
}
|
||||
|
||||
int InitContextFromTrackInfo(SceAtracContext *ctx, const TrackInfo *wave, u32 bufferAddr, int readSize, int bufferSize) {
|
||||
// The frame size comes from the file, and InitLengthAndLoop divides by it.
|
||||
if (wave->blockAlign <= 0) {
|
||||
return SCE_ERROR_ATRAC_BAD_CODEC_PARAMS;
|
||||
}
|
||||
(ctx->info).numChan = (char)wave->numChans;
|
||||
const int extraSamples = (ctx->info).codec == 0x1001 ? 0x45 : 0x170;
|
||||
(ctx->info).firstValidSample = extraSamples + wave->firstSampleOffset;
|
||||
@@ -1020,6 +1024,9 @@ int Atrac2::SetData(const Track &track, u32 bufferAddr, u32 readSize, u32 buffer
|
||||
CreateDecoder(info.codec, info.sampleSize, info.numChan, jointStereo_);
|
||||
|
||||
outputChannels_ = outputChannels;
|
||||
// Sized for the old frame size and channel count.
|
||||
delete[] decodeTemp_;
|
||||
decodeTemp_ = nullptr;
|
||||
|
||||
INFO_LOG(Log::Atrac,
|
||||
"Atrac: sampleSize: %d buffer: %08x bufferByte: %d firstValidSample: %d\n"
|
||||
@@ -1150,6 +1157,8 @@ void Atrac2::InitLowLevel(const Atrac3LowLevelParams ¶ms, int codecType) {
|
||||
info.numChan = params.encodedChannels;
|
||||
outputChannels_ = params.outputChannels;
|
||||
info.sampleSize = params.bytesPerFrame;
|
||||
delete[] decodeTemp_;
|
||||
decodeTemp_ = nullptr;
|
||||
info.dataOff = 0;
|
||||
info.decodePos = 0;
|
||||
info.state = ATRAC_STATUS_LOW_LEVEL;
|
||||
@@ -1255,13 +1264,21 @@ void Atrac2::DecodeForSas(s16 *dstData, int *bytesWritten, int *finish) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Compute the part sizes using the current size.
|
||||
// Compute the part sizes using the current size. The game can shrink the buffer under us,
|
||||
// so keep both parts inside the assembly buffer.
|
||||
int part1Size = sas_.bufSize[sas_.curBuffer] - sas_.streamOffset;
|
||||
int part2Size = info.sampleSize - part1Size;
|
||||
_dbg_assert_(part1Size >= 0);
|
||||
if (part1Size >= 0) {
|
||||
part1Size = std::clamp(part1Size, 0, (int)info.sampleSize);
|
||||
int part2Size = info.sampleSize - part1Size;
|
||||
if (part1Size > 0) {
|
||||
// Grab the partial packet, before we switch over to the other buffer.
|
||||
Memory::Memcpy(assembly, sas_.bufPtr[sas_.curBuffer] + sas_.streamOffset, part1Size);
|
||||
const u32 part1Addr = sas_.bufPtr[sas_.curBuffer] + sas_.streamOffset;
|
||||
if (!Memory::IsValidRange(part1Addr, part1Size)) {
|
||||
ERROR_LOG(Log::Atrac, "SAS: bad buffer %08x", part1Addr);
|
||||
*finish = 1;
|
||||
return;
|
||||
}
|
||||
Memory::MemcpyUnchecked(assembly, part1Addr, part1Size);
|
||||
}
|
||||
|
||||
// Check if we hit the end.
|
||||
|
||||
+17
-2
@@ -37,6 +37,7 @@
|
||||
#include "Core/HLE/sceKernelInterrupt.h"
|
||||
#include "Core/HLE/scePower.h"
|
||||
#include "Core/HLE/sceAtrac.h"
|
||||
#include "Core/HLE/sceSas.h"
|
||||
#include "Core/HLE/sceAudiocodec.h"
|
||||
#include "Core/HLE/AtracCtx.h"
|
||||
#include "Core/HLE/AtracCtx2.h"
|
||||
@@ -333,6 +334,8 @@ static int AllocAndRegisterAtrac(int codecType) {
|
||||
static int UnregisterAndDeleteAtrac(int atracID) {
|
||||
if (atracID >= 0 && atracID < PSP_MAX_ATRAC_IDS) {
|
||||
if (atracContexts[atracID] != nullptr) {
|
||||
// The SAS thread might be decoding from it.
|
||||
__SasWaitForMix();
|
||||
delete atracContexts[atracID];
|
||||
atracContexts[atracID] = nullptr;
|
||||
return 0;
|
||||
@@ -776,6 +779,8 @@ static u32 sceAtracSetHalfwayBuffer(int atracID, u32 buffer, u32 readSize, u32 b
|
||||
return hleLogError(Log::Atrac, SCE_ERROR_ATRAC_WRONG_CODECTYPE, "atracID uses different codec type than data");
|
||||
}
|
||||
|
||||
// SetData replaces the decoder, which the SAS thread might be using.
|
||||
__SasWaitForMix();
|
||||
ret = atrac->SetData(track, buffer, readSize, bufferSize, 0, 2, false);
|
||||
if (ret < 0) {
|
||||
return AtracSetDataError(ret, track);
|
||||
@@ -811,6 +816,8 @@ static u32 sceAtracSetData(int atracID, u32 buffer, u32 bufferSize) {
|
||||
return hleLogError(Log::Atrac, SCE_ERROR_ATRAC_WRONG_CODECTYPE, "atracID uses different codec type than data");
|
||||
}
|
||||
|
||||
// SetData replaces the decoder, which the SAS thread might be using.
|
||||
__SasWaitForMix();
|
||||
ret = atrac->SetData(track, buffer, bufferSize, bufferSize, 0, 2, false);
|
||||
if (ret < 0) {
|
||||
return AtracSetDataError(ret, track);
|
||||
@@ -988,6 +995,8 @@ static int sceAtracSetMOutHalfwayBuffer(int atracID, u32 buffer, u32 readSize, u
|
||||
return hleLogError(Log::Atrac, ret, "%s", error.c_str());
|
||||
}
|
||||
|
||||
// SetData replaces the decoder, which the SAS thread might be using.
|
||||
__SasWaitForMix();
|
||||
ret = atrac->SetData(track, buffer, readSize, bufferSize, 0, 1, false);
|
||||
if (ret < 0 && ret != SCE_ERROR_ATRAC_NOT_MONO) {
|
||||
return AtracSetDataError(ret, track);
|
||||
@@ -1012,6 +1021,8 @@ static u32 sceAtracSetMOutData(int atracID, u32 buffer, u32 bufferSize) {
|
||||
return hleLogError(Log::Atrac, ret, "%s", error.c_str());
|
||||
}
|
||||
|
||||
// SetData replaces the decoder, which the SAS thread might be using.
|
||||
__SasWaitForMix();
|
||||
ret = atrac->SetData(track, buffer, bufferSize, bufferSize, 0, 1, false);
|
||||
if (ret < 0 && ret != SCE_ERROR_ATRAC_NOT_MONO) {
|
||||
return AtracSetDataError(ret, track);
|
||||
@@ -1225,8 +1236,8 @@ void AtracSasDecodeData(int atracID, u8* outbuf, int *SamplesNum, int *finish) {
|
||||
// The context pointer is assumed to be valid.
|
||||
int AtracSasBindContextAndGetID(u32 contextAddr) {
|
||||
// Ugly hack, but needed to support both old and new contexts.
|
||||
int atracID = (int)Memory::ReadUnchecked_U32(contextAddr + 0xfc);
|
||||
if (atracID < PSP_MAX_ATRAC_IDS && atracContexts[atracID] && atracContexts[atracID]->GetContextVersion() == 1) {
|
||||
int atracID = Memory::IsValid4AlignedAddress(contextAddr + 0xfc) ? (int)Memory::ReadUnchecked_U32(contextAddr + 0xfc) : -1;
|
||||
if (atracID >= 0 && atracID < PSP_MAX_ATRAC_IDS && atracContexts[atracID] && atracContexts[atracID]->GetContextVersion() == 1) {
|
||||
// We can assume the old atracID hack was used, and atracID is valid.
|
||||
} else {
|
||||
// Let's just loop around the contexts and find it by address.
|
||||
@@ -1244,6 +1255,10 @@ int AtracSasBindContextAndGetID(u32 contextAddr) {
|
||||
}
|
||||
|
||||
AtracBase *atrac = getAtrac(atracID);
|
||||
if (!atrac) {
|
||||
// The SAS voice treats a negative ID as no data.
|
||||
return -1;
|
||||
}
|
||||
atrac->CheckForSas();
|
||||
return atracID;
|
||||
}
|
||||
|
||||
@@ -287,7 +287,9 @@ static int DecodeJpeg(u32 jpegAddr, int jpegSize, u32 imageAddr, int &usec) {
|
||||
|
||||
usec += (width * height) / 14;
|
||||
|
||||
if (!Memory::IsValidRange(imageAddr, mjpegWidth * mjpegHeight * 4)) {
|
||||
// CreateMJpeg doesn't bound the height, so this could overflow in 32 bits.
|
||||
const u64 outSize = (u64)std::max(mjpegWidth, 0) * (u64)std::max(mjpegHeight, 0) * 4;
|
||||
if (outSize > 0x3FFFFFFF || !Memory::IsValidRange(imageAddr, (u32)outSize)) {
|
||||
free(jpegBuf);
|
||||
return hleLogError(Log::ME, SCE_KERNEL_ERROR_INVALID_POINTER, "invalid output address");
|
||||
}
|
||||
|
||||
+32
-11
@@ -16,6 +16,7 @@
|
||||
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
||||
|
||||
// This code is part shamelessly "inspired" from JPSCP.
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
|
||||
@@ -274,6 +275,12 @@ static u32 getMpegVersion(u32 mpegRawVersion) {
|
||||
}
|
||||
}
|
||||
|
||||
// The game controls both fields, so keep the media engine's buffers to what a savestate can hold.
|
||||
static int RingbufferStreamSize(const SceMpegRingBuffer *ringbuffer) {
|
||||
const s64 size = (s64)ringbuffer->packets * ringbuffer->packetSize;
|
||||
return (int)std::clamp<s64>(size, 0, MediaEngine::MAX_RINGBUFFER_SIZE);
|
||||
}
|
||||
|
||||
static void AnalyzeMpeg(const u8 *buffer, u32 validSize, MpegContext *ctx) {
|
||||
ctx->mpegMagic = *(u32_le*)buffer;
|
||||
ctx->mpegRawVersion = *(u32_le*)(buffer + PSMF_STREAM_VERSION_OFFSET);
|
||||
@@ -309,7 +316,7 @@ static void AnalyzeMpeg(const u8 *buffer, u32 validSize, MpegContext *ctx) {
|
||||
// init mediaEngine
|
||||
auto ringbuffer = PSPPointer<SceMpegRingBuffer>::Create(ctx->mpegRingbufferAddr);
|
||||
if (ringbuffer.IsValid()) {
|
||||
ctx->mediaengine->loadStream(buffer, ctx->mpegOffset, ringbuffer->packets * ringbuffer->packetSize);
|
||||
ctx->mediaengine->loadStream(buffer, ctx->mpegOffset, RingbufferStreamSize(ringbuffer));
|
||||
} else {
|
||||
// TODO: Does this make any sense?
|
||||
ctx->mediaengine->loadStream(buffer, ctx->mpegOffset, 0);
|
||||
@@ -592,6 +599,8 @@ static int sceMpegDelete(u32 mpeg) {
|
||||
|
||||
delete ctx;
|
||||
g_mpegCtxs.erase(Memory::ReadUnchecked_U32(mpeg));
|
||||
// A new context at the same address needs its PMP codec set up again.
|
||||
pmp_ContextList.remove(mpeg);
|
||||
|
||||
return hleDelayResult(hleLogDebug(Log::Mpeg, 0), "mpeg delete", 40000);
|
||||
}
|
||||
@@ -1456,6 +1465,12 @@ static int sceMpegRingbufferAvailableSize(u32 ringbufferAddr) {
|
||||
|
||||
void PostPutAction::run(MipsCall &call) {
|
||||
auto ringbuffer = PSPPointer<SceMpegRingBuffer>::Create(ringAddr_);
|
||||
// The callback could have changed it.
|
||||
if (!ringbuffer.IsValid() || (s32)ringbuffer->packets <= 0) {
|
||||
ERROR_LOG(Log::Mpeg, "sceMpegRingbufferPut: bad ringbuffer %08x", ringAddr_);
|
||||
call.setReturnValue(SCE_MPEG_ERROR_INVALID_VALUE);
|
||||
return;
|
||||
}
|
||||
|
||||
MpegContext *ctx = getMpegCtx(ringbuffer->mpeg);
|
||||
int writeOffset = ringbuffer->packetsWritePos % (s32)ringbuffer->packets;
|
||||
@@ -1471,13 +1486,19 @@ void PostPutAction::run(MipsCall &call) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The count comes from the game's callback, so clamp it before using it for sizes.
|
||||
if (packetsAddedThisRound > ringbuffer->packets - ringbuffer->packetsAvail) {
|
||||
WARN_LOG(Log::Mpeg, "sceMpegRingbufferPut clamping packetsAdded old=%i new=%i", packetsAddedThisRound, ringbuffer->packets - ringbuffer->packetsAvail);
|
||||
packetsAddedThisRound = ringbuffer->packets - ringbuffer->packetsAvail;
|
||||
}
|
||||
|
||||
// It seems validation is done only by older mpeg libs.
|
||||
if (mpegLibVersion < 0x0105 && packetsAddedThisRound > 0) {
|
||||
// TODO: Faster / less wasteful validation.
|
||||
auto demuxer = std::make_unique<MpegDemux>(packetsAddedThisRound * 2048, 0);
|
||||
int readOffset = ringbuffer->packetsRead % (s32)ringbuffer->packets;
|
||||
uint32_t bufSize = Memory::ClampValidSizeAt(ringbuffer->data + readOffset * 2048, packetsAddedThisRound * 2048);
|
||||
const u8 *buf = Memory::GetPointerOrException(ringbuffer->data + readOffset * 2048);
|
||||
uint32_t bufSize = Memory::ClampValidSizeAt(ringbuffer->data + readOffset * 2048, (u32)packetsAddedThisRound * 2048);
|
||||
auto demuxer = std::make_unique<MpegDemux>(std::max(bufSize, 2048U), 0);
|
||||
const u8 *buf = Memory::GetPointerUnchecked(ringbuffer->data + readOffset * 2048);
|
||||
bool invalid = false;
|
||||
for (uint32_t i = 0; i < bufSize / 2048; ++i) {
|
||||
demuxer->addStreamData(buf, 2048);
|
||||
@@ -1505,15 +1526,11 @@ void PostPutAction::run(MipsCall &call) {
|
||||
if (ringbuffer->packetsRead == 0 && ctx->mediaengine && packetsAddedThisRound > 0) {
|
||||
// init mediaEngine
|
||||
AnalyzeMpeg(ctx->mpegheader, 2048, ctx);
|
||||
ctx->mediaengine->loadStream(ctx->mpegheader, 2048, ringbuffer->packets * ringbuffer->packetSize);
|
||||
ctx->mediaengine->loadStream(ctx->mpegheader, 2048, RingbufferStreamSize(ringbuffer));
|
||||
}
|
||||
if (packetsAddedThisRound > 0) {
|
||||
if (packetsAddedThisRound > ringbuffer->packets - ringbuffer->packetsAvail) {
|
||||
WARN_LOG(Log::Mpeg, "sceMpegRingbufferPut clamping packetsAdded old=%i new=%i", packetsAddedThisRound, ringbuffer->packets - ringbuffer->packetsAvail);
|
||||
packetsAddedThisRound = ringbuffer->packets - ringbuffer->packetsAvail;
|
||||
}
|
||||
const u8 *data = Memory::GetPointerOrException(ringbuffer->data + writeOffset * 2048);
|
||||
uint32_t dataSize = Memory::ClampValidSizeAt(ringbuffer->data + writeOffset * 2048, packetsAddedThisRound * 2048);
|
||||
uint32_t dataSize = Memory::ClampValidSizeAt(ringbuffer->data + writeOffset * 2048, (u32)packetsAddedThisRound * 2048);
|
||||
const u8 *data = dataSize ? Memory::GetPointerUnchecked(ringbuffer->data + writeOffset * 2048) : nullptr;
|
||||
int actuallyAdded = ctx->mediaengine == NULL ? 8 : ctx->mediaengine->addStreamData(data, dataSize) / 2048;
|
||||
if (actuallyAdded != packetsAddedThisRound) {
|
||||
WARN_LOG(Log::Mpeg, "sceMpegRingbufferPut(): unable to enqueue all added packets, going to overwrite some frames (%d != %d)", actuallyAdded, packetsAddedThisRound);
|
||||
@@ -1559,6 +1576,10 @@ static u32 sceMpegRingbufferPut(u32 ringbufferAddr, int numPackets, int availabl
|
||||
return hleLogError(Log::Mpeg, -1, "invalid ringbuffer address");
|
||||
}
|
||||
|
||||
if ((s32)ringbuffer->packets <= 0) {
|
||||
return hleLogError(Log::Mpeg, SCE_MPEG_ERROR_INVALID_VALUE, "bad ringbuffer packet count");
|
||||
}
|
||||
|
||||
numPackets = std::min(numPackets, available);
|
||||
// Generally, program will call sceMpegRingbufferAvailableSize() before this func.
|
||||
// Seems still need to check actual available, Patapon 3 for example.
|
||||
|
||||
@@ -595,6 +595,10 @@ static int MpegBaseCscRange(u32 bufferRGB, u32 bufferRGB2, u32 cscAddr, int buff
|
||||
if (bufferWidth == 0) {
|
||||
bufferWidth = g_mpegBaseBufferWidth;
|
||||
}
|
||||
// The stride goes into the output size and row addresses, so keep that math in range.
|
||||
if (bufferWidth < 0 || bufferWidth > 4096) {
|
||||
return hleLogError(Log::Mpeg, -1, "bad buffer width %d", bufferWidth);
|
||||
}
|
||||
|
||||
u32 buffers[8]{};
|
||||
for (int i = 0; i < 8; i++) {
|
||||
@@ -731,6 +735,10 @@ static int sceMpegBaseCscVme(u32 bufferRGB, u32 bufferRGB2, int bufferWidth, u32
|
||||
if (bufferWidth == 0) {
|
||||
bufferWidth = g_mpegBaseBufferWidth;
|
||||
}
|
||||
// The stride goes into the output size and row addresses, so keep that math in range.
|
||||
if (bufferWidth < 0 || bufferWidth > 4096) {
|
||||
return hleLogError(Log::Mpeg, -1, "bad buffer width %d", bufferWidth);
|
||||
}
|
||||
auto csc = PSPPointer<SceMp4AvcCscStruct>::Create(cscAddr);
|
||||
u32 buffers[6];
|
||||
for (int i = 0; i < 6; i++) {
|
||||
|
||||
@@ -302,8 +302,9 @@ public:
|
||||
} else {
|
||||
// No guest address to validate against (player tempbuf path,
|
||||
// where the buffer is 64KB): cap so the reads below stay in bounds.
|
||||
if (epMapBytes > 0x10000) {
|
||||
psmf->EPMapEntriesNum = 0x10000 / EP_MAP_STRIDE;
|
||||
const u32 available = psmf->EPMapOffset < 0x10000 ? 0x10000 - psmf->EPMapOffset : 0;
|
||||
if (epMapBytes > available) {
|
||||
psmf->EPMapEntriesNum = available / EP_MAP_STRIDE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -385,6 +386,10 @@ Psmf::Psmf(const u8 *ptr, u32 data) {
|
||||
if (data != 0 && !Memory::IsValidRange(data, 0x82 + numStreams * 16)) {
|
||||
ERROR_LOG(Log::ME, "Invalid PSMF stream count: %d", numStreams);
|
||||
numStreams = Memory::ClampValidSizeAt(data + 0x82, numStreams * 16) / 16;
|
||||
} else if (data == 0 && 0x82 + numStreams * 16 > 0x10000) {
|
||||
// The player's 64KB tempbuf.
|
||||
ERROR_LOG(Log::ME, "Invalid PSMF stream count: %d", numStreams);
|
||||
numStreams = (0x10000 - 0x82) / 16;
|
||||
}
|
||||
|
||||
for (int i = 0; i < numStreams; i++) {
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#include <algorithm>
|
||||
|
||||
#include "SimpleAudioDec.h"
|
||||
#include "Common/LogReporting.h"
|
||||
#include "ext/at3_standalone/at3_decoders.h"
|
||||
@@ -16,10 +18,11 @@ inline int16_t clamp16(float f) {
|
||||
class Atrac3Audio : public AudioDecoder {
|
||||
public:
|
||||
Atrac3Audio(PSPAudioType audioType, int channels, size_t blockAlign, const uint8_t *extraData, size_t extraDataSize)
|
||||
: audioType_(audioType), channels_(channels) {
|
||||
// The channel count can come straight from the game, and buffers_ only has room for two.
|
||||
: audioType_(audioType), channels_(std::clamp(channels, 1, 2)) {
|
||||
blockAlign_ = (int)blockAlign;
|
||||
if (audioType_ == PSP_CODEC_AT3) {
|
||||
at3Ctx_ = atrac3_alloc(channels, &blockAlign_, extraData, (int)extraDataSize);
|
||||
at3Ctx_ = atrac3_alloc(channels_, &blockAlign_, extraData, (int)extraDataSize);
|
||||
if (at3Ctx_) {
|
||||
codecOpen_ = true;
|
||||
} else {
|
||||
|
||||
@@ -180,7 +180,7 @@ void MediaEngine::DoState(PointerWrap &p) {
|
||||
// All straight from the file. The header ones index m_mpegheader, the ringbuffer size sizes
|
||||
// buffers (the PSP's whole RAM is a generous limit.)
|
||||
if (p.mode == p.MODE_READ && (m_mpegheaderSize < 0 || m_mpegheaderSize > (int)sizeof(m_mpegheader) ||
|
||||
m_mpegheaderReadPos < 0 || m_mpegheaderReadPos > m_mpegheaderSize || m_ringbuffersize < 0 || m_ringbuffersize > 64 * 1024 * 1024)) {
|
||||
m_mpegheaderReadPos < 0 || m_mpegheaderReadPos > m_mpegheaderSize || m_ringbuffersize < 0 || m_ringbuffersize > MAX_RINGBUFFER_SIZE)) {
|
||||
ERROR_LOG(Log::SaveState, "MediaEngine: bad header size %d, position %d or ringbuffer size %d in savestate", m_mpegheaderSize, m_mpegheaderReadPos, m_ringbuffersize);
|
||||
p.SetError(p.ERROR_FAILURE);
|
||||
return;
|
||||
@@ -479,7 +479,7 @@ int MediaEngine::addStreamData(const u8 *buffer, int addSize) {
|
||||
if (!m_pFormatCtx && m_pdata->getQueueSize() >= 2048) {
|
||||
m_mpegheaderSize = m_pdata->get_front(m_mpegheader, sizeof(m_mpegheader));
|
||||
int streamOffset = (int)(*(s32_be *)(m_mpegheader + 8));
|
||||
if (streamOffset <= m_mpegheaderSize) {
|
||||
if (streamOffset >= 0 && streamOffset <= m_mpegheaderSize) {
|
||||
m_mpegheaderSize = streamOffset;
|
||||
m_pdata->pop_front(0, m_mpegheaderSize);
|
||||
openContext();
|
||||
@@ -1000,6 +1000,9 @@ int MediaEngine::writeVideoImageWithRange(u32 bufferPtr, int frameWidth, int vid
|
||||
width = frameWidth;
|
||||
if (height > m_desHeight - ypos)
|
||||
height = m_desHeight - ypos;
|
||||
// A range starting outside the frame clamps to nothing (or less).
|
||||
if (xpos < 0 || ypos < 0 || width <= 0 || height <= 0)
|
||||
return 0;
|
||||
|
||||
switch (videoPixelMode) {
|
||||
case GE_CMODE_32BIT_ABGR8888:
|
||||
|
||||
@@ -52,6 +52,9 @@ bool InitFFmpeg();
|
||||
|
||||
class MediaEngine {
|
||||
public:
|
||||
// Also the most DoState accepts.
|
||||
static constexpr int MAX_RINGBUFFER_SIZE = 64 * 1024 * 1024;
|
||||
|
||||
MediaEngine();
|
||||
~MediaEngine();
|
||||
|
||||
|
||||
@@ -26,7 +26,8 @@ MpegDemux::MpegDemux(int size, int offset) : m_audioStream(size) {
|
||||
m_buf = new u8[size];
|
||||
|
||||
m_len = size;
|
||||
m_index = offset;
|
||||
// The offset comes from the stream header.
|
||||
m_index = std::clamp(offset, 0, size);
|
||||
m_audioChannel = -1;
|
||||
m_readSize = 0;
|
||||
}
|
||||
@@ -162,7 +163,9 @@ int MpegDemux::demuxStream(bool bdemux, int startCode, int length, int channel)
|
||||
length = readPesHeader(pesHeader, length, startCode);
|
||||
if (pesHeader.channel == channel || channel < 0) {
|
||||
channel = pesHeader.channel;
|
||||
m_audioStream.push(m_buf + m_index, length, pesHeader.pts);
|
||||
// The header lengths come from the stream, so don't push past what's been read.
|
||||
const int pushLength = std::clamp(length, 0, std::max(m_readSize - m_index, 0));
|
||||
m_audioStream.push(m_buf + m_index, pushLength, pesHeader.pts);
|
||||
}
|
||||
skip(length);
|
||||
} else {
|
||||
|
||||
+12
-4
@@ -46,22 +46,30 @@ private:
|
||||
// only cross-checked against the outer packet length, not against how much
|
||||
// data is actually available - a crafted stream can claim more than that,
|
||||
// so guard the actual buffer access here rather than in every caller.
|
||||
if (m_index >= m_len) {
|
||||
if (m_index < 0 || m_index >= m_len) {
|
||||
return 0;
|
||||
}
|
||||
return m_buf[m_index++];
|
||||
}
|
||||
// The operands of | aren't sequenced, so read into locals to get the byte order right.
|
||||
int read16() {
|
||||
return (read8() << 8) | read8();
|
||||
const int hi = read8();
|
||||
const int lo = read8();
|
||||
return (hi << 8) | lo;
|
||||
}
|
||||
int read24() {
|
||||
return (read8() << 16) | (read8() << 8) | read8();
|
||||
const int hi = read8();
|
||||
const int mid = read8();
|
||||
const int lo = read8();
|
||||
return (hi << 16) | (mid << 8) | lo;
|
||||
}
|
||||
s64 readPts() {
|
||||
return readPts(read8());
|
||||
}
|
||||
s64 readPts(int c) {
|
||||
return (((s64) (c & 0x0E)) << 29) | ((read16() >> 1) << 15) | (read16() >> 1);
|
||||
const int hi = read16();
|
||||
const int lo = read16();
|
||||
return (((s64) (c & 0x0E)) << 29) | ((hi >> 1) << 15) | (lo >> 1);
|
||||
}
|
||||
bool isEOF() const {
|
||||
return m_index >= m_readSize;
|
||||
|
||||
Reference in new issue
Block a user