Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 93 additions & 5 deletions ps2xRuntime/src/lib/Kernel/Stubs/MPEG.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -502,6 +502,9 @@ namespace ps2_stubs
uint32_t nextCallbackHandle = 1u;
uint64_t cdStreamGeneration = 0u;
bool currentCdStreamEofSeen = false;
std::vector<uint8_t> cdStreamStagedBytes; // host-fed ES held before a decoder exists
uint64_t cdStreamStageGeneration = 0u; // generation the stage belongs to
bool cdStreamStageOverflowed = false; // cap hit -> stage abandoned this generation
uint32_t feedEsTraceCount = 0u;
uint32_t demuxPssTraceCount = 0u;
uint32_t demuxRingTraceCount = 0u;
Expand Down Expand Up @@ -1154,7 +1157,8 @@ namespace ps2_stubs
const uint8_t *data,
size_t size,
uint32_t guestAddr,
std::vector<MpegStreamCallbackEvent> &callbackEvents)
std::vector<MpegStreamCallbackEvent> &callbackEvents,
bool trackGuestAddrs = true)
{
if (!data || size == 0)
{
Expand Down Expand Up @@ -1184,10 +1188,13 @@ namespace ps2_stubs
playback.sawInput = true;

playback.pssBuffer.insert(playback.pssBuffer.end(), data, data + size);
playback.pssGuestAddrs.reserve(playback.pssGuestAddrs.size() + size);
for (size_t i = 0; i < size; ++i)
if (trackGuestAddrs)
{
playback.pssGuestAddrs.push_back(guestAddr + static_cast<uint32_t>(i));
playback.pssGuestAddrs.reserve(playback.pssGuestAddrs.size() + size);
for (size_t i = 0; i < size; ++i)
{
playback.pssGuestAddrs.push_back(guestAddr + static_cast<uint32_t>(i));
}
}
processPssBuffer(mpegAddr, playback, callbackEvents);
if (!playback.decodedFrames.empty())
Expand Down Expand Up @@ -1528,6 +1535,9 @@ namespace ps2_stubs
g_mpeg_stub_state.nextCallbackHandle = 1u;
g_mpeg_stub_state.cdStreamGeneration = 0u;
g_mpeg_stub_state.currentCdStreamEofSeen = false;
g_mpeg_stub_state.cdStreamStagedBytes.clear();
g_mpeg_stub_state.cdStreamStageGeneration = 0u;
g_mpeg_stub_state.cdStreamStageOverflowed = false;
g_mpeg_stub_state.feedEsTraceCount = 0u;
g_mpeg_stub_state.demuxPssTraceCount = 0u;
g_mpeg_stub_state.demuxRingTraceCount = 0u;
Expand All @@ -1551,6 +1561,8 @@ namespace ps2_stubs
std::lock_guard<std::mutex> lock(g_mpeg_stub_mutex);
++g_mpeg_stub_state.cdStreamGeneration;
g_mpeg_stub_state.currentCdStreamEofSeen = false;
g_mpeg_stub_state.cdStreamStagedBytes.clear();
g_mpeg_stub_state.cdStreamStageOverflowed = false;
g_mpeg_stub_state.feedEsTraceCount = 0u;
g_mpeg_stub_state.demuxPssTraceCount = 0u;
g_mpeg_stub_state.demuxRingTraceCount = 0u;
Expand All @@ -1573,6 +1585,8 @@ namespace ps2_stubs
{
std::lock_guard<std::mutex> lock(g_mpeg_stub_mutex);
g_mpeg_stub_state.currentCdStreamEofSeen = true;
g_mpeg_stub_state.cdStreamStagedBytes.clear();
g_mpeg_stub_state.cdStreamStageOverflowed = false;
bool changed = false;
for (auto &[mpegAddr, playback] : g_mpeg_stub_state.playbackByMpeg)
{
Expand All @@ -1599,6 +1613,68 @@ namespace ps2_stubs
}
}

size_t feedMpegCdStreamBytes(const uint8_t *data, size_t size)
{
if (!data || size == 0u)
{
return 0u;
}

size_t routedCount = 0u;
{
std::lock_guard<std::mutex> lock(g_mpeg_stub_mutex);

// Active only between notifyMpegCdStreamStart() and notifyMpegCdStreamEof().
const bool cdStreamActive =
g_mpeg_stub_state.cdStreamGeneration != 0u &&
!g_mpeg_stub_state.currentCdStreamEofSeen;
if (!cdStreamActive)
{
return 0u;
}

// Host-fed data carries no guest addresses, so no callback event is queued.
// TODO(host-feed callbacks): queue on MpegPlaybackState for a guest syscall to drain.
std::vector<MpegStreamCallbackEvent> callbackEvents;
for (auto &[mpegAddr, playback] : g_mpeg_stub_state.playbackByMpeg)
{
if (playback.cdStreamGeneration != g_mpeg_stub_state.cdStreamGeneration)
{
continue;
}
appendPssBytes(mpegAddr, playback, data, size, 0u, callbackEvents, false);
++routedCount;
}

// No decoder on the current generation took these bytes: stage them until the
// first sceMpegCreate on this generation; afterwards feed routes live.
if (routedCount == 0u && !g_mpeg_stub_state.cdStreamStageOverflowed)
{
if (g_mpeg_stub_state.cdStreamStageGeneration != g_mpeg_stub_state.cdStreamGeneration)
{
g_mpeg_stub_state.cdStreamStagedBytes.clear();
g_mpeg_stub_state.cdStreamStageGeneration = g_mpeg_stub_state.cdStreamGeneration;
}

if (g_mpeg_stub_state.cdStreamStagedBytes.size() + size > kMpegHostFeedStageCapBytes)
{
// Dropping the tail would hand a later decoder a gapped stream (staged
// prefix, then a hole, then live feed); discard the whole stage instead
// and let the next decoder resync cleanly on live feed.
g_mpeg_stub_state.cdStreamStagedBytes.clear();
g_mpeg_stub_state.cdStreamStageOverflowed = true;
}
else
{
g_mpeg_stub_state.cdStreamStagedBytes.insert(
g_mpeg_stub_state.cdStreamStagedBytes.end(), data, data + size);
}
}
}
g_mpeg_cv.notify_all();
return size;
}

void sceMpegFlush(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
(void)rdram;
Expand Down Expand Up @@ -1736,7 +1812,19 @@ namespace ps2_stubs

{
std::lock_guard<std::mutex> lock(g_mpeg_stub_mutex);
getPlaybackState(param_1) = makeFreshPlaybackState();
auto &pb = getPlaybackState(param_1);
pb = makeFreshPlaybackState();
if (!g_mpeg_stub_state.cdStreamStageOverflowed &&
!g_mpeg_stub_state.cdStreamStagedBytes.empty() &&
g_mpeg_stub_state.cdStreamStageGeneration == pb.cdStreamGeneration)
{
std::vector<MpegStreamCallbackEvent> replayEvents; // host feed: none consumed
appendPssBytes(param_1, pb,
g_mpeg_stub_state.cdStreamStagedBytes.data(),
g_mpeg_stub_state.cdStreamStagedBytes.size(),
0u, replayEvents, /*trackGuestAddrs=*/false);
g_mpeg_stub_state.cdStreamStagedBytes.clear();
}
}

const uint32_t puVar4 = uVar3 + 0x108u;
Expand Down
16 changes: 16 additions & 0 deletions ps2xRuntime/src/lib/Kernel/Stubs/MPEG.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,25 @@

namespace ps2_stubs
{
// Upper bound on how many host-fed bytes are held in the pre-decoder stage
// (see feedMpegCdStreamBytes below) before the stage is abandoned for the
// rest of the current CD-stream generation. Internal tuning knob, not part
// of the API contract.
constexpr size_t kMpegHostFeedStageCapBytes = 4u * 1024u * 1024u;

void resetMpegStubState();
void notifyMpegCdStreamStart();
void notifyMpegCdStreamEof();
// Push `size` bytes of the active CD movie stream's program-stream/PES data into
// the guest-driven MPEG decoder from host memory. Must be bracketed by
// notifyMpegCdStreamStart()/notifyMpegCdStreamEof(). Order-insensitive w.r.t.
// sceMpegCreate: bytes fed before any decoder exists on the current CD-stream
// generation are staged (bounded; see kMpegHostFeedStageCapBytes) and replayed
// into the next decoder created on that generation. Staged bytes are discarded on
// stream stop/restart. Returns `size` while a CD stream is active (routed or
// staged), 0 only when no CD stream is active. Thread-safe; stream callbacks are
// not dispatched on this path.
size_t feedMpegCdStreamBytes(const uint8_t *data, size_t size);
void sceMpegFlush(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceMpegAddBs(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
void sceMpegAddCallback(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
Expand Down
Loading