Merge pull request #8189 from unknownbrackets/atrac-remaining

Improve the remaining frames value calculation
This commit is contained in:
Henrik Rydgård committed 2015-11-08 11:53:18 +01:00
commit b9f703d112
1 file changed
+129 -66
+129 -66
View File
@@ -67,6 +67,8 @@
#define ATRAC_ERROR_SECOND_BUFFER_NOT_NEEDED 0x80630022
#define ATRAC_ERROR_BUFFER_IS_EMPTY 0x80630023
#define ATRAC_ERROR_ALL_DATA_DECODED 0x80630024
#define ATRAC_ERROR_IS_LOW_LEVEL 0x80630031
#define ATRAC_ERROR_IS_FOR_SCESAS 0x80630040
#define ATRAC_ERROR_AA3_INVALID_DATA 0x80631003
#define ATRAC_ERROR_AA3_SIZE_TOO_SMALL 0x80631004
@@ -109,18 +111,22 @@ enum AtracDecodeResult {
};
struct InputBuffer {
// Address of the buffer.
u32 addr;
// Size of data valid in the buffer from the start.
u32 size;
u32 offset;
u32 writableBytes;
u32 neededBytes;
// Total size of the entire file data.
u32 filesize;
// Offset into the file at which the stream data ends.
u32 fileoffset;
};
struct Atrac;
int __AtracSetContext(Atrac *atrac);
void _AtracGenarateContext(Atrac *atrac, SceAtracId *context);
void _AtracGenerateContext(Atrac *atrac, SceAtracId *context);
struct AtracLoopInfo {
int cuePointID;
@@ -131,6 +137,14 @@ struct AtracLoopInfo {
int playCount;
};
#ifndef USE_FFMPEG
struct AVPacket {
uint8_t *data;
int size;
int64_t pos;
};
#endif
struct Atrac {
Atrac() : atracID(-1), data_buf(0), decodePos(0), decodeEnd(0), bufferPos(0),
atracChannels(0),atracOutputChannels(2),
@@ -161,6 +175,7 @@ struct Atrac {
if (data_buf)
delete [] data_buf;
data_buf = 0;
bufferState = ATRAC_STATUS_NO_DATA;
if (atracContext.IsValid())
kernelMemory.Free(atracContext.ptr);
@@ -279,20 +294,24 @@ struct Atrac {
}
int getRemainFrames() const {
// games would like to add atrac data when it wants.
// Do not try to guess when it want to add data.
// Just return current remainFrames.
int remainFrame;
if (first.fileoffset >= first.filesize || currentSample >= endSample)
remainFrame = PSP_ATRAC_ALLDATA_IS_ON_MEMORY;
else {
// guess the remain frames.
int atracSamplesPerFrame = (codecType == PSP_MODE_AT_3_PLUS ? ATRAC3PLUS_MAX_SAMPLES : ATRAC3_MAX_SAMPLES);
remainFrame = ((int)first.size - (int)getFileOffsetBySample(currentSample)) / atracBytesPerFrame - (firstSampleoffset / atracSamplesPerFrame);
if (remainFrame < 0)
remainFrame = 0;
if (bufferState == ATRAC_STATUS_ALL_DATA_LOADED) {
// Meaning, infinite I guess? We've got it all.
return PSP_ATRAC_ALLDATA_IS_ON_MEMORY;
}
return remainFrame;
if (currentSample >= endSample && loopNum == 0) {
if (bufferState == ATRAC_STATUS_STREAMED_WITHOUT_LOOP) {
return PSP_ATRAC_NONLOOP_STREAM_DATA_IS_ON_MEMORY;
} else if (bufferState == ATRAC_STATUS_STREAMED_LOOP_FROM_END || bufferState == ATRAC_STATUS_STREAMED_LOOP_WITH_TRAILER) {
return PSP_ATRAC_LOOP_STREAM_DATA_IS_ON_MEMORY;
}
}
// Since the first frame is shorter by this offset, add to round up at this offset.
const u32 firstOffsetExtra = codecType == PSP_CODEC_AT3PLUS ? 368 : 69;
int atracSamplesPerFrame = (codecType == PSP_MODE_AT_3_PLUS ? ATRAC3PLUS_MAX_SAMPLES : ATRAC3_MAX_SAMPLES);
const int remainingBytes = first.fileoffset - getFileOffsetBySample(currentSample - atracSamplesPerFrame + firstOffsetExtra);
return remainingBytes / atracBytesPerFrame;
}
int atracID;
@@ -338,8 +357,10 @@ struct Atrac {
AVCodecContext *pCodecCtx;
SwrContext *pSwrCtx;
AVFrame *pFrame;
#endif // USE_FFMPEG
AVPacket *packet;
#ifdef USE_FFMPEG
void ReleaseFFMPEGContext() {
// All of these allow null pointers.
av_freep(&pFrame);
@@ -358,9 +379,12 @@ struct Atrac {
delete packet;
packet = nullptr;
}
#endif // USE_FFMPEG
void ForceSeekToSample(int sample) {
#ifdef USE_FFMPEG
avcodec_flush_buffers(pCodecCtx);
#endif
// Discard any pending packet data.
packet->size = 0;
@@ -381,15 +405,15 @@ struct Atrac {
const u32 unalignedSamples = (offsetSamples + sample) % atracSamplesPerFrame;
int seekFrame = sample + offsetSamples - unalignedSamples;
if (sample != currentSample) {
#ifdef USE_FFMPEG
if (sample != currentSample && pCodecCtx != nullptr) {
avcodec_flush_buffers(pCodecCtx);
}
currentSample = sample;
if (sample == 0) {
if (sample == 0 && pCodecCtx != nullptr) {
// Prefill the decode buffer with packets before the first sample offset.
avcodec_flush_buffers(pCodecCtx);
u32 off = getFileOffsetBySample(currentSample);
u32 off = getFileOffsetBySample(sample);
for (u32 pos = dataOff; pos < off; pos += atracBytesPerFrame) {
av_init_packet(packet);
packet->data = data_buf + pos;
@@ -400,12 +424,17 @@ struct Atrac {
DecodePacket();
}
}
#endif // USE_FFMPEG
currentSample = sample;
}
bool FillPacket() {
u32 off = getFileOffsetBySample(currentSample);
if (off < first.filesize) {
#ifdef USE_FFMPEG
av_init_packet(packet);
#endif // USE_FFMPEG
packet->data = data_buf + off;
packet->size = atracBytesPerFrame;
packet->pos = off;
@@ -419,7 +448,9 @@ struct Atrac {
}
bool FillLowLevelPacket() {
#ifdef USE_FFMPEG
av_init_packet(packet);
#endif // USE_FFMPEG
if (bufferPos < (u32)dataOff) {
bufferPos = dataOff;
}
@@ -432,6 +463,11 @@ struct Atrac {
}
AtracDecodeResult DecodePacket() {
#ifdef USE_FFMPEG
if (pCodecCtx == nullptr) {
return ATDECODE_FAILED;
}
int got_frame = 0;
int bytes_read = avcodec_decode_audio4(pCodecCtx, pFrame, &got_frame, packet);
av_free_packet(packet);
@@ -447,8 +483,10 @@ struct Atrac {
}
return got_frame ? ATDECODE_GOTFRAME : ATDECODE_FEEDME;
}
#else
return ATDECODE_BADFRAME;
#endif // USE_FFMPEG
}
private:
void AnalyzeReset();
@@ -519,7 +557,15 @@ static Atrac *getAtrac(int atracID) {
if (atracID < 0 || atracID >= PSP_NUM_ATRAC_IDS) {
return NULL;
}
return atracIDs[atracID];
Atrac *atrac = atracIDs[atracID];
if (atrac && atrac->atracContext.IsValid()) {
// Read in any changes from the game to the context.
// TODO: Might be better to just always track in RAM.
atrac->bufferState = atrac->atracContext->info.state;
}
return atrac;
}
static int createAtrac(Atrac *atrac) {
@@ -868,7 +914,7 @@ u32 _AtracAddStreamData(int atracID, u32 bufPtr, u32 bytesToAdd) {
int addbytes = std::min(bytesToAdd, atrac->first.filesize - atrac->first.fileoffset);
Memory::Memcpy(atrac->data_buf + atrac->first.fileoffset, bufPtr, addbytes);
atrac->first.size += bytesToAdd;
if (atrac->first.size > atrac->first.filesize) {
if (atrac->first.size >= atrac->first.filesize) {
atrac->first.size = atrac->first.filesize;
if (atrac->bufferState == ATRAC_STATUS_HALFWAY_BUFFER)
atrac->bufferState = ATRAC_STATUS_ALL_DATA_LOADED;
@@ -877,7 +923,7 @@ u32 _AtracAddStreamData(int atracID, u32 bufPtr, u32 bytesToAdd) {
atrac->first.writableBytes = 0;
if (atrac->atracContext.IsValid()) {
// refresh atracContext
_AtracGenarateContext(atrac, atrac->atracContext);
_AtracGenerateContext(atrac, atrac->atracContext);
}
return 0;
}
@@ -910,10 +956,13 @@ static u32 sceAtracAddStreamData(int atracID, u32 bytesToAdd) {
atrac->first.fileoffset += addbytes;
}
atrac->first.size += bytesToAdd;
if (atrac->first.size > atrac->first.filesize) {
if (atrac->first.size >= atrac->first.filesize) {
atrac->first.size = atrac->first.filesize;
if (atrac->bufferState == ATRAC_STATUS_HALFWAY_BUFFER)
atrac->bufferState = ATRAC_STATUS_ALL_DATA_LOADED;
if (atrac->atracContext.IsValid()) {
_AtracGenerateContext(atrac, atrac->atracContext);
}
}
atrac->first.writableBytes -= bytesToAdd;
atrac->first.offset += bytesToAdd;
@@ -954,8 +1003,7 @@ u32 _AtracDecodeData(int atracID, u8 *outbuf, u32 outbufPtr, u32 *SamplesNum, u3
skipSamples = unalignedSamples;
}
#ifdef USE_FFMPEG
if (!atrac->failedDecode && (atrac->codecType == PSP_MODE_AT_3 || atrac->codecType == PSP_MODE_AT_3_PLUS) && atrac->pCodecCtx) {
if (!atrac->failedDecode && (atrac->codecType == PSP_MODE_AT_3 || atrac->codecType == PSP_MODE_AT_3_PLUS)) {
atrac->SeekToSample(atrac->currentSample);
AtracDecodeResult res = ATDECODE_FEEDME;
@@ -969,6 +1017,7 @@ u32 _AtracDecodeData(int atracID, u8 *outbuf, u32 outbufPtr, u32 *SamplesNum, u3
}
if (res == ATDECODE_GOTFRAME) {
#ifdef USE_FFMPEG
// got a frame
// Use a small buffer and keep overwriting it with file data constantly
atrac->first.writableBytes += atrac->atracBytesPerFrame;
@@ -1006,6 +1055,7 @@ u32 _AtracDecodeData(int atracID, u8 *outbuf, u32 outbufPtr, u32 *SamplesNum, u3
ERROR_LOG(ME, "swr_convert: Error while converting %d", avret);
}
}
#endif // USE_FFMPEG
}
if (res == ATDECODE_GOTFRAME || res == ATDECODE_BADFRAME) {
// We only want one frame per call, let's continue the next time.
@@ -1024,7 +1074,6 @@ u32 _AtracDecodeData(int atracID, u8 *outbuf, u32 outbufPtr, u32 *SamplesNum, u3
}
}
}
#endif // USE_FFMPEG
*SamplesNum = numSamples;
// update current sample and decodePos
@@ -1048,7 +1097,7 @@ u32 _AtracDecodeData(int atracID, u8 *outbuf, u32 outbufPtr, u32 *SamplesNum, u3
}
if (atrac->atracContext.IsValid()) {
// refresh atracContext
_AtracGenarateContext(atrac, atrac->atracContext);
_AtracGenerateContext(atrac, atrac->atracContext);
}
}
@@ -1139,6 +1188,8 @@ static void AtracGetResetBufferInfo(Atrac *atrac, AtracResetBufferInfo *bufferIn
bufferInfo->second.filePos = atrac->second.fileoffset;
}
// Obtains information about what needs to be in the buffer to seek (or "reset")
// Generally called by games right before calling sceAtracResetPlayPosition().
static u32 sceAtracGetBufferInfoForResetting(int atracID, int sample, u32 bufferInfoAddr) {
auto bufferInfo = PSPPointer<AtracResetBufferInfo>::Create(bufferInfoAddr);
@@ -1320,24 +1371,29 @@ static u32 sceAtracGetNextSample(int atracID, u32 outNAddr) {
return 0;
}
// Obtains the number of frames remaining in the buffer which can be decoded.
// When no more data would be needed, this returns a negative number.
static u32 sceAtracGetRemainFrame(int atracID, u32 remainAddr) {
auto remainingFrames = PSPPointer<u32>::Create(remainAddr);
Atrac *atrac = getAtrac(atracID);
if (!atrac) {
ERROR_LOG(ME, "sceAtracGetRemainFrame(%i, %08x): bad atrac ID", atracID, remainAddr);
return ATRAC_ERROR_BAD_ATRACID;
} else if (!atrac->data_buf) {
ERROR_LOG(ME, "sceAtracGetRemainFrame(%i, %08x): no data", atracID, remainAddr);
return ATRAC_ERROR_NO_DATA;
return hleLogError(ME, ATRAC_ERROR_BAD_ATRACID, "bad atrac ID");
} else if (atrac->bufferState == ATRAC_STATUS_NO_DATA) {
return hleLogError(ME, ATRAC_ERROR_NO_DATA, "no data");
} else if (atrac->bufferState == ATRAC_STATUS_LOW_LEVEL) {
return hleLogError(ME, ATRAC_ERROR_IS_LOW_LEVEL, "cannot use for low level stream");
} else if (atrac->bufferState == ATRAC_STATUS_FOR_SCESAS) {
return hleLogError(ME, ATRAC_ERROR_IS_FOR_SCESAS, "cannot use for SAS stream");
} else if (!remainingFrames.IsValid()) {
// Would crash.
return hleReportError(ME, SCE_KERNEL_ERROR_ILLEGAL_ADDR, "invalid remainingFrames pointer");
} else {
if (Memory::IsValidAddress(remainAddr)) {
Memory::Write_U32(atrac->getRemainFrames(), remainAddr);
DEBUG_LOG(ME, "sceAtracGetRemainFrame(%i, %08x[%d])", atracID, remainAddr, atrac->getRemainFrames());
}
else
DEBUG_LOG_REPORT(ME, "sceAtracGetRemainFrame(%i, %08x[%d]) invalid address", atracID, remainAddr, atrac->getRemainFrames());
// Let sceAtracGetStreamDataInfo() know to set the full filled buffer .
atrac->resetBuffer = true;
*remainingFrames = atrac->getRemainFrames();
// Let sceAtracGetStreamDataInfo() know to set the full filled buffer.
atrac->resetBuffer = true;
return hleLogSuccessI(ME, 0);
}
return 0;
}
@@ -1437,6 +1493,9 @@ static u32 sceAtracReleaseAtracID(int atracID) {
return deleteAtrac(atracID);
}
// This is called when a game wants to seek (or "reset") to a specific position in the audio data.
// Normally, sceAtracGetBufferInfoForResetting() is called to determine how to buffer.
// The game must add sufficient packets to the buffer in order to complete the seek.
static u32 sceAtracResetPlayPosition(int atracID, int sample, int bytesWrittenFirstBuf, int bytesWrittenSecondBuf) {
Atrac *atrac = getAtrac(atracID);
if (!atrac) {
@@ -1493,14 +1552,13 @@ static u32 sceAtracResetPlayPosition(int atracID, int sample, int bytesWrittenFi
atrac->first.writableBytes = bufferInfo.first.writableBytes - bytesWrittenFirstBuf;
atrac->first.offset = bytesWrittenFirstBuf;
}
#ifdef USE_FFMPEG
if ((atrac->codecType == PSP_MODE_AT_3 || atrac->codecType == PSP_MODE_AT_3_PLUS) && atrac->pCodecCtx) {
if (atrac->codecType == PSP_MODE_AT_3 || atrac->codecType == PSP_MODE_AT_3_PLUS) {
atrac->SeekToSample(sample);
} else
#endif // USE_FFMPEG
{
atrac->currentSample = sample;
atrac->decodePos = atrac->getDecodePosBySample(sample);
}
if (atrac->atracContext.IsValid()) {
_AtracGenerateContext(atrac, atrac->atracContext);
}
return hleDelayResult(hleLogSuccessInfoI(ME, 0), "reset play pos", 3000);
@@ -1638,12 +1696,10 @@ static int _AtracSetData(Atrac *atrac, u32 buffer, u32 bufferSize) {
WARN_LOG(ME, "This is an atrac3 stereo audio");
}
#ifdef USE_FFMPEG
atrac->data_buf = new u8[atrac->first.filesize];
u32 copybytes = std::min(bufferSize, atrac->first.filesize);
Memory::Memcpy(atrac->data_buf, buffer, copybytes);
return __AtracSetContext(atrac);
#endif // USE_FFMPEG
} else if (atrac->codecType == PSP_MODE_AT_3_PLUS) {
if (atrac->atracChannels == 1) {
@@ -1655,6 +1711,9 @@ static int _AtracSetData(Atrac *atrac, u32 buffer, u32 bufferSize) {
u32 copybytes = std::min(bufferSize, atrac->first.filesize);
Memory::Memcpy(atrac->data_buf, buffer, copybytes);
return __AtracSetContext(atrac);
} else {
// Should not get here, but just in case, force it.
atrac->bufferState = ATRAC_STATUS_NO_DATA;
}
return 0;
@@ -2052,7 +2111,7 @@ int _AtracGetIDByContext(u32 contextAddr) {
return atracID;
}
void _AtracGenarateContext(Atrac *atrac, SceAtracId *context) {
void _AtracGenerateContext(Atrac *atrac, SceAtracId *context) {
context->info.buffer = atrac->first.addr;
context->info.bufferByte = atrac->atracBufSize;
context->info.secondBuffer = atrac->second.addr;
@@ -2061,15 +2120,11 @@ void _AtracGenarateContext(Atrac *atrac, SceAtracId *context) {
context->info.loopNum = atrac->loopNum;
context->info.loopStart = atrac->loopStartSample > 0 ? atrac->loopStartSample : 0;
context->info.loopEnd = atrac->loopEndSample > 0 ? atrac->loopEndSample : 0;
if (context->info.endSample > 0) {
// do not change info.state if this was not called at first time
// In Sol Trigger, it would set info.state = 0x10 outside
// TODO: Should we just keep this in PSP ram then, or something?
} else if (!atrac->data_buf) {
context->info.state = ATRAC_STATUS_NO_DATA;
} else {
context->info.state = atrac->bufferState;
}
// Note that we read in the state when loading the atrac object, so it's safe
// to update it back here all the time. Some games, like Sol Trigger, change it.
// TODO: Should we just keep this in PSP ram then, or something?
context->info.state = atrac->bufferState;
if (atrac->firstSampleoffset != 0) {
const u32 firstOffsetExtra = atrac->codecType == PSP_CODEC_AT3PLUS ? 368 : 69;
context->info.samplesPerChan = atrac->firstSampleoffset + firstOffsetExtra;
@@ -2108,7 +2163,7 @@ static int _sceAtracGetContextAddress(int atracID) {
else
WARN_LOG(ME, "%08x=_sceAtracGetContextAddress(%i)", atrac->atracContext.ptr, atracID);
if (atrac->atracContext.IsValid())
_AtracGenarateContext(atrac, atrac->atracContext);
_AtracGenerateContext(atrac, atrac->atracContext);
return atrac->atracContext.ptr;
}
@@ -2183,7 +2238,7 @@ static int sceAtracLowLevelInitDecoder(int atracID, u32 paramsAddr) {
atrac->CleanStuff();
INFO_LOG(ME, "Channels: %i outputChannels: %i bytesperFrame: %x",
atrac->atracChannels, atrac->atracOutputChannels, atrac->atracBytesPerFrame);
#ifdef USE_FFMPEG
if (atrac->codecType == PSP_MODE_AT_3) {
if (atrac->atracChannels == 1) {
WARN_LOG(ME, "This is an atrac3 mono audio (low level)");
@@ -2208,6 +2263,10 @@ static int sceAtracLowLevelInitDecoder(int atracID, u32 paramsAddr) {
atrac->currentSample = 0;
// TODO: Check failure?
__AtracSetContext(atrac);
if (atrac->atracContext.IsValid()) {
_AtracGenerateContext(atrac, atrac->atracContext);
}
return 0;
}
@@ -2231,9 +2290,12 @@ static int sceAtracLowLevelInitDecoder(int atracID, u32 paramsAddr) {
memcpy(atrac->data_buf, at3plusHeader, headersize);
atrac->currentSample = 0;
__AtracSetContext(atrac);
if (atrac->atracContext.IsValid()) {
_AtracGenerateContext(atrac, atrac->atracContext);
}
return 0;
}
#endif // USE_FFMPEG
}
return 0;
}
@@ -2246,8 +2308,8 @@ static int sceAtracLowLevelDecode(int atracID, u32 sourceAddr, u32 sourceBytesCo
}
DEBUG_LOG(ME, "UNIMPL sceAtracLowLevelDecode(%i, %08x, %08x, %08x, %08x)", atracID, sourceAddr, sourceBytesConsumedAddr, samplesAddr, sampleBytesAddr);
#ifdef USE_FFMPEG
if (atrac && atrac->pCodecCtx && Memory::IsValidAddress(sourceAddr) && Memory::IsValidAddress(sourceBytesConsumedAddr) &&
if (atrac && Memory::IsValidAddress(sourceAddr) && Memory::IsValidAddress(sourceBytesConsumedAddr) &&
Memory::IsValidAddress(samplesAddr) && Memory::IsValidAddress(sampleBytesAddr)) {
u32 sourcebytes = atrac->first.writableBytes;
if (sourcebytes > 0) {
@@ -2270,6 +2332,7 @@ static int sceAtracLowLevelDecode(int atracID, u32 sourceAddr, u32 sourceBytesCo
}
if (res == ATDECODE_GOTFRAME) {
#ifdef USE_FFMPEG
// got a frame
u8 *out = Memory::GetPointer(samplesAddr);
numSamples = atrac->pFrame->nb_samples;
@@ -2280,6 +2343,7 @@ static int sceAtracLowLevelDecode(int atracID, u32 sourceAddr, u32 sourceBytesCo
if (avret < 0) {
ERROR_LOG(ME, "swr_convert: Error while converting %d", avret);
}
#endif // USE_FFMPEG
break;
} else if (res == ATDECODE_BADFRAME) {
break;
@@ -2302,7 +2366,6 @@ static int sceAtracLowLevelDecode(int atracID, u32 sourceAddr, u32 sourceBytesCo
Memory::Write_U32(atrac->first.writableBytes, sourceBytesConsumedAddr);
return hleDelayResult(0, "low level atrac decode data", atracDecodeDelay);
}
#endif // USE_FFMPEG
return 0;
}
@@ -2346,7 +2409,7 @@ const HLEFunction sceAtrac3plus[] = {
{0XD6A5F2F7, &WrapU_IU<sceAtracGetMaxSample>, "sceAtracGetMaxSample", 'x', "ix" },
{0XE23E3A35, &WrapU_IU<sceAtracGetNextDecodePosition>, "sceAtracGetNextDecodePosition", 'x', "ix" },
{0X36FAABFB, &WrapU_IU<sceAtracGetNextSample>, "sceAtracGetNextSample", 'x', "ix" },
{0X9AE849A7, &WrapU_IU<sceAtracGetRemainFrame>, "sceAtracGetRemainFrame", 'x', "ix" },
{0X9AE849A7, &WrapU_IU<sceAtracGetRemainFrame>, "sceAtracGetRemainFrame", 'x', "ip" },
{0X83E85EA0, &WrapU_IUU<sceAtracGetSecondBufferInfo>, "sceAtracGetSecondBufferInfo", 'x', "ixx" },
{0XA2BBA8BE, &WrapU_IUUU<sceAtracGetSoundSample>, "sceAtracGetSoundSample", 'x', "ixxx" },
{0X5D268707, &WrapU_IUUU<sceAtracGetStreamDataInfo>, "sceAtracGetStreamDataInfo", 'x', "ixxx" },