mirror of
https://github.com/hrydgard/ppsspp.git
synced 2024-12-03 19:21:13 +00:00
1743 lines
51 KiB
C++
1743 lines
51 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"
|
|
|
|
static bool useMediaEngine;
|
|
|
|
// 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 int MPEG_MEMSIZE = 0x10000; // 64k.
|
|
|
|
static const int MPEG_AVC_DECODE_SUCCESS = 1; // Internal value.
|
|
static const int MPEG_AVC_DECODE_ERROR_FATAL = -8;
|
|
|
|
static const int atracDecodeDelay = 3000; // Microseconds
|
|
static const int avcDecodeDelay = 5400; // Microseconds
|
|
static const int mpegDecodeErrorDelay = 100; // Delay in Microseconds in case of decode error
|
|
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;
|
|
|
|
static const int MP3_BITRATES[] = {0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320};
|
|
|
|
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 Mp3Context {
|
|
void DoState(PointerWrap &p) {
|
|
p.Do(mp3StreamStart);
|
|
p.Do(mp3StreamEnd);
|
|
p.Do(mp3Buf);
|
|
p.Do(mp3BufSize);
|
|
p.Do(mp3PcmBuf);
|
|
p.Do(mp3BufPendingSize);
|
|
p.Do(mp3PcmBufSize);
|
|
p.Do(mp3InputFileReadPos);
|
|
p.Do(mp3InputBufWritePos);
|
|
p.Do(mp3InputBufSize);
|
|
p.Do(mp3InputFileSize);
|
|
p.Do(mp3DecodedBytes);
|
|
p.Do(mp3LoopNum);
|
|
p.Do(mp3MaxSamples);
|
|
p.Do(mp3Bitrate);
|
|
p.Do(mp3Channels);
|
|
p.Do(mp3SamplingRate);
|
|
p.Do(mp3Version);
|
|
mediaengine->DoState(p);
|
|
p.DoMarker("Mp3Context");
|
|
}
|
|
|
|
int mp3StreamStart;
|
|
int mp3StreamEnd;
|
|
int mp3StreamPosition;
|
|
u32 mp3Buf;
|
|
int mp3BufSize;
|
|
int mp3BufPendingSize;
|
|
u32 mp3PcmBuf;
|
|
int mp3PcmBufSize;
|
|
|
|
int mp3InputFileReadPos;
|
|
int mp3InputBufWritePos;
|
|
int mp3InputBufSize;
|
|
int mp3InputFileSize;
|
|
int mp3DecodedBytes;
|
|
int mp3LoopNum;
|
|
int mp3MaxSamples;
|
|
|
|
int mp3Channels;
|
|
int mp3Bitrate;
|
|
int mp3SamplingRate;
|
|
int mp3Version;
|
|
|
|
MediaEngine *mediaengine;
|
|
};
|
|
|
|
struct StreamInfo {
|
|
int type;
|
|
int num;
|
|
int sid;
|
|
};
|
|
|
|
typedef std::map<u32, StreamInfo> StreamInfoMap;
|
|
|
|
// Internal structure
|
|
struct MpegContext {
|
|
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<StreamInfo>(streamMap);
|
|
mediaengine->DoState(p);
|
|
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 u32 streamIdGen;
|
|
static bool isCurrentMpegAnalyzed;
|
|
static bool fakeMode;
|
|
static int actionPostPut;
|
|
static std::map<u32, MpegContext *> mpegMap;
|
|
static std::map<u32, Mp3Context *> mp3Map;
|
|
// TODO: Remove.
|
|
static u32 lastMpegHandle = 0;
|
|
static u32 lastMp3Handle = 0;
|
|
|
|
MpegContext *getMpegCtx(u32 mpegAddr) {
|
|
u32 mpeg = Memory::Read_U32(mpegAddr);
|
|
|
|
// TODO: Remove.
|
|
if (mpegMap.find(mpeg) == mpegMap.end())
|
|
{
|
|
ERROR_LOG(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];
|
|
}
|
|
|
|
Mp3Context *getMp3Ctx(u32 mp3) {
|
|
if (mp3Map.find(mp3) == mp3Map.end())
|
|
{
|
|
ERROR_LOG(HLE, "Bad mp3 handle %08x - using last one (%08x) instead", mp3, lastMp3Handle);
|
|
mp3 = lastMp3Handle;
|
|
}
|
|
|
|
if (mp3Map.find(mp3) == mp3Map.end())
|
|
return NULL;
|
|
return mp3Map[mp3];
|
|
}
|
|
|
|
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; // set later
|
|
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;
|
|
|
|
//if (!isCurrentMpegAnalyzed) {
|
|
//SceMpegRingBuffer ringbuffer;
|
|
//InitRingbuffer(&ringbuffer, 0, 0, 0, 0, 0);
|
|
// ????
|
|
//Memory::WriteStruct(ctx->mpegRingbufferAddr, &ringbuffer);
|
|
//}
|
|
|
|
ctx->videoFrameCount = 0;
|
|
ctx->audioFrameCount = 0;
|
|
ctx->endOfAudioReached = false;
|
|
ctx->endOfVideoReached = false;
|
|
|
|
if ((ctx->mpegStreamSize > 0) && !ctx->isAnalyzed) {
|
|
ctx->mediaengine->setFakeMode(fakeMode);
|
|
ctx->mediaengine->init(buffer_addr, ctx->mpegStreamSize, ctx->mpegOffset);
|
|
ctx->mediaengine->setVideoDim(ctx->avc.avcDetailFrameWidth, ctx->avc.avcDetailFrameHeight);
|
|
// mysterious?
|
|
//meChannel = new PacketChannel();
|
|
//meChannel.write(buffer_addr, mpegOffset);
|
|
}
|
|
// 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(bool useMediaEngine_) {
|
|
lastMpegHandle = 0;
|
|
streamIdGen = 1;
|
|
fakeMode = !useMediaEngine_;
|
|
isCurrentMpegAnalyzed = false;
|
|
actionPostPut = __KernelRegisterActionType(PostPutAction::Create);
|
|
}
|
|
|
|
void __MpegDoState(PointerWrap &p) {
|
|
p.Do(lastMpegHandle);
|
|
p.Do(streamIdGen);
|
|
p.Do(fakeMode);
|
|
p.Do(isCurrentMpegAnalyzed);
|
|
p.Do(actionPostPut);
|
|
__KernelRestoreActionType(actionPostPut, PostPutAction::Create);
|
|
|
|
int n = (int) mpegMap.size();
|
|
p.Do(n);
|
|
if (p.mode == p.MODE_READ) {
|
|
std::map<u32, MpegContext *>::iterator it, end;
|
|
for (it = mpegMap.begin(), end = mpegMap.end(); it != end; ++it) {
|
|
delete it->second->mediaengine;
|
|
delete it->second;
|
|
}
|
|
mpegMap.clear();
|
|
|
|
for (int i = 0; i < n; ++i) {
|
|
u32 key;
|
|
p.Do(key);
|
|
MpegContext *ctx = new MpegContext;
|
|
ctx->mediaengine = new MediaEngine;
|
|
ctx->DoState(p);
|
|
mpegMap[key] = ctx;
|
|
}
|
|
} else {
|
|
std::map<u32, MpegContext *>::iterator it, end;
|
|
for (it = mpegMap.begin(), end = mpegMap.end(); it != end; ++it) {
|
|
p.Do(it->first);
|
|
it->second->DoState(p);
|
|
}
|
|
}
|
|
|
|
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->mediaengine;
|
|
delete it->second;
|
|
}
|
|
mpegMap.clear();
|
|
}
|
|
|
|
u32 sceMpegInit()
|
|
{
|
|
WARN_LOG(HLE, "sceMpegInit()");
|
|
return 0;
|
|
}
|
|
|
|
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;
|
|
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 = 0;
|
|
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 0;
|
|
}
|
|
|
|
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->mediaengine;
|
|
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");
|
|
return ERROR_MPEG_INVALID_VALUE;
|
|
} else if (ctx->mpegVersion < 0) {
|
|
ERROR_LOG(HLE, "sceMpegQueryStreamOffset: Bad version");
|
|
return ERROR_MPEG_BAD_VERSION;
|
|
} else if ((ctx->mpegOffset & 2047) != 0 || ctx->mpegOffset == 0) {
|
|
ERROR_LOG(HLE, "sceMpegQueryStreamOffset: Bad offset");
|
|
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 temp;
|
|
temp.mediaengine = new MediaEngine();
|
|
|
|
AnalyzeMpeg(bufferAddr, &temp);
|
|
|
|
delete temp.mediaengine;
|
|
|
|
if (temp.mpegMagic != PSMF_MAGIC) {
|
|
ERROR_LOG(HLE, "sceMpegQueryStreamOffset: Bad PSMF magic");
|
|
return ERROR_MPEG_INVALID_VALUE;
|
|
} else if (temp.mpegVersion < 0) {
|
|
ERROR_LOG(HLE, "sceMpegQueryStreamOffset: Bad version");
|
|
return ERROR_MPEG_BAD_VERSION;
|
|
} else if ((temp.mpegOffset & 2047) != 0 || temp.mpegOffset == 0) {
|
|
ERROR_LOG(HLE, "sceMpegQueryStreamOffset: Bad offset");
|
|
return ERROR_MPEG_INVALID_VALUE;
|
|
}
|
|
Memory::Write_U32(temp.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;
|
|
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) {
|
|
// empty!
|
|
return MPEG_AVC_DECODE_ERROR_FATAL;
|
|
}
|
|
|
|
u32 buffer = Memory::Read_U32(bufferAddr);
|
|
u32 init = Memory::Read_U32(initAddr);
|
|
DEBUG_LOG(HLE, "*buffer = %08x, *init = %08x", buffer, init);
|
|
|
|
const int width = std::min((int)frameWidth, 480);
|
|
const int height = ctx->avc.avcDetailFrameHeight;
|
|
|
|
int packetsInRingBuffer = ringbuffer.packets - ringbuffer.packetsFree;
|
|
int processedPackets = ringbuffer.packetsRead - packetsInRingBuffer;
|
|
int processedSize = processedPackets * ringbuffer.packetSize;
|
|
|
|
int packetsConsumed = 3;
|
|
if (ctx->mpegStreamSize > 0 && ctx->mpegLastTimestamp > 0) {
|
|
// Try a better approximation of the packets consumed based on the timestamp
|
|
int processedSizeBasedOnTimestamp = (int) ((((float) avcAu.pts) / ctx->mpegLastTimestamp) * ctx->mpegStreamSize);
|
|
if (processedSizeBasedOnTimestamp < processedSize) {
|
|
packetsConsumed = 0;
|
|
} else {
|
|
packetsConsumed = (processedSizeBasedOnTimestamp - processedSize) / ringbuffer.packetSize;
|
|
if (packetsConsumed > 10) {
|
|
packetsConsumed = 10;
|
|
}
|
|
}
|
|
DEBUG_LOG(HLE, "sceMpegAvcDecode consumed %d %d/%d %d", processedSizeBasedOnTimestamp, processedSize, ctx->mpegStreamSize, packetsConsumed);
|
|
}
|
|
|
|
if (ctx->mediaengine->stepVideo()) {
|
|
ctx->mediaengine->writeVideoImage(buffer, frameWidth, ctx->videoPixelMode);
|
|
packetsConsumed += ctx->mediaengine->readLength() / ringbuffer.packetSize;
|
|
|
|
// The MediaEngine is already consuming all the remaining
|
|
// packets when approaching the end of the video. The PSP
|
|
// is only consuming the last packet when reaching the end,
|
|
// not before.
|
|
// Consuming all the remaining packets?
|
|
if (ringbuffer.packetsFree + packetsConsumed >= ringbuffer.packets) {
|
|
// Having not yet reached the last timestamp?
|
|
if (ctx->mpegLastTimestamp > 0 && avcAu.pts < ctx->mpegLastTimestamp) {
|
|
// Do not yet consume all the remaining packets.
|
|
packetsConsumed = 0;
|
|
}
|
|
}
|
|
ctx->mediaengine->setReadLength(ctx->mediaengine->readLength() - packetsConsumed * ringbuffer.packetSize);
|
|
} else {
|
|
// Consume all remaining packets
|
|
packetsConsumed = ringbuffer.packets - ringbuffer.packetsFree;
|
|
}
|
|
ctx->avc.avcFrameStatus = 1;
|
|
ctx->videoFrameCount++;
|
|
|
|
// Update the ringbuffer with the consumed packets
|
|
if (ringbuffer.packetsFree < ringbuffer.packets && packetsConsumed > 0) {
|
|
ringbuffer.packetsFree = std::min(ringbuffer.packets, ringbuffer.packetsFree + packetsConsumed);
|
|
DEBUG_LOG(HLE, "sceMpegAvcDecode consumed %d packets, remaining %d packets", packetsConsumed, ringbuffer.packets - ringbuffer.packetsFree);
|
|
}
|
|
|
|
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);
|
|
|
|
return 0;
|
|
}
|
|
|
|
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 ;
|
|
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, %d, %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 MPEG_AVC_DECODE_ERROR_FATAL;
|
|
}
|
|
|
|
u32 buffer = Memory::Read_U32(bufferAddr);
|
|
u32 init = Memory::Read_U32(initAddr);
|
|
DEBUG_LOG(HLE, "*buffer = %08x, *init = %08x", buffer, init);
|
|
|
|
int packetsInRingBuffer = ringbuffer.packets - ringbuffer.packetsFree;
|
|
int processedPackets = ringbuffer.packetsRead - packetsInRingBuffer;
|
|
int processedSize = processedPackets * ringbuffer.packetSize;
|
|
|
|
int packetsConsumed = 3;
|
|
if (ctx->mpegStreamSize > 0 && ctx->mpegLastTimestamp > 0) {
|
|
// Try a better approximation of the packets consumed based on the timestamp
|
|
int processedSizeBasedOnTimestamp = (int) ((((float) avcAu.pts) / ctx->mpegLastTimestamp) * ctx->mpegStreamSize);
|
|
if (processedSizeBasedOnTimestamp < processedSize) {
|
|
packetsConsumed = 0;
|
|
} else {
|
|
packetsConsumed = (processedSizeBasedOnTimestamp - processedSize) / ringbuffer.packetSize;
|
|
if (packetsConsumed > 10) {
|
|
packetsConsumed = 10;
|
|
}
|
|
}
|
|
DEBUG_LOG(HLE, "sceMpegAvcDecodeYCbCr consumed %d %d/%d %d", processedSizeBasedOnTimestamp, processedSize, ctx->mpegStreamSize, packetsConsumed);
|
|
}
|
|
|
|
if (ctx->mediaengine->stepVideo()) {
|
|
// TODO: Write it somewhere or buffer it or something?
|
|
packetsConsumed += ctx->mediaengine->readLength() / ringbuffer.packetSize;
|
|
|
|
// Consuming all the remaining packets?
|
|
if (ringbuffer.packetsFree + packetsConsumed >= ringbuffer.packets) {
|
|
// Having not yet reached the last timestamp?
|
|
if (ctx->mpegLastTimestamp > 0 && avcAu.pts < ctx->mpegLastTimestamp) {
|
|
// Do not yet consume all the remaining packets.
|
|
packetsConsumed = 0;
|
|
}
|
|
}
|
|
ctx->mediaengine->setReadLength(ctx->mediaengine->readLength() - packetsConsumed * ringbuffer.packetSize);
|
|
} else {
|
|
// Consume all remaining packets
|
|
packetsConsumed = ringbuffer.packets - ringbuffer.packetsFree;
|
|
}
|
|
ctx->avc.avcFrameStatus = 1;
|
|
ctx->videoFrameCount++;
|
|
|
|
// Update the ringbuffer with the consumed packets
|
|
if (ringbuffer.packetsFree < ringbuffer.packets && packetsConsumed > 0) {
|
|
ringbuffer.packetsFree = std::min(ringbuffer.packets, ringbuffer.packetsFree + packetsConsumed);
|
|
DEBUG_LOG(HLE, "sceMpegAvcDecodeYCbCr consumed %d packets, remaining %d packets", packetsConsumed, ringbuffer.packets - ringbuffer.packetsFree);
|
|
}
|
|
|
|
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);
|
|
|
|
return 0;
|
|
}
|
|
|
|
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 <= 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);
|
|
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 (ctx)
|
|
ctx->mediaengine->feedPacketData(ringbuffer.data, packetsAdded * ringbuffer.packetSize);
|
|
if (packetsAdded > ringbuffer.packetsFree) {
|
|
WARN_LOG(HLE, "sceMpegRingbufferPut clamping packetsAdded old=%i new=%i", packetsAdded, ringbuffer.packetsFree);
|
|
packetsAdded = ringbuffer.packetsFree;
|
|
}
|
|
ringbuffer.packetsRead += packetsAdded;
|
|
ringbuffer.packetsWritten += packetsAdded;
|
|
ringbuffer.packetsFree -= packetsAdded;
|
|
}
|
|
|
|
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);
|
|
if (numPackets < 0) {
|
|
ERROR_LOG(HLE, "sub-zero number of packets put");
|
|
return 0;
|
|
}
|
|
|
|
SceMpegRingBuffer ringbuffer;
|
|
Memory::ReadStruct(ringbufferAddr, &ringbuffer);
|
|
|
|
numPackets = std::min(numPackets, available);
|
|
|
|
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;
|
|
}
|
|
|
|
// Clamp to length of mpeg stream - this seems like a hack as we don't have access to the context here really
|
|
int mpegStreamPackets = (ctx->mpegStreamSize + ringbuffer.packetSize - 1) / ringbuffer.packetSize;
|
|
int remainingPackets = mpegStreamPackets - ringbuffer.packetsRead;
|
|
if (remainingPackets < 0) {
|
|
remainingPackets = 0;
|
|
}
|
|
numPackets = std::min(numPackets, (u32)remainingPackets);
|
|
|
|
// 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);
|
|
u32 args[3] = {(u32)ringbuffer.data, numPackets, (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;
|
|
}
|
|
DEBUG_LOG(HLE, "sceMpegGetAvcAu(%08x, %08x, %08x, %08x)", mpeg, streamId, auAddr, attrAddr);
|
|
|
|
SceMpegRingBuffer mpegRingbuffer;
|
|
Memory::ReadStruct(ctx->mpegRingbufferAddr, &mpegRingbuffer);
|
|
|
|
SceMpegAu sceAu;
|
|
sceAu.read(auAddr);
|
|
|
|
if (mpegRingbuffer.packetsRead == 0) {
|
|
// delayThread(mpegErrorDecodeDelay)
|
|
return PSP_ERROR_MPEG_NO_DATA;
|
|
}
|
|
|
|
if (ctx->streamMap.find(streamId) == ctx->streamMap.end())
|
|
{
|
|
ERROR_LOG(HLE, "sceMpegGetAvcAu - bad stream id %i", streamId);
|
|
return -1;
|
|
}
|
|
|
|
// Wait for audio if too much ahead
|
|
if (ctx->atracRegistered && (sceAu.pts > sceAu.pts + getMaxAheadTimestamp(mpegRingbuffer)))
|
|
{
|
|
ERROR_LOG(HLE, "sceMpegGetAvcAu - video too much ahead");
|
|
return PSP_ERROR_MPEG_NO_DATA;
|
|
}
|
|
|
|
int result = 0;
|
|
|
|
// read the au struct from ram
|
|
// TODO: For now, always checking, since readVideoAu() is stubbed.
|
|
if (!ctx->mediaengine->readVideoAu(&sceAu) || true) {
|
|
// Only return this after the video already ended.
|
|
if (ctx->endOfVideoReached) {
|
|
if (mpegRingbuffer.packetsFree < mpegRingbuffer.packets) {
|
|
mpegRingbuffer.packetsFree = mpegRingbuffer.packets;
|
|
Memory::WriteStruct(ctx->mpegRingbufferAddr, &mpegRingbuffer);
|
|
}
|
|
result = PSP_ERROR_MPEG_NO_DATA;
|
|
}
|
|
if (ctx->mpegLastTimestamp <= 0 || sceAu.pts >= ctx->mpegLastTimestamp) {
|
|
NOTICE_LOG(HLE, "End of video reached");
|
|
ctx->endOfVideoReached = true;
|
|
} else {
|
|
ctx->endOfAudioReached = false;
|
|
}
|
|
}
|
|
|
|
// The avcau struct may have been modified by mediaengine, write it back.
|
|
sceAu.write(auAddr);
|
|
|
|
if (Memory::IsValidAddress(attrAddr)) {
|
|
Memory::Write_U32(1, attrAddr);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
u32 sceMpegFinish()
|
|
{
|
|
ERROR_LOG(HLE, "sceMpegFinish(...)");
|
|
//__MpegFinish();
|
|
return 0;
|
|
}
|
|
|
|
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;
|
|
}
|
|
DEBUG_LOG(HLE, "sceMpegGetAtracAu(%08x, %08x, %08x, %08x)", mpeg, streamId, auAddr, attrAddr);
|
|
|
|
SceMpegRingBuffer mpegRingbuffer;
|
|
Memory::ReadStruct(ctx->mpegRingbufferAddr, &mpegRingbuffer);
|
|
|
|
SceMpegAu sceAu;
|
|
sceAu.read(auAddr);
|
|
|
|
int result = 0;
|
|
|
|
//...
|
|
// TODO: Just faking it.
|
|
sceAu.pts += videoTimestampStep;
|
|
sceAu.write(auAddr);
|
|
|
|
// TODO: And also audio end?
|
|
if (ctx->endOfVideoReached) {
|
|
if (mpegRingbuffer.packetsFree < mpegRingbuffer.packets) {
|
|
mpegRingbuffer.packetsFree = mpegRingbuffer.packets;
|
|
Memory::WriteStruct(ctx->mpegRingbufferAddr, &mpegRingbuffer);
|
|
}
|
|
result = PSP_ERROR_MPEG_NO_DATA;
|
|
}
|
|
|
|
if (Memory::IsValidAddress(attrAddr)) {
|
|
Memory::Write_U32(0, attrAddr);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
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);
|
|
ERROR_LOG(HLE, "UNIMPL sceMpegFlushAllStream(%08x)", mpeg);
|
|
if ( ctx->videoFrameCount > 0 || ctx->audioFrameCount > 0) {
|
|
//__MpegFinish();
|
|
}
|
|
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)
|
|
{
|
|
ERROR_LOG(HLE, "UNIMPL sceMpegAtracDecode(%08x, %08x, %08x, %i)", mpeg, auAddr, bufferAddr, init);
|
|
if (Memory::IsValidAddress(bufferAddr))
|
|
Memory::Memset(bufferAddr, 0, MPEG_ATRAC_ES_OUTPUT_SIZE);
|
|
return 0;
|
|
}
|
|
|
|
// YCbCr -> RGB color space conversion
|
|
u32 sceMpegAvcCsc(u32 mpeg, u32 sourceAddr, u32 rangeAddr, int frameWidth, u32 destAddr)
|
|
{
|
|
ERROR_LOG(HLE, "UNIMPL sceMpegAvcCsc(%08x, %08x, %08x, %i, %08x)", mpeg, sourceAddr, rangeAddr, frameWidth, destAddr);
|
|
return 0;
|
|
}
|
|
|
|
u32 sceMpegRingbufferDestruct(u32 ringbufferAddr)
|
|
{
|
|
DEBUG_LOG(HLE, "sceMpegRingbufferDestruct(%08x)", ringbufferAddr);
|
|
// Don't need to do anything here
|
|
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;
|
|
}
|
|
|
|
|
|
/* MP3 */
|
|
int sceMp3Decode(u32 mp3, u32 outPcmPtr)
|
|
{
|
|
DEBUG_LOG(HLE, "sceMp3Decode(%08x,%08x)", mp3, outPcmPtr);
|
|
|
|
Mp3Context *ctx = getMp3Ctx(mp3);
|
|
if (!ctx)
|
|
{
|
|
ERROR_LOG(HLE, "%s: bad mp3 handle %08x", __FUNCTION__, mp3);
|
|
return -1;
|
|
}
|
|
|
|
// Nothing to decode
|
|
if(ctx->mp3BufPendingSize == 0 || ctx->mp3StreamPosition >= ctx->mp3StreamEnd) {
|
|
return 0;
|
|
}
|
|
|
|
Memory::Memset(ctx->mp3PcmBuf, 0, ctx->mp3PcmBufSize);
|
|
Memory::Write_U32(ctx->mp3PcmBuf, outPcmPtr);
|
|
|
|
// TODO: Actually decode the data
|
|
#ifdef _DEBUG
|
|
char fileName[256];
|
|
sprintf(fileName, "%i.mp3", ctx->mp3StreamPosition);
|
|
|
|
FILE * file = fopen(fileName, "wb");
|
|
if(file) {
|
|
if(!Memory::IsValidAddress(ctx->mp3Buf)) {
|
|
ERROR_LOG(HLE, "sceMp3Decode mp3Buf %08X is not a valid address!", ctx->mp3Buf);
|
|
}
|
|
|
|
u8 * ptr = Memory::GetPointer(ctx->mp3Buf);
|
|
fwrite(ptr, 1, ctx->mp3BufPendingSize, file);
|
|
|
|
fclose(file);
|
|
}
|
|
#endif
|
|
|
|
ctx->mp3StreamPosition += ctx->mp3BufPendingSize;
|
|
if(ctx->mp3StreamPosition > ctx->mp3StreamEnd)
|
|
ctx->mp3StreamPosition = ctx->mp3StreamEnd;
|
|
|
|
// Reset the pending buffer size so the program will know that we need to buffer more data
|
|
ctx->mp3BufPendingSize = (ctx->mp3StreamPosition < ctx->mp3StreamEnd)?-1:0;
|
|
|
|
return ctx->mp3PcmBufSize;
|
|
}
|
|
|
|
int sceMp3ResetPlayPosition(u32 mp3)
|
|
{
|
|
DEBUG_LOG(HLE, "SceMp3ResetPlayPosition(%08x)", mp3);
|
|
|
|
Mp3Context *ctx = getMp3Ctx(mp3);
|
|
if (!ctx)
|
|
{
|
|
ERROR_LOG(HLE, "%s: bad mp3 handle %08x", __FUNCTION__, mp3);
|
|
return -1;
|
|
}
|
|
|
|
ctx->mp3StreamPosition = 0;
|
|
ctx->mp3BufPendingSize = -1;
|
|
return 0;
|
|
}
|
|
|
|
int sceMp3CheckStreamDataNeeded(u32 mp3)
|
|
{
|
|
DEBUG_LOG(HLE, "sceMp3CheckStreamDataNeeded(%08x)", mp3);
|
|
|
|
Mp3Context *ctx = getMp3Ctx(mp3);
|
|
if (!ctx)
|
|
{
|
|
ERROR_LOG(HLE, "%s: bad mp3 handle %08x", __FUNCTION__, mp3);
|
|
return -1;
|
|
}
|
|
|
|
return (ctx->mp3BufPendingSize < 0) && (ctx->mp3StreamPosition < ctx->mp3StreamEnd);
|
|
}
|
|
|
|
u32 sceMp3ReserveMp3Handle(u32 mp3Addr)
|
|
{
|
|
DEBUG_LOG(HLE, "sceMp3ReserveMp3Handle(%08x)", mp3Addr);
|
|
Mp3Context *ctx = new Mp3Context;
|
|
|
|
memset(ctx, 0, sizeof(Mp3Context));
|
|
|
|
ctx->mp3StreamStart = Memory::Read_U64(mp3Addr);
|
|
ctx->mp3StreamEnd = Memory::Read_U64(mp3Addr+8);
|
|
ctx->mp3Buf = Memory::Read_U32(mp3Addr+16);
|
|
ctx->mp3BufSize = Memory::Read_U32(mp3Addr+20);
|
|
ctx->mp3PcmBuf = Memory::Read_U32(mp3Addr+24);
|
|
ctx->mp3PcmBufSize = Memory::Read_U32(mp3Addr+28);
|
|
|
|
ctx->mp3StreamPosition = ctx->mp3StreamStart;
|
|
ctx->mp3BufPendingSize = -1;
|
|
ctx->mp3MaxSamples = ctx->mp3PcmBufSize / 4 ;
|
|
|
|
/*ctx->mp3Channels = 2;
|
|
ctx->mp3Bitrate = 128;
|
|
ctx->mp3SamplingRate = 44100;*/
|
|
|
|
mp3Map[mp3Addr] = ctx;
|
|
return mp3Addr;
|
|
}
|
|
|
|
int sceMp3InitResource()
|
|
{
|
|
WARN_LOG(HLE, "UNIML: sceMp3InitResource");
|
|
// Do nothing here
|
|
return 0;
|
|
}
|
|
|
|
int sceMp3TermResource()
|
|
{
|
|
WARN_LOG(HLE, "UNIML: sceMp3TermResource");
|
|
// Do nothing here
|
|
return 0;
|
|
}
|
|
|
|
int sceMp3Init(u32 mp3)
|
|
{
|
|
DEBUG_LOG(HLE, "sceMp3Init(%08x)", mp3);
|
|
|
|
Mp3Context *ctx = getMp3Ctx(mp3);
|
|
if (!ctx)
|
|
{
|
|
ERROR_LOG(HLE, "%s: bad mp3 handle %08x", __FUNCTION__, mp3);
|
|
return -1;
|
|
}
|
|
|
|
// Read in the header and swap the endian
|
|
int header = Memory::Read_U32(ctx->mp3Buf);
|
|
header = (header >> 24) |
|
|
((header<<8) & 0x00FF0000) |
|
|
((header>>8) & 0x0000FF00) |
|
|
(header << 24);
|
|
|
|
int channels = ((header >> 6) & 0x3);
|
|
if(channels == 0 || channels == 1 || channels == 2)
|
|
ctx->mp3Channels = 2;
|
|
else if(channels == 3)
|
|
ctx->mp3Channels = 1;
|
|
else
|
|
ctx->mp3Channels = 0;
|
|
|
|
// 0 == VBR
|
|
int bitrate = ((header >> 10) & 0x3);
|
|
if(bitrate < sizeof(MP3_BITRATES) / sizeof(MP3_BITRATES[0]))
|
|
ctx->mp3Bitrate = MP3_BITRATES[bitrate];
|
|
else
|
|
ctx->mp3Bitrate = -1;
|
|
|
|
int samplerate = ((header >> 12) & 0x3);
|
|
if (samplerate == 0) {
|
|
ctx->mp3SamplingRate = 44100;
|
|
} else if (samplerate == 1) {
|
|
ctx->mp3SamplingRate = 48000;
|
|
} else if (samplerate == 2) {
|
|
ctx->mp3SamplingRate = 32000;
|
|
} else {
|
|
ctx->mp3SamplingRate = 0;
|
|
}
|
|
|
|
ctx->mp3Version = ((header >> 19) & 0x3);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int sceMp3GetLoopNum(u32 mp3)
|
|
{
|
|
DEBUG_LOG(HLE, "sceMp3GetLoopNum(%08x)", mp3);
|
|
Mp3Context *ctx = getMp3Ctx(mp3);
|
|
if (!ctx)
|
|
{
|
|
ERROR_LOG(HLE, "%s: bad mp3 handle %08x", __FUNCTION__, mp3);
|
|
return -1;
|
|
}
|
|
|
|
return ctx->mp3LoopNum;
|
|
}
|
|
|
|
int sceMp3GetMaxOutputSample(u32 mp3)
|
|
{
|
|
DEBUG_LOG(HLE, "sceMp3GetMaxOutputSample(%08x)", mp3);
|
|
Mp3Context *ctx = getMp3Ctx(mp3);
|
|
if (!ctx)
|
|
{
|
|
ERROR_LOG(HLE, "%s: bad mp3 handle %08x", __FUNCTION__, mp3);
|
|
return -1;
|
|
}
|
|
|
|
return ctx->mp3MaxSamples;
|
|
}
|
|
|
|
int sceMp3NotifyAddStreamData(u32 mp3, int size) {
|
|
DEBUG_LOG(HLE, "sceMp3NotifyAddStreamData(%08X, %i)", mp3, size);
|
|
|
|
Mp3Context *ctx = getMp3Ctx(mp3);
|
|
if (!ctx)
|
|
{
|
|
ERROR_LOG(HLE, "%s: bad mp3 handle %08x", __FUNCTION__, mp3);
|
|
return -1;
|
|
}
|
|
|
|
ctx->mp3BufPendingSize = size;
|
|
|
|
return 0;
|
|
}
|
|
int sceMp3GetSumDecodedSample(u32 mp3) {
|
|
DEBUG_LOG(HLE, "UNIMPL: sceMp3GetSumDecodedSample(%08X)", mp3);
|
|
return 0;
|
|
}
|
|
int sceMp3SetLoopNum(u32 mp3, int loop) {
|
|
DEBUG_LOG(HLE, "sceMp3SetLoopNum(%08X, %i)", mp3, loop);
|
|
|
|
Mp3Context *ctx = getMp3Ctx(mp3);
|
|
if (!ctx)
|
|
{
|
|
ERROR_LOG(HLE, "%s: bad mp3 handle %08x", __FUNCTION__, mp3);
|
|
return -1;
|
|
}
|
|
|
|
ctx->mp3LoopNum = loop;
|
|
|
|
return 0;
|
|
}
|
|
int sceMp3GetMp3ChannelNum(u32 mp3) {
|
|
DEBUG_LOG(HLE, "sceMp3GetMp3ChannelNum(%08X)", mp3);
|
|
|
|
Mp3Context *ctx = getMp3Ctx(mp3);
|
|
if (!ctx)
|
|
{
|
|
ERROR_LOG(HLE, "%s: bad mp3 handle %08x", __FUNCTION__, mp3);
|
|
return -1;
|
|
}
|
|
|
|
return ctx->mp3Channels;
|
|
}
|
|
int sceMp3GetBitRate(u32 mp3) {
|
|
DEBUG_LOG(HLE, "sceMp3GetBitRate(%08X)", mp3);
|
|
|
|
Mp3Context *ctx = getMp3Ctx(mp3);
|
|
if (!ctx)
|
|
{
|
|
ERROR_LOG(HLE, "%s: bad mp3 handle %08x", __FUNCTION__, mp3);
|
|
return -1;
|
|
}
|
|
|
|
return ctx->mp3Bitrate;
|
|
}
|
|
int sceMp3GetSamplingRate(u32 mp3) {
|
|
DEBUG_LOG(HLE, "sceMp3GetSamplingRate(%08X)", mp3);
|
|
|
|
Mp3Context *ctx = getMp3Ctx(mp3);
|
|
if (!ctx)
|
|
{
|
|
ERROR_LOG(HLE, "%s: bad mp3 handle %08x", __FUNCTION__, mp3);
|
|
return -1;
|
|
}
|
|
|
|
return ctx->mp3SamplingRate;
|
|
}
|
|
int sceMp3GetInfoToAddStreamData(u32 mp3, u32 dstPtr, u32 towritePtr, u32 srcposPtr) {
|
|
DEBUG_LOG(HLE, "HACK: sceMp3GetInfoToAddStreamData(%08X, %08X, %08X, %08X)", mp3, dstPtr, towritePtr, srcposPtr);
|
|
|
|
Mp3Context *ctx = getMp3Ctx(mp3);
|
|
if (!ctx)
|
|
{
|
|
ERROR_LOG(HLE, "%s: bad mp3 handle %08x", __FUNCTION__, mp3);
|
|
return -1;
|
|
}
|
|
|
|
if(Memory::IsValidAddress(dstPtr))
|
|
Memory::Write_U32(ctx->mp3Buf, dstPtr);
|
|
if(Memory::IsValidAddress(towritePtr))
|
|
Memory::Write_U32(ctx->mp3BufSize, towritePtr);
|
|
if(Memory::IsValidAddress(srcposPtr))
|
|
Memory::Write_U32(ctx->mp3StreamPosition, srcposPtr);
|
|
|
|
return 0;
|
|
}
|
|
int sceMp3ReleaseMp3Handle(u32 mp3) {
|
|
DEBUG_LOG(HLE, "sceMp3ReleaseMp3Handle(%08X)", mp3);
|
|
|
|
Mp3Context *ctx = getMp3Ctx(mp3);
|
|
if (!ctx)
|
|
{
|
|
ERROR_LOG(HLE, "%s: bad mp3 handle %08x", __FUNCTION__, mp3);
|
|
return -1;
|
|
}
|
|
|
|
mp3Map.erase(mp3Map.find(mp3));
|
|
|
|
delete ctx;
|
|
|
|
return 0;
|
|
}
|
|
|
|
u32 sceMp3EndEntry()
|
|
{
|
|
DEBUG_LOG(HLE, "UNIMPL sceMp3StartEntry(.)");
|
|
return 0;
|
|
}
|
|
|
|
u32 sceMp3StartEntry()
|
|
{
|
|
DEBUG_LOG(HLE, "UNIMPL sceMp3StartEntry(.)");
|
|
return 0;
|
|
}
|
|
|
|
u32 sceMp3GetFrameNum(u32 mp3)
|
|
{
|
|
DEBUG_LOG(HLE, "UNIMPL sceMp3GetFrameNum(%08x)", mp3);
|
|
return 0;
|
|
}
|
|
|
|
u32 sceMp3GetVersion(u32 mp3)
|
|
{
|
|
DEBUG_LOG(HLE, "sceMp3GetVersion(%08x)", mp3);
|
|
Mp3Context *ctx = getMp3Ctx(mp3);
|
|
if (!ctx)
|
|
{
|
|
ERROR_LOG(HLE, "%s: bad mp3 handle %08x", __FUNCTION__, mp3);
|
|
return -1;
|
|
}
|
|
|
|
return ctx->mp3Version;
|
|
}
|
|
|
|
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"},
|
|
};
|
|
|
|
const HLEFunction sceMp3[] =
|
|
{
|
|
{0x07EC321A,WrapU_U<sceMp3ReserveMp3Handle>,"sceMp3ReserveMp3Handle"},
|
|
{0x0DB149F4,WrapI_UI<sceMp3NotifyAddStreamData>,"sceMp3NotifyAddStreamData"},
|
|
{0x2A368661,WrapI_U<sceMp3ResetPlayPosition>,"sceMp3ResetPlayPosition"},
|
|
{0x354D27EA,WrapI_U<sceMp3GetSumDecodedSample>,"sceMp3GetSumDecodedSample"},
|
|
{0x35750070,WrapI_V<sceMp3InitResource>,"sceMp3InitResource"},
|
|
{0x3C2FA058,WrapI_V<sceMp3TermResource>,"sceMp3TermResource"},
|
|
{0x3CEF484F,WrapI_UI<sceMp3SetLoopNum>,"sceMp3SetLoopNum"},
|
|
{0x44E07129,WrapI_U<sceMp3Init>,"sceMp3Init"},
|
|
{0x732B042A,WrapU_V<sceMp3EndEntry>,"sceMp3EndEntry"},
|
|
{0x7F696782,WrapI_U<sceMp3GetMp3ChannelNum>,"sceMp3GetMp3ChannelNum"},
|
|
{0x87677E40,WrapI_U<sceMp3GetBitRate>,"sceMp3GetBitRate"},
|
|
{0x87C263D1,WrapI_U<sceMp3GetMaxOutputSample>,"sceMp3GetMaxOutputSample"},
|
|
{0x8AB81558,WrapU_V<sceMp3StartEntry>,"sceMp3StartEntry"},
|
|
{0x8F450998,WrapI_U<sceMp3GetSamplingRate>,"sceMp3GetSamplingRate"},
|
|
{0xA703FE0F,WrapI_UUUU<sceMp3GetInfoToAddStreamData>,"sceMp3GetInfoToAddStreamData"},
|
|
{0xD021C0FB,WrapI_UU<sceMp3Decode>,"sceMp3Decode"},
|
|
{0xD0A56296,WrapI_U<sceMp3CheckStreamDataNeeded>,"sceMp3CheckStreamDataNeeded"},
|
|
{0xD8F54A51,WrapI_U<sceMp3GetLoopNum>,"sceMp3GetLoopNum"},
|
|
{0xF5478233,WrapI_U<sceMp3ReleaseMp3Handle>,"sceMp3ReleaseMp3Handle"},
|
|
{0xAE6D2027,WrapU_U<sceMp3GetVersion>,"sceMp3GetVersion"},
|
|
{0x3548AEC8,WrapU_U<sceMp3GetFrameNum>,"sceMp3GetFrameNum"},
|
|
};
|
|
|
|
void Register_sceMpeg()
|
|
{
|
|
RegisterModule("sceMpeg", ARRAY_SIZE(sceMpeg), sceMpeg);
|
|
}
|
|
|
|
void Register_sceMp3()
|
|
{
|
|
RegisterModule("sceMp3", ARRAY_SIZE(sceMp3), sceMp3);
|
|
}
|