ppsspp/Core/HLE/sceMpeg.cpp
2013-06-08 22:53:36 +02:00

1379 lines
43 KiB
C++

// Copyright (c) 2012- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
// This code is part shamelessly "inspired" from JPSCP.
#include <map>
#include "sceMpeg.h"
#include "sceKernelThread.h"
#include "HLE.h"
#include "../HW/MediaEngine.h"
#include "Core/Config.h"
#include "Core/Reporting.h"
#include "GPU/GPUInterface.h"
#include "GPU/GPUState.h"
// MPEG AVC elementary stream.
static const int MPEG_AVC_ES_SIZE = 2048; // MPEG packet size.
// MPEG ATRAC elementary stream.
static const int MPEG_ATRAC_ES_SIZE = 2112;
static const int MPEG_ATRAC_ES_OUTPUT_SIZE = 8192;
// MPEG PCM elementary stream.
static const int MPEG_PCM_ES_SIZE = 320;
static const int MPEG_PCM_ES_OUTPUT_SIZE = 320;
// MPEG Userdata elementary stream.
static const int MPEG_DATA_ES_SIZE = 0xA0000;
static const int MPEG_DATA_ES_OUTPUT_SIZE = 0xA0000;
// MPEG analysis results.
static const int MPEG_VERSION_0012 = 0;
static const int MPEG_VERSION_0013 = 1;
static const int MPEG_VERSION_0014 = 2;
static const int MPEG_VERSION_0015 = 3;
// MPEG streams.
static const int MPEG_AVC_STREAM = 0;
static const int MPEG_ATRAC_STREAM = 1;
static const int MPEG_PCM_STREAM = 2;
static const int MPEG_DATA_STREAM = 3; // Arbitrary user defined type. Can represent audio or video.
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 int MPEG_AVC_DECODE_ERROR_FATAL = 0x80628002;
static const int atracDecodeDelayMs = 3000;
static const int avcFirstDelayMs = 3600;
static const int avcDecodeDelayMs = 5400; // Varies between 4700 and 6000.
static const int avcEmptyDelayMs = 320;
static const int mpegDecodeErrorDelayMs = 100;
static const int mpegTimestampPerSecond = 90000; // How many MPEG Timestamp units in a second.
static const int videoTimestampStep = 3003; // Value based on pmfplayer (mpegTimestampPerSecond / 29.970 (fps)).
static const int audioTimestampStep = 4180; // For audio play at 44100 Hz (2048 samples / 44100 * mpegTimestampPerSecond == 4180)
//static const int audioFirstTimestamp = 89249; // The first MPEG audio AU has always this timestamp
static const int audioFirstTimestamp = 90000; // The first MPEG audio AU has always this timestamp
static const s64 UNKNOWN_TIMESTAMP = -1;
// At least 2048 bytes of MPEG data is provided when analysing the MPEG header
static const int MPEG_HEADER_BUFFER_MINIMUM_SIZE = 2048;
static const int NUM_ES_BUFFERS = 2;
static const int PSP_ERROR_MPEG_NO_DATA = 0x80618001;
static const int TPSM_PIXEL_STORAGE_MODE_16BIT_BGR5650 = 0X00;
static const int TPSM_PIXEL_STORAGE_MODE_32BIT_ABGR8888 = 0X03;
int getMaxAheadTimestamp(const SceMpegRingBuffer &ringbuf) {
return std::max(40000, ringbuf.packets * 700); // empiric value from JPCSP, thanks!
}
// Internal structure
struct AvcContext {
int avcDetailFrameWidth;
int avcDetailFrameHeight;
int avcDecodeResult;
int avcFrameStatus;
};
struct StreamInfo {
int type;
int num;
int sid;
bool needsReset;
};
typedef std::map<u32, StreamInfo> StreamInfoMap;
// Internal structure
struct MpegContext {
MpegContext() : mediaengine(NULL) {}
~MpegContext() {
if (mediaengine != NULL) {
delete mediaengine;
}
}
void DoState(PointerWrap &p) {
p.Do(defaultFrameWidth);
p.Do(videoFrameCount);
p.Do(audioFrameCount);
p.Do(endOfAudioReached);
p.Do(endOfVideoReached);
p.Do(videoPixelMode);
p.Do(mpegMagic);
p.Do(mpegVersion);
p.Do(mpegRawVersion);
p.Do(mpegOffset);
p.Do(mpegStreamSize);
p.Do(mpegFirstTimestamp);
p.Do(mpegLastTimestamp);
p.Do(mpegFirstDate);
p.Do(mpegLastDate);
p.Do(mpegRingbufferAddr);
p.Do(mpegStreamAddr);
p.DoArray(esBuffers, NUM_ES_BUFFERS);
p.Do(avc);
p.Do(avcRegistered);
p.Do(atracRegistered);
p.Do(pcmRegistered);
p.Do(dataRegistered);
p.Do(ignoreAtrac);
p.Do(ignorePcm);
p.Do(ignoreAvc);
p.Do(isAnalyzed);
p.Do<u32, StreamInfo>(streamMap);
p.DoClass(mediaengine);
p.DoMarker("MpegContext");
}
u32 defaultFrameWidth;
int videoFrameCount;
int audioFrameCount;
bool endOfAudioReached;
bool endOfVideoReached;
int videoPixelMode;
u32 mpegMagic;
int mpegVersion;
u32 mpegRawVersion;
u32 mpegOffset;
u32 mpegStreamSize;
u32 mpegFirstTimestamp;
u32 mpegLastTimestamp;
u32 mpegFirstDate;
u32 mpegLastDate;
u32 mpegRingbufferAddr;
u32 mpegStreamAddr;
bool esBuffers[NUM_ES_BUFFERS];
AvcContext avc;
bool avcRegistered;
bool atracRegistered;
bool pcmRegistered;
bool dataRegistered;
bool ignoreAtrac;
bool ignorePcm;
bool ignoreAvc;
bool isAnalyzed;
StreamInfoMap streamMap;
MediaEngine *mediaengine;
};
static bool isMpegInit;
static u32 streamIdGen;
static bool isCurrentMpegAnalyzed;
static int actionPostPut;
static std::map<u32, MpegContext *> mpegMap;
static u32 lastMpegHandle = 0;
MpegContext *getMpegCtx(u32 mpegAddr) {
u32 mpeg = Memory::Read_U32(mpegAddr);
// TODO: Remove.
if (mpegMap.find(mpeg) == mpegMap.end())
{
ERROR_LOG_REPORT(HLE, "Bad mpeg handle %08x - using last one (%08x) instead", mpeg, lastMpegHandle);
mpeg = lastMpegHandle;
}
if (mpegMap.find(mpeg) == mpegMap.end())
return NULL;
return mpegMap[mpeg];
}
u32 getMpegHandle(u32 mpeg) {
return Memory::Read_U32(mpeg);
}
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->packetsFree = 0;
buf->packetSize = 2048;
buf->data = data;
buf->callback_addr = callback_addr;
buf->callback_args = callback_args;
buf->dataUpperBound = data + packets * 2048;
buf->semaID = -1;
buf->mpeg = 0;
}
u32 convertTimestampToDate(u32 ts) {
return ts; // TODO
}
void AnalyzeMpeg(u32 buffer_addr, MpegContext *ctx) {
ctx->mpegStreamAddr = buffer_addr;
ctx->mpegMagic = Memory::Read_U32(buffer_addr);
ctx->mpegRawVersion = Memory::Read_U32(buffer_addr + PSMF_STREAM_VERSION_OFFSET);
switch (ctx->mpegRawVersion) {
case PSMF_VERSION_0012:
ctx->mpegVersion = MPEG_VERSION_0012;
break;
case PSMF_VERSION_0013:
ctx->mpegVersion = MPEG_VERSION_0013;
break;
case PSMF_VERSION_0014:
ctx->mpegVersion = MPEG_VERSION_0014;
break;
case PSMF_VERSION_0015:
ctx->mpegVersion = MPEG_VERSION_0015;
break;
default:
ctx->mpegVersion = -1;
break;
}
ctx->mpegOffset = bswap32(Memory::Read_U32(buffer_addr + PSMF_STREAM_OFFSET_OFFSET));
ctx->mpegStreamSize = bswap32(Memory::Read_U32(buffer_addr + PSMF_STREAM_SIZE_OFFSET));
ctx->mpegFirstTimestamp = bswap32(Memory::Read_U32(buffer_addr + PSMF_FIRST_TIMESTAMP_OFFSET));
ctx->mpegLastTimestamp = bswap32(Memory::Read_U32(buffer_addr + PSMF_LAST_TIMESTAMP_OFFSET));
ctx->mpegFirstDate = convertTimestampToDate(ctx->mpegFirstTimestamp);
ctx->mpegLastDate = convertTimestampToDate(ctx->mpegLastTimestamp);
ctx->avc.avcDetailFrameWidth = (Memory::Read_U8(buffer_addr + 142) * 0x10);
ctx->avc.avcDetailFrameHeight = (Memory::Read_U8(buffer_addr + 143) * 0x10);
ctx->avc.avcDecodeResult = MPEG_AVC_DECODE_SUCCESS;
ctx->avc.avcFrameStatus = 0;
ctx->videoFrameCount = 0;
ctx->audioFrameCount = 0;
ctx->endOfAudioReached = false;
ctx->endOfVideoReached = false;
if (ctx->mpegMagic != PSMF_MAGIC || ctx->mpegVersion < 0 ||
(ctx->mpegOffset & 2047) != 0 || ctx->mpegOffset == 0) {
// mpeg header is invalid!
return;
}
if (ctx->mediaengine && (ctx->mpegStreamSize > 0) && !ctx->isAnalyzed) {
// init mediaEngine
ctx->mediaengine->loadStream(Memory::GetPointer(buffer_addr), ctx->mpegOffset, ctx->mpegOffset + ctx->mpegStreamSize);
ctx->mediaengine->setVideoDim();
}
// When used with scePsmf, some applications attempt to use sceMpegQueryStreamOffset
// and sceMpegQueryStreamSize, which forces a packet overwrite in the Media Engine and in
// the MPEG ringbuffer.
// Mark the current MPEG as analyzed to filter this, and restore it at sceMpegFinish.
ctx->isAnalyzed = true;
INFO_LOG(ME, "Stream offset: %d, Stream size: 0x%X", ctx->mpegOffset, ctx->mpegStreamSize);
INFO_LOG(ME, "First timestamp: %d, Last timestamp: %d", ctx->mpegFirstTimestamp, ctx->mpegLastTimestamp);
}
class PostPutAction : public Action {
public:
PostPutAction() {}
void setRingAddr(u32 ringAddr) { ringAddr_ = ringAddr; }
static Action *Create() { return new PostPutAction; }
void DoState(PointerWrap &p) { p.Do(ringAddr_); p.DoMarker("PostPutAction"); }
void run(MipsCall &call);
private:
u32 ringAddr_;
};
void __MpegInit() {
lastMpegHandle = 0;
streamIdGen = 1;
isCurrentMpegAnalyzed = false;
isMpegInit = false;
actionPostPut = __KernelRegisterActionType(PostPutAction::Create);
}
void __MpegDoState(PointerWrap &p) {
p.Do(lastMpegHandle);
p.Do(streamIdGen);
p.Do(isCurrentMpegAnalyzed);
p.Do(isMpegInit);
p.Do(actionPostPut);
__KernelRestoreActionType(actionPostPut, PostPutAction::Create);
p.Do(mpegMap);
p.DoMarker("sceMpeg");
}
void __MpegShutdown() {
std::map<u32, MpegContext *>::iterator it, end;
for (it = mpegMap.begin(), end = mpegMap.end(); it != end; ++it) {
delete it->second;
}
mpegMap.clear();
}
u32 sceMpegInit() {
if (isMpegInit) {
WARN_LOG(HLE, "sceMpegInit(): already initialized");
return ERROR_MPEG_ALREADY_INIT;
}
INFO_LOG(HLE, "sceMpegInit()");
isMpegInit = true;
return hleDelayResult(0, "mpeg init", 750);
}
u32 sceMpegRingbufferQueryMemSize(int packets)
{
DEBUG_LOG(HLE, "sceMpegRingbufferQueryMemSize(%i)", packets);
int size = packets * (104 + 2048);
return size;
}
u32 sceMpegRingbufferConstruct(u32 ringbufferAddr, u32 numPackets, u32 data, u32 size, u32 callbackAddr, u32 callbackArg)
{
DEBUG_LOG(HLE, "sceMpegRingbufferConstruct(%08x, %i, %08x, %i, %08x, %i)", ringbufferAddr, numPackets, data, size, callbackAddr, callbackArg);
SceMpegRingBuffer ring;
InitRingbuffer(&ring, numPackets, data, size, callbackAddr, callbackArg);
Memory::WriteStruct(ringbufferAddr, &ring);
return 0;
}
u32 sceMpegCreate(u32 mpegAddr, u32 dataPtr, u32 size, u32 ringbufferAddr, u32 frameWidth, u32 mode, u32 ddrTop)
{
if (size < MPEG_MEMSIZE) {
WARN_LOG(HLE, "ERROR_MPEG_NO_MEMORY=sceMpegCreate(%08x, %08x, %i, %08x, %i, %i, %i)",
mpegAddr, dataPtr, size, ringbufferAddr, frameWidth, mode, ddrTop);
return ERROR_MPEG_NO_MEMORY;
}
SceMpegRingBuffer ringbuffer;
if(ringbufferAddr != 0){
Memory::ReadStruct(ringbufferAddr, &ringbuffer);
if (ringbuffer.packetSize == 0) {
ringbuffer.packetsFree = 0;
} else {
ringbuffer.packetsFree = (ringbuffer.dataUpperBound - ringbuffer.data) / ringbuffer.packetSize;
}
ringbuffer.mpeg = mpegAddr;
Memory::WriteStruct(ringbufferAddr, &ringbuffer);
}
// Generate, and write mpeg handle into mpeg data, for some reason
int mpegHandle = dataPtr + 0x30;
Memory::Write_U32(mpegHandle, mpegAddr);
Memory::Memcpy(mpegHandle, "LIBMPEG.001", 12);
Memory::Write_U32(-1, mpegHandle + 12);
Memory::Write_U32(ringbufferAddr, mpegHandle + 16);
Memory::Write_U32(ringbuffer.dataUpperBound, mpegHandle + 20);
MpegContext *ctx = new MpegContext;
mpegMap[mpegHandle] = ctx;
lastMpegHandle = mpegHandle;
ctx->mpegRingbufferAddr = ringbufferAddr;
ctx->videoFrameCount = 0;
ctx->audioFrameCount = 0;
// TODO: What's the actual default?
ctx->videoPixelMode = TPSM_PIXEL_STORAGE_MODE_32BIT_ABGR8888;
ctx->avcRegistered = false;
ctx->atracRegistered = false;
ctx->pcmRegistered = false;
ctx->dataRegistered = false;
ctx->ignoreAtrac = false;
ctx->ignorePcm = false;
ctx->ignoreAvc = false;
ctx->defaultFrameWidth = frameWidth;
for (int i = 0; i < NUM_ES_BUFFERS; i++) {
ctx->esBuffers[i] = false;
}
// Detailed "analysis" is done later in Query* for some reason.
ctx->isAnalyzed = false;
ctx->mediaengine = new MediaEngine();
INFO_LOG(HLE, "%08x=sceMpegCreate(%08x, %08x, %i, %08x, %i, %i, %i)",
mpegHandle, mpegAddr, dataPtr, size, ringbufferAddr, frameWidth, mode, ddrTop);
return hleDelayResult(0, "mpeg create", 29000);
}
int sceMpegDelete(u32 mpeg)
{
MpegContext *ctx = getMpegCtx(mpeg);
if (!ctx) {
WARN_LOG(HLE, "sceMpegDelete(%08x): bad mpeg handle", mpeg);
return -1;
}
DEBUG_LOG(HLE, "sceMpegDelete(%08x)", mpeg);
delete ctx;
mpegMap.erase(Memory::Read_U32(mpeg));
return 0;
}
int sceMpegAvcDecodeMode(u32 mpeg, u32 modeAddr)
{
MpegContext *ctx = getMpegCtx(mpeg);
if (!ctx) {
WARN_LOG(HLE, "sceMpegAvcDecodeMode(%08x, %08x): bad mpeg handle", mpeg, modeAddr);
return -1;
}
DEBUG_LOG(HLE, "sceMpegAvcDecodeMode(%08x, %08x)", mpeg, modeAddr);
if (Memory::IsValidAddress(modeAddr)) {
int mode = Memory::Read_U32(modeAddr);
int pixelMode = Memory::Read_U32(modeAddr + 4);
if (pixelMode >= TPSM_PIXEL_STORAGE_MODE_16BIT_BGR5650 && pixelMode <= TPSM_PIXEL_STORAGE_MODE_32BIT_ABGR8888) {
ctx->videoPixelMode = pixelMode;
} else {
ERROR_LOG(HLE, "sceMpegAvcDecodeMode(%i, %i): unknown pixelMode ", mode, pixelMode);
}
} else {
ERROR_LOG(HLE, "sceMpegAvcDecodeMode(%08x, %08x): invalid modeAddr", mpeg, modeAddr);
return -1;
}
return 0;
}
int sceMpegQueryStreamOffset(u32 mpeg, u32 bufferAddr, u32 offsetAddr)
{
MpegContext *ctx = getMpegCtx(mpeg);
if (!ctx) {
WARN_LOG(HLE, "sceMpegQueryStreamOffset(%08x, %08x, %08x): bad mpeg handle", mpeg, bufferAddr, offsetAddr);
return -1;
}
DEBUG_LOG(HLE, "sceMpegQueryStreamOffset(%08x, %08x, %08x)", mpeg, bufferAddr, offsetAddr);
// Kinda destructive, no?
AnalyzeMpeg(bufferAddr, ctx);
if (ctx->mpegMagic != PSMF_MAGIC) {
ERROR_LOG(HLE, "sceMpegQueryStreamOffset: Bad PSMF magic");
Memory::Write_U32(0, offsetAddr);
return ERROR_MPEG_INVALID_VALUE;
} else if (ctx->mpegVersion < 0) {
ERROR_LOG(HLE, "sceMpegQueryStreamOffset: Bad version");
Memory::Write_U32(0, offsetAddr);
return ERROR_MPEG_BAD_VERSION;
} else if ((ctx->mpegOffset & 2047) != 0 || ctx->mpegOffset == 0) {
ERROR_LOG(HLE, "sceMpegQueryStreamOffset: Bad offset");
Memory::Write_U32(0, offsetAddr);
return ERROR_MPEG_INVALID_VALUE;
}
Memory::Write_U32(ctx->mpegOffset, offsetAddr);
return 0;
}
u32 sceMpegQueryStreamSize(u32 bufferAddr, u32 sizeAddr)
{
DEBUG_LOG(HLE, "sceMpegQueryStreamSize(%08x, %08x)", bufferAddr, sizeAddr);
MpegContext ctx;
ctx.mediaengine = 0;
AnalyzeMpeg(bufferAddr, &ctx);
if (ctx.mpegMagic != PSMF_MAGIC) {
ERROR_LOG(HLE, "sceMpegQueryStreamSize: Bad PSMF magic");
Memory::Write_U32(0, sizeAddr);
return ERROR_MPEG_INVALID_VALUE;
} else if ((ctx.mpegOffset & 2047) != 0 ) {
ERROR_LOG(HLE, "sceMpegQueryStreamSize: Bad offset");
Memory::Write_U32(0, sizeAddr);
return ERROR_MPEG_INVALID_VALUE;
}
Memory::Write_U32(ctx.mpegStreamSize, sizeAddr);
return 0;
}
int sceMpegRegistStream(u32 mpeg, u32 streamType, u32 streamNum)
{
MpegContext *ctx = getMpegCtx(mpeg);
if (!ctx)
{
WARN_LOG(HLE, "sceMpegRegistStream(%08x, %i, %i): bad mpeg handle", mpeg, streamType, streamNum);
return -1;
}
DEBUG_LOG(HLE, "sceMpegRegistStream(%08x, %i, %i)", mpeg, streamType, streamNum);
switch (streamType) {
case MPEG_AVC_STREAM:
ctx->avcRegistered = true;
break;
case MPEG_AUDIO_STREAM:
case MPEG_ATRAC_STREAM:
ctx->atracRegistered = true;
break;
case MPEG_PCM_STREAM:
ctx->pcmRegistered = true;
break;
case MPEG_DATA_STREAM:
ctx->dataRegistered = true;
break;
default :
DEBUG_LOG(HLE, "sceMpegRegistStream(%i) : unknown stream type", streamType);
break;
}
// ...
u32 sid = streamIdGen++;
StreamInfo info;
info.type = streamType;
info.num = streamNum;
info.needsReset = true;
ctx->streamMap[sid] = info;
return sid;
}
int sceMpegMallocAvcEsBuf(u32 mpeg)
{
MpegContext *ctx = getMpegCtx(mpeg);
if (!ctx) {
WARN_LOG(HLE, "sceMpegMallocAvcEsBuf(%08x): bad mpeg handle", mpeg);
return -1;
}
DEBUG_LOG(HLE, "sceMpegMallocAvcEsBuf(%08x)", mpeg);
// Doesn't actually malloc, just keeps track of a couple of flags
for (int i = 0; i < NUM_ES_BUFFERS; i++) {
if (!ctx->esBuffers[i]) {
ctx->esBuffers[i] = true;
return i + 1;
}
}
// No es buffer
return 0;
}
int sceMpegFreeAvcEsBuf(u32 mpeg, int esBuf)
{
MpegContext *ctx = getMpegCtx(mpeg);
if (!ctx) {
WARN_LOG(HLE, "sceMpegFreeAvcEsBuf(%08x, %i): bad mpeg handle", mpeg, esBuf);
return -1;
}
DEBUG_LOG(HLE, "sceMpegFreeAvcEsBuf(%08x, %i)", mpeg, esBuf);
if (esBuf == 0) {
return ERROR_MPEG_INVALID_VALUE;
}
if (esBuf >= 1 && esBuf <= NUM_ES_BUFFERS) {
// TODO: Check if it's already been free'd?
ctx->esBuffers[esBuf - 1] = false;
}
return 0;
}
u32 sceMpegAvcDecode(u32 mpeg, u32 auAddr, u32 frameWidth, u32 bufferAddr, u32 initAddr)
{
MpegContext *ctx = getMpegCtx(mpeg);
if (!ctx) {
WARN_LOG(HLE, "sceMpegAvcDecode(%08x, %08x, %d, %08x, %08x): bad mpeg handle", mpeg, auAddr, frameWidth, bufferAddr, initAddr);
return 0;
}
if (!Memory::IsValidAddress(auAddr) || !Memory::IsValidAddress(bufferAddr) || !Memory::IsValidAddress(initAddr)) {
ERROR_LOG(HLE, "sceMpegAvcDecode: bad addresses");
return 0;
}
if (frameWidth == 0) { // wtf, go sudoku passes in 0xccccccccc
if (!ctx->defaultFrameWidth) {
frameWidth = ctx->avc.avcDetailFrameWidth;
} else {
frameWidth = ctx->defaultFrameWidth;
}
}
SceMpegAu avcAu;
avcAu.read(auAddr);
SceMpegRingBuffer ringbuffer;
Memory::ReadStruct(ctx->mpegRingbufferAddr, &ringbuffer);
if (ringbuffer.packetsRead == 0 || ctx->mediaengine->IsVideoEnd()) {
WARN_LOG(HLE, "sceMpegAvcDecode(%08x, %08x, %d, %08x, %08x): mpeg buffer empty", mpeg, auAddr, frameWidth, bufferAddr, initAddr);
return hleDelayResult(MPEG_AVC_DECODE_ERROR_FATAL, "mpeg buffer empty", avcEmptyDelayMs);
}
u32 buffer = Memory::Read_U32(bufferAddr);
u32 init = Memory::Read_U32(initAddr);
DEBUG_LOG(HLE, "*buffer = %08x, *init = %08x", buffer, init);
if (ctx->mediaengine->stepVideo(ctx->videoPixelMode)) {
int bufferSize = ctx->mediaengine->writeVideoImage(Memory::GetPointer(buffer), frameWidth, ctx->videoPixelMode);
gpu->InvalidateCache(buffer, bufferSize, GPU_INVALIDATE_SAFE);
ctx->avc.avcFrameStatus = 1;
ctx->videoFrameCount++;
} else {
ctx->avc.avcFrameStatus = 0;
}
ringbuffer.packetsFree = std::max(0, ringbuffer.packets - ctx->mediaengine->getBufferedSize() / 2048);
avcAu.pts = ctx->mediaengine->getVideoTimeStamp();
ctx->avc.avcDecodeResult = MPEG_AVC_DECODE_SUCCESS;
// Flush structs back to memory
avcAu.write(auAddr);
Memory::WriteStruct(ctx->mpegRingbufferAddr, &ringbuffer);
Memory::Write_U32(ctx->avc.avcFrameStatus, initAddr); // 1 = showing, 0 = not showing
DEBUG_LOG(HLE, "sceMpegAvcDecode(%08x, %08x, %i, %08x, %08x)", mpeg, auAddr, frameWidth, bufferAddr, initAddr);
if (ctx->videoFrameCount <= 1)
return hleDelayResult(0, "mpeg decode", avcFirstDelayMs);
else
return hleDelayResult(0, "mpeg decode", avcDecodeDelayMs);
//hleEatMicro(3300);
//return hleDelayResult(0, "mpeg decode", 200);
}
u32 sceMpegAvcDecodeStop(u32 mpeg, u32 frameWidth, u32 bufferAddr, u32 statusAddr)
{
ERROR_LOG(HLE, "sceMpegAvcDecodeStop(%08x, %08x, %08x, %08x)", mpeg, frameWidth, bufferAddr, statusAddr);
if (Memory::IsValidAddress(statusAddr)) {
Memory::Write_U32(0,statusAddr);
} else {
ERROR_LOG(HLE, "sceMpegAvcDecodeStop(%08x, %08x): invalid statusAddr", mpeg, statusAddr);
return -1;
}
return 0;
}
u32 sceMpegUnRegistStream(u32 mpeg, int streamUid)
{
MpegContext *ctx = getMpegCtx(mpeg);
if (!ctx)
{
WARN_LOG(HLE, "sceMpegUnRegistStream(%08x, %i): bad mpeg handle", mpeg, streamUid);
return -1;
}
StreamInfo info = {0};
switch (info.type) {
case MPEG_AVC_STREAM:
ctx->avcRegistered = false;
break;
case MPEG_AUDIO_STREAM:
case MPEG_ATRAC_STREAM:
ctx->atracRegistered = false;
break;
case MPEG_PCM_STREAM:
ctx->pcmRegistered = false;
break;
case MPEG_DATA_STREAM:
ctx->dataRegistered = false;
break;
default :
DEBUG_LOG(HLE, "sceMpegUnRegistStream(%i) : unknown streamID ", streamUid);
break;
}
ctx->streamMap[streamUid] = info;
info.type = -1;
info.sid = -1 ;
info.needsReset = true;
ctx->isAnalyzed = false;
return 0;
}
int sceMpegAvcDecodeDetail(u32 mpeg, u32 detailAddr)
{
if (!Memory::IsValidAddress(detailAddr))
{
WARN_LOG(HLE, "sceMpegAvcDecodeDetail(%08x, %08x): invalid detailAddr", mpeg, detailAddr);
return -1;
}
MpegContext *ctx = getMpegCtx(mpeg);
if (!ctx)
{
WARN_LOG(HLE, "sceMpegAvcDecodeDetail(%08x, %08x): bad mpeg handle", mpeg, detailAddr);
return -1;
}
DEBUG_LOG(HLE, "sceMpegAvcDecodeDetail(%08x, %08x)", mpeg, detailAddr);
Memory::Write_U32(ctx->avc.avcDecodeResult, detailAddr + 0);
Memory::Write_U32(ctx->videoFrameCount, detailAddr + 4);
Memory::Write_U32(ctx->avc.avcDetailFrameWidth, detailAddr + 8);
Memory::Write_U32(ctx->avc.avcDetailFrameHeight, detailAddr + 12);
Memory::Write_U32(0, detailAddr + 16);
Memory::Write_U32(0, detailAddr + 20);
Memory::Write_U32(0, detailAddr + 24);
Memory::Write_U32(0, detailAddr + 28);
Memory::Write_U32(ctx->avc.avcFrameStatus, detailAddr + 32);
return 0;
}
u32 sceMpegAvcDecodeStopYCbCr(u32 mpeg, u32 bufferAddr, u32 statusAddr)
{
ERROR_LOG(HLE, "UNIMPL sceMpegAvcDecodeStopYCbCr(%08x, %08x, %08x)", mpeg, bufferAddr, statusAddr);
return 0;
}
int sceMpegAvcDecodeYCbCr(u32 mpeg, u32 auAddr, u32 bufferAddr, u32 initAddr)
{
MpegContext *ctx = getMpegCtx(mpeg);
if (!ctx) {
WARN_LOG(HLE, "sceMpegAvcDecodeYCbCr(%08x, %08x, %08x, %08x): bad mpeg handle", mpeg, auAddr, bufferAddr, initAddr);
return 0;
}
if (!Memory::IsValidAddress(auAddr) || !Memory::IsValidAddress(bufferAddr) || !Memory::IsValidAddress(initAddr)) {
ERROR_LOG(HLE, "sceMpegAvcDecodeYCbCr: bad addresses");
return 0;
}
SceMpegAu avcAu;
avcAu.read(auAddr);
SceMpegRingBuffer ringbuffer;
Memory::ReadStruct(ctx->mpegRingbufferAddr, &ringbuffer);
if (ringbuffer.packetsRead == 0) {
// empty!
return hleDelayResult(MPEG_AVC_DECODE_ERROR_FATAL, "mpeg buffer empty", avcEmptyDelayMs);
}
u32 buffer = Memory::Read_U32(bufferAddr);
u32 init = Memory::Read_U32(initAddr);
DEBUG_LOG(HLE, "*buffer = %08x, *init = %08x", buffer, init);
if (ctx->mediaengine->stepVideo(ctx->videoPixelMode)) {
// do nothing
;
}
ringbuffer.packetsFree = std::max(0, ringbuffer.packets - ctx->mediaengine->getBufferedSize() / 2048);
avcAu.pts = ctx->mediaengine->getVideoTimeStamp();
ctx->avc.avcFrameStatus = 1;
ctx->videoFrameCount++;
ctx->avc.avcDecodeResult = MPEG_AVC_DECODE_SUCCESS;
// Flush structs back to memory
avcAu.write(auAddr);
Memory::WriteStruct(ctx->mpegRingbufferAddr, &ringbuffer);
Memory::Write_U32(ctx->avc.avcFrameStatus, initAddr); // 1 = showing, 0 = not showing
DEBUG_LOG(HLE, "UNIMPL sceMpegAvcDecodeYCbCr(%08x, %08x, %08x, %08x)", mpeg, auAddr, bufferAddr, initAddr);
if (ctx->videoFrameCount <= 1)
return hleDelayResult(0, "mpeg decode", avcFirstDelayMs);
else
return hleDelayResult(0, "mpeg decode", avcDecodeDelayMs);
//hleEatMicro(3300);
//return hleDelayResult(0, "mpeg decode", 200);
}
u32 sceMpegAvcDecodeFlush(u32 mpeg)
{
MpegContext *ctx = getMpegCtx(mpeg);
ERROR_LOG(HLE, "UNIMPL sceMpegAvcDecodeFlush(%08x)", mpeg);
if ( ctx->videoFrameCount > 0 || ctx->audioFrameCount > 0) {
//__MpegFinish();
}
return 0;
}
int sceMpegInitAu(u32 mpeg, u32 bufferAddr, u32 auPointer)
{
MpegContext *ctx = getMpegCtx(mpeg);
if (!ctx) {
WARN_LOG(HLE, "sceMpegInitAu(%08x, %i, %08x): bad mpeg handle", mpeg, bufferAddr, auPointer);
return -1;
}
DEBUG_LOG(HLE, "sceMpegInitAu(%08x, %i, %08x)", mpeg, bufferAddr, auPointer);
SceMpegAu sceAu;
sceAu.read(auPointer);
if (bufferAddr >= 1 && bufferAddr <= (u32)NUM_ES_BUFFERS && ctx->esBuffers[bufferAddr - 1]) {
// This esbuffer has been allocated for Avc.
sceAu.esBuffer = bufferAddr; // Can this be right??? not much of a buffer pointer..
sceAu.esSize = MPEG_AVC_ES_SIZE;
sceAu.dts = 0;
sceAu.pts = 0;
sceAu.write(auPointer);
} else {
// This esbuffer has been left as Atrac.
sceAu.esBuffer = bufferAddr;
sceAu.esSize = MPEG_ATRAC_ES_SIZE;
sceAu.pts = 0;
sceAu.dts = UNKNOWN_TIMESTAMP;
sceAu.write(auPointer);
}
return 0;
}
int sceMpegQueryAtracEsSize(u32 mpeg, u32 esSizeAddr, u32 outSizeAddr)
{
if (!Memory::IsValidAddress(esSizeAddr) || !Memory::IsValidAddress(outSizeAddr)) {
ERROR_LOG(HLE, "sceMpegQueryAtracEsSize(%08x, %08x, %08x) - bad address", mpeg, esSizeAddr, outSizeAddr);
return -1;
}
DEBUG_LOG(HLE, "sceMpegQueryAtracEsSize(%08x, %08x, %08x)", mpeg, esSizeAddr, outSizeAddr);
Memory::Write_U32(MPEG_ATRAC_ES_SIZE, esSizeAddr);
Memory::Write_U32(MPEG_ATRAC_ES_OUTPUT_SIZE, outSizeAddr);
return 0;
}
int sceMpegRingbufferAvailableSize(u32 ringbufferAddr)
{
if (!Memory::IsValidAddress(ringbufferAddr)) {
ERROR_LOG(HLE, "sceMpegRingbufferAvailableSize(%08x) - bad address", ringbufferAddr);
return -1;
}
SceMpegRingBuffer ringbuffer;
Memory::ReadStruct(ringbufferAddr, &ringbuffer);
DEBUG_LOG(HLE, "%i=sceMpegRingbufferAvailableSize(%08x)", ringbuffer.packetsFree, ringbufferAddr);
MpegContext *ctx = getMpegCtx(ringbuffer.mpeg);
int result = std::min(ringbuffer.packetsFree, ctx->mediaengine->getRemainSize() / 2048);
return ringbuffer.packetsFree;
}
void PostPutAction::run(MipsCall &call) {
SceMpegRingBuffer ringbuffer;
Memory::ReadStruct(ringAddr_, &ringbuffer);
MpegContext *ctx = getMpegCtx(ringbuffer.mpeg);
int packetsAdded = currentMIPS->r[2];
if (packetsAdded > 0) {
if (packetsAdded > ringbuffer.packetsFree) {
WARN_LOG(HLE, "sceMpegRingbufferPut clamping packetsAdded old=%i new=%i", packetsAdded, ringbuffer.packetsFree);
packetsAdded = ringbuffer.packetsFree;
}
int actuallyAdded = ctx->mediaengine->addStreamData(Memory::GetPointer(ringbuffer.data), packetsAdded * 2048) / 2048;
if (actuallyAdded != packetsAdded) {
WARN_LOG_REPORT(HLE, "sceMpegRingbufferPut(): unable to enqueue all added packets, going to overwrite some frames.");
}
ringbuffer.packetsRead += packetsAdded;
ringbuffer.packetsWritten += packetsAdded;
ringbuffer.packetsFree -= packetsAdded;
}
DEBUG_LOG(HLE, "packetAdded: %i packetsRead: %i packetsTotal: %i", packetsAdded, ringbuffer.packetsRead, ringbuffer.packets);
Memory::WriteStruct(ringAddr_, &ringbuffer);
call.setReturnValue(packetsAdded);
}
// Program signals that it has written data to the ringbuffer and gets a callback ?
u32 sceMpegRingbufferPut(u32 ringbufferAddr, u32 numPackets, u32 available)
{
DEBUG_LOG(HLE, "sceMpegRingbufferPut(%08x, %i, %i)", ringbufferAddr, numPackets, available);
numPackets = std::min(numPackets, available);
if (numPackets <= 0)
return 0;
SceMpegRingBuffer ringbuffer;
Memory::ReadStruct(ringbufferAddr, &ringbuffer);
MpegContext *ctx = getMpegCtx(ringbuffer.mpeg);
if (!ctx) {
WARN_LOG(HLE, "sceMpegRingbufferPut(%08x, %i, %i): bad mpeg handle %08x", ringbufferAddr, numPackets, available, ringbuffer.mpeg);
return 0;
}
// Execute callback function as a direct MipsCall, no blocking here so no messing around with wait states etc
if (ringbuffer.callback_addr) {
PostPutAction *action = (PostPutAction *)__KernelCreateAction(actionPostPut);
action->setRingAddr(ringbufferAddr);
// 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 args[3] = {(u32)ringbuffer.data, packetsThisRound, (u32)ringbuffer.callback_args};
__KernelDirectMipsCall(ringbuffer.callback_addr, action, args, 3, false);
} else {
ERROR_LOG(HLE, "sceMpegRingbufferPut: callback_addr zero");
}
return 0;
}
int sceMpegGetAvcAu(u32 mpeg, u32 streamId, u32 auAddr, u32 attrAddr)
{
MpegContext *ctx = getMpegCtx(mpeg);
if (!ctx) {
WARN_LOG(HLE, "sceMpegGetAvcAu(%08x, %08x, %08x, %08x): bad mpeg handle", mpeg, streamId, auAddr, attrAddr);
return -1;
}
SceMpegRingBuffer mpegRingbuffer;
Memory::ReadStruct(ctx->mpegRingbufferAddr, &mpegRingbuffer);
SceMpegAu sceAu;
sceAu.read(auAddr);
if (mpegRingbuffer.packetsRead == 0 || mpegRingbuffer.packetsFree == mpegRingbuffer.packets) {
DEBUG_LOG(HLE, "PSP_ERROR_MPEG_NO_DATA=sceMpegGetAvcAu(%08x, %08x, %08x, %08x)", mpeg, streamId, auAddr, attrAddr);
sceAu.pts = -1;
sceAu.dts = -1;
sceAu.write(auAddr);
// TODO: Does this really reschedule?
return hleDelayResult(PSP_ERROR_MPEG_NO_DATA, "mpeg get avc", mpegDecodeErrorDelayMs);
}
auto streamInfo = ctx->streamMap.find(streamId);
if (streamInfo == ctx->streamMap.end())
{
ERROR_LOG(HLE, "sceMpegGetAvcAu - bad stream id %i", streamId);
return -1;
}
if (streamInfo->second.needsReset)
{
sceAu.pts = 0;
streamInfo->second.needsReset = false;
}
/*// Wait for audio if too much ahead
if (ctx->atracRegistered && (ctx->mediaengine->getVideoTimeStamp() > ctx->mediaengine->getAudioTimeStamp() + getMaxAheadTimestamp(mpegRingbuffer)))
{
ERROR_LOG(HLE, "sceMpegGetAvcAu - video too much ahead");
// TODO: Does this really reschedule?
return hleDelayResult(PSP_ERROR_MPEG_NO_DATA, "mpeg get avc", mpegDecodeErrorDelayMs);
}*/
int result = 0;
sceAu.pts = ctx->mediaengine->getVideoTimeStamp();
sceAu.dts = sceAu.pts - videoTimestampStep;
if (ctx->mediaengine->IsVideoEnd()) {
INFO_LOG(HLE, "video end reach. pts: %i dts: %i", (int)sceAu.pts, (int)ctx->mediaengine->getLastTimeStamp());
mpegRingbuffer.packetsFree = mpegRingbuffer.packets;
Memory::WriteStruct(ctx->mpegRingbufferAddr, &mpegRingbuffer);
result = PSP_ERROR_MPEG_NO_DATA;
}
// The avcau struct may have been modified by mediaengine, write it back.
sceAu.write(auAddr);
if (Memory::IsValidAddress(attrAddr)) {
Memory::Write_U32(1, attrAddr);
}
DEBUG_LOG(HLE, "%x=sceMpegGetAvcAu(%08x, %08x, %08x, %08x)", result, mpeg, streamId, auAddr, attrAddr);
// TODO: sceMpegGetAvcAu seems to modify esSize, and delay when it's > 1000 or something.
// There's definitely more to it, but ultimately it seems games should expect it to delay randomly.
return hleDelayResult(result, "mpeg get avc", 100);
}
u32 sceMpegFinish()
{
if (!isMpegInit)
{
WARN_LOG(HLE, "sceMpegFinish(...): not initialized");
return ERROR_MPEG_NOT_YET_INIT;
}
INFO_LOG(HLE, "sceMpegFinish(...)");
isMpegInit = false;
//__MpegFinish();
return hleDelayResult(0, "mpeg finish", 250);
}
u32 sceMpegQueryMemSize()
{
DEBUG_LOG(HLE, "sceMpegQueryMemSize()");
return 0x10000; // 64K
}
int sceMpegGetAtracAu(u32 mpeg, u32 streamId, u32 auAddr, u32 attrAddr)
{
MpegContext *ctx = getMpegCtx(mpeg);
if (!ctx) {
WARN_LOG(HLE, "sceMpegGetAtracAu(%08x, %08x, %08x, %08x): bad mpeg handle", mpeg, streamId, auAddr, attrAddr);
return -1;
}
SceMpegRingBuffer mpegRingbuffer;
Memory::ReadStruct(ctx->mpegRingbufferAddr, &mpegRingbuffer);
SceMpegAu sceAu;
sceAu.read(auAddr);
auto streamInfo = ctx->streamMap.find(streamId);
if (streamInfo != ctx->streamMap.end() && streamInfo->second.needsReset)
{
sceAu.pts = 0;
streamInfo->second.needsReset = false;
}
// The audio can end earlier than the video does.
if (mpegRingbuffer.packetsFree == mpegRingbuffer.packets || (ctx->mediaengine->IsAudioEnd() && !ctx->mediaengine->IsVideoEnd())) {
DEBUG_LOG(HLE, "PSP_ERROR_MPEG_NO_DATA=sceMpegGetAtracAu(%08x, %08x, %08x, %08x)", mpeg, streamId, auAddr, attrAddr);
// TODO: Does this really delay?
return hleDelayResult(PSP_ERROR_MPEG_NO_DATA, "mpeg get atrac", mpegDecodeErrorDelayMs);
}
int result = 0;
sceAu.pts = ctx->mediaengine->getAudioTimeStamp();
if (ctx->mediaengine->IsVideoEnd()) {
INFO_LOG(HLE, "video end reach. pts: %i dts: %i", (int)sceAu.pts, (int)ctx->mediaengine->getLastTimeStamp());
mpegRingbuffer.packetsFree = mpegRingbuffer.packets;
Memory::WriteStruct(ctx->mpegRingbufferAddr, &mpegRingbuffer);
result = PSP_ERROR_MPEG_NO_DATA;
}
sceAu.write(auAddr);
if (Memory::IsValidAddress(attrAddr)) {
Memory::Write_U32(0, attrAddr);
}
DEBUG_LOG(HLE, "%x=sceMpegGetAtracAu(%08x, %08x, %08x, %08x)", result, mpeg, streamId, auAddr, attrAddr);
// TODO: Not clear on exactly when this delays.
return hleDelayResult(result, "mpeg get atrac", 100);
}
int sceMpegQueryPcmEsSize(u32 mpeg, u32 esSizeAddr, u32 outSizeAddr)
{
if (Memory::IsValidAddress(esSizeAddr) && Memory::IsValidAddress(outSizeAddr)) {
DEBUG_LOG(HLE, "sceMpegQueryPcmEsSize(%08x, %08x, %08x)", mpeg, esSizeAddr, outSizeAddr);
Memory::Write_U32(MPEG_PCM_ES_SIZE, esSizeAddr);
Memory::Write_U32(MPEG_PCM_ES_OUTPUT_SIZE, outSizeAddr);
return 0;
}
ERROR_LOG(HLE, "sceMpegQueryPcmEsSize - bad pointers(%08x, %08x, %08x)", mpeg, esSizeAddr, outSizeAddr);
return -1;
}
u32 sceMpegChangeGetAuMode(u32 mpeg, int streamUid, int mode)
{
MpegContext *ctx = getMpegCtx(mpeg);
if (!ctx) {
WARN_LOG(HLE, "sceMpegChangeGetAuMode(%08x, %i, %i): bad mpeg handle", mpeg, streamUid, mode);
return -1;
}
// NOTE: Where is the info supposed to come from?
StreamInfo info = {0};
info.sid = streamUid;
if (info.sid) {
switch (info.type) {
case MPEG_AVC_STREAM:
if(mode == MPEG_AU_MODE_DECODE) {
ctx->ignoreAvc = false;
} else if (mode == MPEG_AU_MODE_SKIP) {
ctx->ignoreAvc = true;
}
break;
case MPEG_AUDIO_STREAM:
case MPEG_ATRAC_STREAM:
if(mode == MPEG_AU_MODE_DECODE) {
ctx->ignoreAtrac = false;
} else if (mode == MPEG_AU_MODE_SKIP) {
ctx->ignoreAtrac = true;
}
break;
case MPEG_PCM_STREAM:
if(mode == MPEG_AU_MODE_DECODE) {
ctx->ignorePcm = false;
} else if (mode == MPEG_AU_MODE_SKIP) {
ctx->ignorePcm = true;
}
break;
default:
ERROR_LOG(HLE, "UNIMPL sceMpegChangeGetAuMode(%08x, %i): unkown streamID", mpeg, streamUid);
break;
}
} else {
ERROR_LOG(HLE, "UNIMPL sceMpegChangeGetAuMode(%08x, %i): unkown streamID", mpeg, streamUid);
}
return 0;
}
u32 sceMpegChangeGetAvcAuMode(u32 mpeg, u32 stream_addr, int mode)
{
ERROR_LOG(HLE, "UNIMPL sceMpegChangeGetAvcAuMode(%08x, %08x, %i)", mpeg, stream_addr, mode);
return 0;
}
u32 sceMpegGetPcmAu(u32 mpeg, int streamUid, u32 auAddr, u32 attrAddr)
{
ERROR_LOG(HLE, "UNIMPL sceMpegGetPcmAu(%08x, %i, %08x, %08x)", mpeg, streamUid, auAddr, attrAddr);
return 0;
}
u32 sceMpegRingbufferQueryPackNum(int memorySize)
{
ERROR_LOG(HLE, "sceMpegRingbufferQueryPackNum(%i)", memorySize);
int packets = memorySize / (2048 + 104);
return packets;
}
u32 sceMpegFlushAllStream(u32 mpeg)
{
MpegContext *ctx = getMpegCtx(mpeg);
if (!ctx) {
WARN_LOG(HLE, "sceMpegFlushAllStream(%08x): bad mpeg handle", mpeg);
return -1;
}
WARN_LOG(HLE, "UNIMPL sceMpegFlushAllStream(%08x)", mpeg);
ctx->isAnalyzed = false;
if (Memory::IsValidAddress(ctx->mpegRingbufferAddr))
{
auto ringbuffer = Memory::GetStruct<SceMpegRingBuffer>(ctx->mpegRingbufferAddr);
ringbuffer->packetsFree = ringbuffer->packets;
ringbuffer->packetsRead = 0;
ringbuffer->packetsWritten = 0;
}
return 0;
}
u32 sceMpegFlushStream(u32 mpeg, int stream_addr)
{
ERROR_LOG(HLE, "UNIMPL sceMpegFlushStream(%08x, %i)", mpeg , stream_addr);
//__MpegFinish();
return 0;
}
u32 sceMpegAvcCopyYCbCr(u32 mpeg, u32 sourceAddr, u32 YCbCrAddr)
{
ERROR_LOG(HLE, "UNIMPL sceMpegAvcCopyYCbCr(%08x, %08x, %08x)", mpeg, sourceAddr, YCbCrAddr);
return 0;
}
u32 sceMpegAtracDecode(u32 mpeg, u32 auAddr, u32 bufferAddr, int init)
{
DEBUG_LOG(HLE, "UNIMPL sceMpegAtracDecode(%08x, %08x, %08x, %i)", mpeg, auAddr, bufferAddr, init);
MpegContext *ctx = getMpegCtx(mpeg);
if (!ctx) {
return 0;
}
if (!Memory::IsValidAddress(auAddr) || !Memory::IsValidAddress(bufferAddr)) {
ERROR_LOG(HLE, "sceMpegAtracDecode: bad addresses");
return 0;
}
SceMpegAu avcAu;
avcAu.read(auAddr);
Memory::Memset(bufferAddr, 0, MPEG_ATRAC_ES_OUTPUT_SIZE);
ctx->mediaengine->getAudioSamples(Memory::GetPointer(bufferAddr));
avcAu.pts = ctx->mediaengine->getAudioTimeStamp();
avcAu.write(auAddr);
return hleDelayResult(0, "mpeg atrac decode", atracDecodeDelayMs);
//hleEatMicro(4000);
//return hleDelayResult(0, "mpeg atrac decode", 200);
}
// YCbCr -> RGB color space conversion
u32 sceMpegAvcCsc(u32 mpeg, u32 sourceAddr, u32 rangeAddr, int frameWidth, u32 destAddr)
{
DEBUG_LOG(HLE, "sceMpegAvcCsc(%08x, %08x, %08x, %i, %08x)", mpeg, sourceAddr, rangeAddr, frameWidth, destAddr);
MpegContext *ctx = getMpegCtx(mpeg);
if (!ctx)
return -1;
if ((!Memory::IsValidAddress(rangeAddr)) || (!Memory::IsValidAddress(destAddr)))
return -1;
int x = Memory::Read_U32(rangeAddr);
int y = Memory::Read_U32(rangeAddr + 4);
int width = Memory::Read_U32(rangeAddr + 8);
int height = Memory::Read_U32(rangeAddr + 12);
int destSize = ctx->mediaengine->writeVideoImageWithRange(Memory::GetPointer(destAddr), frameWidth, ctx->videoPixelMode,
x, y, width, height);
gpu->InvalidateCache(destAddr, destSize, GPU_INVALIDATE_SAFE);
return 0;
}
u32 sceMpegRingbufferDestruct(u32 ringbufferAddr)
{
DEBUG_LOG(HLE, "sceMpegRingbufferDestruct(%08x)", ringbufferAddr);
if (Memory::IsValidAddress(ringbufferAddr))
{
auto ringbuffer = Memory::GetStruct<SceMpegRingBuffer>(ringbufferAddr);
ringbuffer->packetsFree = ringbuffer->packets;
ringbuffer->packetsRead = 0;
ringbuffer->packetsWritten = 0;
}
return 0;
}
u32 sceMpegAvcInitYCbCr(u32 mpeg, int mode, int width, int height, u32 ycbcr_addr)
{
ERROR_LOG(HLE, "UNIMPL sceMpegAvcInitYCbCr(%08x, %i, %i, %i, %08x)", mpeg, mode, width, height, ycbcr_addr);
return 0;
}
int sceMpegAvcQueryYCbCrSize(u32 mpeg, u32 mode, u32 width, u32 height, u32 resultAddr)
{
if ((width & 15) != 0 || (height & 15) != 0 || height > 272 || width > 480)
{
ERROR_LOG(HLE, "sceMpegAvcQueryYCbCrSize: bad w/h %i x %i", width, height);
return ERROR_MPEG_INVALID_VALUE;
}
DEBUG_LOG(HLE, "sceMpegAvcQueryYCbCrSize(%08x, %i, %i, %i, %08x)", mpeg, mode, width, height, resultAddr);
int size = (width / 2) * (height / 2) * 6 + 128;
Memory::Write_U32(size, resultAddr);
return 0;
}
u32 sceMpegQueryUserdataEsSize(u32 mpeg, u32 esSizeAddr, u32 outSizeAddr)
{
if (Memory::IsValidAddress(esSizeAddr) && Memory::IsValidAddress(outSizeAddr)) {
DEBUG_LOG(HLE, "sceMpegQueryUserdataEsSize(%08x, %08x, %08x)", mpeg, esSizeAddr, outSizeAddr);
Memory::Write_U32(MPEG_DATA_ES_SIZE, esSizeAddr);
Memory::Write_U32(MPEG_DATA_ES_OUTPUT_SIZE, outSizeAddr);
return 0;
}
ERROR_LOG(HLE, "sceMpegQueryUserdataEsSize - bad pointers(%08x, %08x, %08x)", mpeg, esSizeAddr, outSizeAddr);
return -1;
}
u32 sceMpegAvcResourceGetAvcDecTopAddr(u32 mpeg)
{
ERROR_LOG(HLE, "UNIMPL sceMpegAvcResourceGetAvcDecTopAddr(%08x)", mpeg);
// it's just a random address
return 0x12345678;
}
u32 sceMpegAvcResourceFinish(u32 mpeg)
{
DEBUG_LOG(HLE,"sceMpegAvcResourceFinish(%08x)", mpeg);
return 0;
}
u32 sceMpegAvcResourceGetAvcEsBuf(u32 mpeg)
{
ERROR_LOG(HLE, "UNIMPL sceMpegAvcResourceGetAvcEsBuf(%08x)", mpeg);
return 0;
}
u32 sceMpegAvcResourceInit(u32 mpeg)
{
ERROR_LOG(HLE, "UNIMPL sceMpegAvcResourceInit(%08x)", mpeg);
if (mpeg != 1) {
return ERROR_MPEG_INVALID_VALUE;
}
return 0;
}
int sceMpegAvcConvertToYuv420(u32 mpeg, u32 bufferOutput, u32 unknown1, int unknown2)
{
ERROR_LOG(HLE, "UNIMPL sceMpegAvcConvertToYuv420(%08x, %08x, %08x, %08x)", mpeg, bufferOutput, unknown1, unknown2);
return 0;
}
int sceMpegGetUserdataAu(u32 mpeg, u32 streamUid, u32 auAddr, u32 resultAddr)
{
ERROR_LOG(HLE, "UNIMPL sceMpegGetUserdataAu(%08x, %08x, %08x, %08x)", mpeg, streamUid, auAddr, resultAddr);
// TODO: Are these at all right? Seen in Phantasy Star Portable 2.
Memory::Write_U32(0, resultAddr);
Memory::Write_U32(0, resultAddr + 4);
return 0;
}
const HLEFunction sceMpeg[] =
{
{0xe1ce83a7,WrapI_UUUU<sceMpegGetAtracAu>,"sceMpegGetAtracAu"},
{0xfe246728,WrapI_UUUU<sceMpegGetAvcAu>,"sceMpegGetAvcAu"},
{0xd8c5f121,WrapU_UUUUUUU<sceMpegCreate>,"sceMpegCreate"},
{0xf8dcb679,WrapI_UUU<sceMpegQueryAtracEsSize>,"sceMpegQueryAtracEsSize"},
{0xc132e22f,WrapU_V<sceMpegQueryMemSize>,"sceMpegQueryMemSize"},
{0x21ff80e4,WrapI_UUU<sceMpegQueryStreamOffset>,"sceMpegQueryStreamOffset"},
{0x611e9e11,WrapU_UU<sceMpegQueryStreamSize>,"sceMpegQueryStreamSize"},
{0x42560f23,WrapI_UUU<sceMpegRegistStream>,"sceMpegRegistStream"},
{0x591a4aa2,WrapU_UI<sceMpegUnRegistStream>,"sceMpegUnRegistStream"},
{0x707b7629,WrapU_U<sceMpegFlushAllStream>,"sceMpegFlushAllStream"},
{0x500F0429,WrapU_UI<sceMpegFlushStream>,"sceMpegFlushStream"},
{0xa780cf7e,WrapI_U<sceMpegMallocAvcEsBuf>,"sceMpegMallocAvcEsBuf"},
{0xceb870b1,WrapI_UI<sceMpegFreeAvcEsBuf>,"sceMpegFreeAvcEsBuf"},
{0x167afd9e,WrapI_UUU<sceMpegInitAu>,"sceMpegInitAu"},
{0x682a619b,WrapU_V<sceMpegInit>,"sceMpegInit"},
{0x606a4649,WrapI_U<sceMpegDelete>,"sceMpegDelete"},
{0x874624d6,WrapU_V<sceMpegFinish>,"sceMpegFinish"},
{0x800c44df,WrapU_UUUI<sceMpegAtracDecode>,"sceMpegAtracDecode"},
{0x0e3c2e9d,&WrapU_UUUUU<sceMpegAvcDecode>,"sceMpegAvcDecode"},
{0x740fccd1,&WrapU_UUUU<sceMpegAvcDecodeStop>,"sceMpegAvcDecodeStop"},
{0x4571cc64,&WrapU_U<sceMpegAvcDecodeFlush>,"sceMpegAvcDecodeFlush"},
{0x0f6c18d7,&WrapI_UU<sceMpegAvcDecodeDetail>,"sceMpegAvcDecodeDetail"},
{0xa11c7026,WrapI_UU<sceMpegAvcDecodeMode>,"sceMpegAvcDecodeMode"},
{0x37295ed8,WrapU_UUUUUU<sceMpegRingbufferConstruct>,"sceMpegRingbufferConstruct"},
{0x13407f13,WrapU_U<sceMpegRingbufferDestruct>,"sceMpegRingbufferDestruct"},
{0xb240a59e,WrapU_UUU<sceMpegRingbufferPut>,"sceMpegRingbufferPut"},
{0xb5f6dc87,WrapI_U<sceMpegRingbufferAvailableSize>,"sceMpegRingbufferAvailableSize"},
{0xd7a29f46,WrapU_I<sceMpegRingbufferQueryMemSize>,"sceMpegRingbufferQueryMemSize"},
{0x769BEBB6,WrapU_I<sceMpegRingbufferQueryPackNum>,"sceMpegRingbufferQueryPackNum"},
{0x211a057c,WrapI_UUUUU<sceMpegAvcQueryYCbCrSize>,"sceMpegAvcQueryYCbCrSize"},
{0xf0eb1125,WrapI_UUUU<sceMpegAvcDecodeYCbCr>,"sceMpegAvcDecodeYCbCr"},
{0xf2930c9c,WrapU_UUU<sceMpegAvcDecodeStopYCbCr>,"sceMpegAvcDecodeStopYCbCr"},
{0x67179b1b,WrapU_UIIIU<sceMpegAvcInitYCbCr>,"sceMpegAvcInitYCbCr"},
{0x0558B075,WrapU_UUU<sceMpegAvcCopyYCbCr>,"sceMpegAvcCopyYCbCr"},
{0x31bd0272,WrapU_UUUIU<sceMpegAvcCsc>,"sceMpegAvcCsc"},
{0x9DCFB7EA,WrapU_UII<sceMpegChangeGetAuMode>,"sceMpegChangeGetAuMode"},
{0x8C1E027D,WrapU_UIUU<sceMpegGetPcmAu>,"sceMpegGetPcmAu"},
{0xC02CF6B5,WrapI_UUU<sceMpegQueryPcmEsSize>,"sceMpegQueryPcmEsSize"},
{0xC45C99CC,WrapU_UUU<sceMpegQueryUserdataEsSize>,"sceMpegQueryUserdataEsSize"},
{0x234586AE,WrapU_UUI<sceMpegChangeGetAvcAuMode>,"sceMpegChangeGetAvcAuMode"},
{0x63B9536A,WrapU_U<sceMpegAvcResourceGetAvcDecTopAddr>,"sceMpegAvcResourceGetAvcDecTopAddr"},
{0x8160a2fe,WrapU_U<sceMpegAvcResourceFinish>,"sceMpegAvcResourceFinish"},
{0xaf26bb01,WrapU_U<sceMpegAvcResourceGetAvcEsBuf>,"sceMpegAvcResourceGetAvcEsBuf"},
{0xfcbdb5ad,WrapU_U<sceMpegAvcResourceInit>,"sceMpegAvcResourceInit"},
{0xF5E7EA31,WrapI_UUUI<sceMpegAvcConvertToYuv420>,"sceMpegAvcConvertToYuv420"},
{0x01977054,WrapI_UUUU<sceMpegGetUserdataAu>,"sceMpegGetUserdataAu"},
{0x3c37a7a6,0,"sceMpegNextAvcRpAu"},
{0x11f95cf1,0,"sceMpegGetAvcNalAu"},
{0xab0e9556,0,"sceMpegAvcDecodeDetailIndex"},
{0xcf3547a2,0,"sceMpegAvcDecodeDetail2"},
{0x921fcccf,0,"sceMpegGetAvcEsAu"},
};
void Register_sceMpeg()
{
RegisterModule("sceMpeg", ARRAY_SIZE(sceMpeg), sceMpeg);
}