mirror of
https://github.com/hrydgard/ppsspp.git
synced 2025-04-02 11:01:50 -04:00
Merge pull request #8011 from unknownbrackets/atrac-demux
Manually read packets in sceAtrac, skipping ffmpeg
This commit is contained in:
commit
e632b32be2
7 changed files with 163 additions and 332 deletions
|
@ -42,5 +42,4 @@ void Compatibility::Clear() {
|
|||
void Compatibility::LoadIniSection(IniFile &iniFile, std::string section) {
|
||||
iniFile.Get(section.c_str(), "NoDepthRounding", &flags_.NoDepthRounding, flags_.NoDepthRounding);
|
||||
iniFile.Get(section.c_str(), "PixelDepthRounding", &flags_.PixelDepthRounding, flags_.PixelDepthRounding);
|
||||
iniFile.Get(section.c_str(), "GTAMusicFix", &flags_.GTAMusicFix, flags_.GTAMusicFix);
|
||||
}
|
||||
|
|
|
@ -47,7 +47,6 @@
|
|||
struct CompatFlags {
|
||||
bool NoDepthRounding;
|
||||
bool PixelDepthRounding;
|
||||
bool GTAMusicFix;
|
||||
};
|
||||
|
||||
class IniFile;
|
||||
|
|
|
@ -17,16 +17,13 @@
|
|||
|
||||
#include <algorithm>
|
||||
|
||||
#include "Core/CoreParameter.h"
|
||||
#include "Core/HLE/HLE.h"
|
||||
#include "Core/HLE/FunctionWrappers.h"
|
||||
#include "Core/MIPS/MIPS.h"
|
||||
#include "Core/CoreTiming.h"
|
||||
#include "Core/Compatibility.h"
|
||||
#include "Core/MemMapHelpers.h"
|
||||
#include "Core/Reporting.h"
|
||||
#include "Core/Config.h"
|
||||
#include "Core/System.h"
|
||||
#include "Core/Debugger/Breakpoints.h"
|
||||
#include "Core/HW/MediaEngine.h"
|
||||
#include "Core/HW/BufferQueue.h"
|
||||
|
@ -105,6 +102,7 @@ enum AtracDecodeResult {
|
|||
ATDECODE_FAILED = -1,
|
||||
ATDECODE_FEEDME = 0,
|
||||
ATDECODE_GOTFRAME = 1,
|
||||
ATDECODE_BADFRAME = 2,
|
||||
};
|
||||
|
||||
struct InputBuffer {
|
||||
|
@ -133,20 +131,17 @@ struct AtracLoopInfo {
|
|||
struct Atrac {
|
||||
Atrac() : atracID(-1), data_buf(0), decodePos(0), decodeEnd(0), bufferPos(0),
|
||||
atracChannels(0),atracOutputChannels(2),
|
||||
atracBitrate(64), atracBytesPerFrame(0), atracBufSize(0),
|
||||
atracBitrate(64), atracBytesPerFrame(0), atracBufSize(0), jointStereo(0),
|
||||
currentSample(0), endSample(0), firstSampleoffset(0), dataOff(0),
|
||||
loopinfoNum(0), loopStartSample(-1), loopEndSample(-1), loopNum(0),
|
||||
failedDecode(false), resetBuffer(false), codecType(0) {
|
||||
memset(&first, 0, sizeof(first));
|
||||
memset(&second, 0, sizeof(second));
|
||||
#ifdef USE_FFMPEG
|
||||
pFormatCtx = nullptr;
|
||||
pAVIOCtx = nullptr;
|
||||
pCodecCtx = nullptr;
|
||||
pSwrCtx = nullptr;
|
||||
pFrame = nullptr;
|
||||
packet = nullptr;
|
||||
audio_stream_index = 0;
|
||||
#endif // USE_FFMPEG
|
||||
atracContext = 0;
|
||||
}
|
||||
|
@ -169,12 +164,15 @@ struct Atrac {
|
|||
}
|
||||
|
||||
void DoState(PointerWrap &p) {
|
||||
auto s = p.Section("Atrac", 1, 4);
|
||||
auto s = p.Section("Atrac", 1, 5);
|
||||
if (!s)
|
||||
return;
|
||||
|
||||
p.Do(atracChannels);
|
||||
p.Do(atracOutputChannels);
|
||||
if (s >= 5) {
|
||||
p.Do(jointStereo);
|
||||
}
|
||||
|
||||
p.Do(atracID);
|
||||
p.Do(first);
|
||||
|
@ -190,7 +188,7 @@ struct Atrac {
|
|||
dataOff = firstSampleoffset;
|
||||
}
|
||||
|
||||
u32 has_data_buf = data_buf != NULL;
|
||||
u32 has_data_buf = data_buf != nullptr;
|
||||
p.Do(has_data_buf);
|
||||
if (has_data_buf) {
|
||||
if (p.mode == p.MODE_READ) {
|
||||
|
@ -200,9 +198,6 @@ struct Atrac {
|
|||
}
|
||||
p.DoArray(data_buf, first.filesize);
|
||||
}
|
||||
if (p.mode == p.MODE_READ && data_buf != NULL) {
|
||||
__AtracSetContext(this);
|
||||
}
|
||||
p.Do(second);
|
||||
|
||||
p.Do(decodePos);
|
||||
|
@ -224,6 +219,11 @@ struct Atrac {
|
|||
p.Do(loopNum);
|
||||
|
||||
p.Do(atracContext);
|
||||
|
||||
// Make sure to do this late; it depends on things like atracBytesPerFrame.
|
||||
if (p.mode == p.MODE_READ && data_buf != nullptr) {
|
||||
__AtracSetContext(this);
|
||||
}
|
||||
|
||||
if (s >= 2)
|
||||
p.Do(resetBuffer);
|
||||
|
@ -237,6 +237,12 @@ struct Atrac {
|
|||
return (u32)(firstSampleoffset + sample / atracSamplesPerFrame * atracBytesPerFrame );
|
||||
}
|
||||
|
||||
u32 getFileOffsetBySample(int sample) const {
|
||||
int atracSamplesPerFrame = (codecType == PSP_MODE_AT_3_PLUS ? ATRAC3PLUS_MAX_SAMPLES : ATRAC3_MAX_SAMPLES);
|
||||
// This matches where ffmpeg was getting the packets, but it's not clear why the first atracBytesPerFrame is there...
|
||||
return (u32)(dataOff + atracBytesPerFrame + (sample + atracSamplesPerFrame - 1) / atracSamplesPerFrame * atracBytesPerFrame);
|
||||
}
|
||||
|
||||
int getRemainFrames() const {
|
||||
// games would like to add atrac data when it wants.
|
||||
// Do not try to guess when it want to add data.
|
||||
|
@ -259,6 +265,7 @@ struct Atrac {
|
|||
|
||||
u32 decodePos;
|
||||
u32 decodeEnd;
|
||||
// Used only by low-level decoding.
|
||||
u32 bufferPos;
|
||||
|
||||
u16 atracChannels;
|
||||
|
@ -266,6 +273,7 @@ struct Atrac {
|
|||
u32 atracBitrate;
|
||||
u16 atracBytesPerFrame;
|
||||
u32 atracBufSize;
|
||||
int jointStereo;
|
||||
|
||||
int currentSample;
|
||||
int endSample;
|
||||
|
@ -291,41 +299,31 @@ struct Atrac {
|
|||
PSPPointer<SceAtracId> atracContext;
|
||||
|
||||
#ifdef USE_FFMPEG
|
||||
AVFormatContext *pFormatCtx;
|
||||
AVIOContext *pAVIOCtx;
|
||||
AVCodecContext *pCodecCtx;
|
||||
SwrContext *pSwrCtx;
|
||||
AVFrame *pFrame;
|
||||
AVPacket *packet;
|
||||
int audio_stream_index;
|
||||
|
||||
void ReleaseFFMPEGContext() {
|
||||
if (pFrame)
|
||||
av_free(pFrame);
|
||||
if (pAVIOCtx && pAVIOCtx->buffer)
|
||||
av_free(pAVIOCtx->buffer);
|
||||
if (pAVIOCtx)
|
||||
av_free(pAVIOCtx);
|
||||
if (pSwrCtx)
|
||||
swr_free(&pSwrCtx);
|
||||
if (pCodecCtx)
|
||||
avcodec_close(pCodecCtx);
|
||||
if (pFormatCtx)
|
||||
avformat_close_input(&pFormatCtx);
|
||||
if (packet)
|
||||
av_free_packet(packet);
|
||||
// All of these allow null pointers.
|
||||
av_freep(&pFrame);
|
||||
swr_free(&pSwrCtx);
|
||||
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(55, 52, 0)
|
||||
// If necessary, extradata is automatically freed.
|
||||
avcodec_free_context(&pCodecCtx);
|
||||
#else
|
||||
// Future versions may add other things to free, but avcodec_free_context didn't exist yet here.
|
||||
avcodec_close(pCodecCtx);
|
||||
av_freep(&pCodecCtx->extradata);
|
||||
av_freep(&pCodecCtx->subtitle_header);
|
||||
av_freep(&pCodecCtx);
|
||||
#endif
|
||||
av_free_packet(packet);
|
||||
delete packet;
|
||||
pFormatCtx = nullptr;
|
||||
pAVIOCtx = nullptr;
|
||||
pCodecCtx = nullptr;
|
||||
pSwrCtx = nullptr;
|
||||
pFrame = nullptr;
|
||||
packet = nullptr;
|
||||
}
|
||||
|
||||
void ForceSeekToSample(int sample) {
|
||||
av_seek_frame(pFormatCtx, audio_stream_index, sample + 0x200000, 0);
|
||||
av_seek_frame(pFormatCtx, audio_stream_index, sample, 0);
|
||||
avcodec_flush_buffers(pCodecCtx);
|
||||
|
||||
// Discard any pending packet data.
|
||||
|
@ -335,12 +333,6 @@ struct Atrac {
|
|||
}
|
||||
|
||||
void SeekToSample(int sample) {
|
||||
if (PSP_CoreParameter().compat.flags().GTAMusicFix) {
|
||||
s64 seek_pos = (s64)sample;
|
||||
av_seek_frame(pFormatCtx, audio_stream_index, seek_pos, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
const u32 atracSamplesPerFrame = (codecType == PSP_MODE_AT_3_PLUS ? ATRAC3PLUS_MAX_SAMPLES : ATRAC3_MAX_SAMPLES);
|
||||
|
||||
// Discard any pending packet data.
|
||||
|
@ -354,97 +346,55 @@ struct Atrac {
|
|||
int seekFrame = sample + offsetSamples - unalignedSamples;
|
||||
|
||||
if (sample != currentSample) {
|
||||
// "Seeking" by reading frames seems to work much better.
|
||||
av_seek_frame(pFormatCtx, audio_stream_index, 0, AVSEEK_FLAG_BACKWARD);
|
||||
avcodec_flush_buffers(pCodecCtx);
|
||||
|
||||
for (int i = 0; i < seekFrame; i += atracSamplesPerFrame) {
|
||||
while (FillPacket() && DecodePacket() == ATDECODE_FEEDME) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// For some reason, if we skip seeking, we get the wrong amount of data.
|
||||
// (even without flushing the packet...)
|
||||
av_seek_frame(pFormatCtx, audio_stream_index, seekFrame, 0);
|
||||
avcodec_flush_buffers(pCodecCtx);
|
||||
}
|
||||
currentSample = sample;
|
||||
}
|
||||
|
||||
bool FillPacket() {
|
||||
if (packet->size > 0) {
|
||||
u32 off = getFileOffsetBySample(currentSample);
|
||||
if (off < first.filesize) {
|
||||
av_init_packet(packet);
|
||||
packet->data = data_buf + off;
|
||||
packet->size = atracBytesPerFrame;
|
||||
packet->pos = off;
|
||||
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
do {
|
||||
// This is double-free safe, so we just call it before each read and at the end.
|
||||
av_free_packet(packet);
|
||||
if (av_read_frame(pFormatCtx, packet) < 0) {
|
||||
return false;
|
||||
}
|
||||
// We keep reading until we get the right stream index.
|
||||
} while (packet->stream_index != audio_stream_index);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FillLowLevelPacket() {
|
||||
av_init_packet(packet);
|
||||
if (bufferPos < (u32)dataOff) {
|
||||
bufferPos = dataOff;
|
||||
}
|
||||
|
||||
packet->data = data_buf + bufferPos;
|
||||
packet->size = atracBytesPerFrame;
|
||||
packet->pos = bufferPos;
|
||||
bufferPos += atracBytesPerFrame;
|
||||
return true;
|
||||
}
|
||||
|
||||
AtracDecodeResult DecodePacket() {
|
||||
AVPacket tempPacket;
|
||||
AVPacket *decodePacket = packet;
|
||||
if (packet->size < (int)atracBytesPerFrame) {
|
||||
// Whoops, we have a packet that is smaller than a frame. Let's meld a new one.
|
||||
u32 initialSize = packet->size;
|
||||
int needed = atracBytesPerFrame - initialSize;
|
||||
av_init_packet(&tempPacket);
|
||||
av_copy_packet(&tempPacket, packet);
|
||||
av_grow_packet(&tempPacket, needed);
|
||||
|
||||
// Okay, we're "out of data", let's get more.
|
||||
packet->size = 0;
|
||||
|
||||
if (FillPacket()) {
|
||||
if (PSP_CoreParameter().compat.flags().GTAMusicFix) {
|
||||
if (packet->size >= needed) {
|
||||
memcpy(tempPacket.data + initialSize, packet->data, needed);
|
||||
packet->size -= needed;
|
||||
packet->data += needed;
|
||||
}
|
||||
} else {
|
||||
int to_copy = packet->size >= needed ? needed : packet->size;
|
||||
memcpy(tempPacket.data + initialSize, packet->data, to_copy);
|
||||
packet->size -= to_copy;
|
||||
packet->data += to_copy;
|
||||
tempPacket.size = initialSize + to_copy;
|
||||
}
|
||||
} else {
|
||||
tempPacket.size = initialSize;
|
||||
}
|
||||
decodePacket = &tempPacket;
|
||||
}
|
||||
|
||||
int got_frame = 0;
|
||||
int bytes_read = avcodec_decode_audio4(pCodecCtx, pFrame, &got_frame, decodePacket);
|
||||
if (packet != decodePacket) {
|
||||
av_free_packet(&tempPacket);
|
||||
}
|
||||
int bytes_read = avcodec_decode_audio4(pCodecCtx, pFrame, &got_frame, packet);
|
||||
av_free_packet(packet);
|
||||
if (bytes_read == AVERROR_PATCHWELCOME) {
|
||||
ERROR_LOG(ME, "Unsupported feature in ATRAC audio.");
|
||||
// Let's try the next packet.
|
||||
if (packet == decodePacket) {
|
||||
packet->size = 0;
|
||||
}
|
||||
// TODO: Or actually, should we return a blank frame and pretend it worked?
|
||||
return ATDECODE_FEEDME;
|
||||
packet->size = 0;
|
||||
return ATDECODE_BADFRAME;
|
||||
} else if (bytes_read < 0) {
|
||||
ERROR_LOG_REPORT(ME, "avcodec_decode_audio4: Error decoding audio %d / %08x", bytes_read, bytes_read);
|
||||
failedDecode = true;
|
||||
return ATDECODE_FAILED;
|
||||
}
|
||||
|
||||
if (packet == decodePacket) {
|
||||
packet->size -= bytes_read;
|
||||
packet->data += bytes_read;
|
||||
}
|
||||
return got_frame ? ATDECODE_GOTFRAME : ATDECODE_FEEDME;
|
||||
}
|
||||
#endif // USE_FFMPEG
|
||||
|
@ -665,6 +615,12 @@ int Atrac::Analyze() {
|
|||
}
|
||||
|
||||
// TODO: There are some format specific bytes here which seem to have fixed values?
|
||||
// Probably don't need them.
|
||||
|
||||
if (at3fmt->fmtTag == AT3_MAGIC) {
|
||||
// This is the offset to the jointStereo field.
|
||||
jointStereo = Memory::Read_U32(first.addr + offset + 24);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case FACT_CHUNK_MAGIC:
|
||||
|
@ -770,6 +726,7 @@ int Atrac::AnalyzeAA3() {
|
|||
atracBytesPerFrame = (codecParams & 0x03FF) * 8;
|
||||
atracBitrate = at3SampleRates[(codecParams >> 13) & 7] * atracBytesPerFrame * 8 / 1024;
|
||||
atracChannels = 2;
|
||||
jointStereo = (codecParams >> 17) & 1;
|
||||
break;
|
||||
case 1:
|
||||
codecType = PSP_MODE_AT_3_PLUS;
|
||||
|
@ -786,7 +743,7 @@ int Atrac::AnalyzeAA3() {
|
|||
return ATRAC_ERROR_AA3_INVALID_DATA;
|
||||
}
|
||||
|
||||
dataOff = 0;
|
||||
dataOff = 10 + tagSize + 96;
|
||||
firstSampleoffset = 0;
|
||||
if (endSample < 0 && atracBytesPerFrame != 0) {
|
||||
int atracSamplesPerFrame = (codecType == PSP_MODE_AT_3_PLUS ? ATRAC3PLUS_MAX_SAMPLES : ATRAC3_MAX_SAMPLES);
|
||||
|
@ -823,7 +780,7 @@ u32 _AtracAddStreamData(int atracID, u32 bufPtr, u32 bytesToAdd) {
|
|||
atrac->first.size += bytesToAdd;
|
||||
if (atrac->first.size > atrac->first.filesize)
|
||||
atrac->first.size = atrac->first.filesize;
|
||||
atrac->first.fileoffset = atrac->first.size;
|
||||
atrac->first.fileoffset += addbytes;
|
||||
atrac->first.writableBytes = 0;
|
||||
if (atrac->atracContext.IsValid()) {
|
||||
// refresh atracContext
|
||||
|
@ -857,11 +814,11 @@ static u32 sceAtracAddStreamData(int atracID, u32 bytesToAdd) {
|
|||
if (bytesToAdd > 0) {
|
||||
int addbytes = std::min(bytesToAdd, atrac->first.filesize - atrac->first.fileoffset);
|
||||
Memory::Memcpy(atrac->data_buf + atrac->first.fileoffset, atrac->first.addr + atrac->first.offset, addbytes);
|
||||
atrac->first.fileoffset += addbytes;
|
||||
}
|
||||
atrac->first.size += bytesToAdd;
|
||||
if (atrac->first.size > atrac->first.filesize)
|
||||
atrac->first.size = atrac->first.filesize;
|
||||
atrac->first.fileoffset = atrac->first.size;
|
||||
atrac->first.writableBytes -= bytesToAdd;
|
||||
atrac->first.offset += bytesToAdd;
|
||||
}
|
||||
|
@ -893,28 +850,17 @@ u32 _AtracDecodeData(int atracID, u8 *outbuf, u32 outbufPtr, u32 *SamplesNum, u3
|
|||
// It seems like the PSP aligns the sample position to 0x800...?
|
||||
int offsetSamples = atrac->firstSampleoffset + firstOffsetExtra;
|
||||
int skipSamples = 0;
|
||||
if (PSP_CoreParameter().compat.flags().GTAMusicFix) {
|
||||
skipSamples = atrac->currentSample == 0 ? offsetSamples : 0;
|
||||
}
|
||||
u32 maxSamples = atrac->endSample - atrac->currentSample;
|
||||
u32 unalignedSamples = (offsetSamples + atrac->currentSample) % atracSamplesPerFrame;
|
||||
if (unalignedSamples != 0) {
|
||||
// We're off alignment, possibly due to a loop. Force it back on.
|
||||
maxSamples = atracSamplesPerFrame - unalignedSamples;
|
||||
if (!PSP_CoreParameter().compat.flags().GTAMusicFix) {
|
||||
skipSamples = unalignedSamples;
|
||||
}
|
||||
skipSamples = unalignedSamples;
|
||||
}
|
||||
|
||||
#ifdef USE_FFMPEG
|
||||
if (!atrac->failedDecode && (atrac->codecType == PSP_MODE_AT_3 || atrac->codecType == PSP_MODE_AT_3_PLUS) && atrac->pCodecCtx) {
|
||||
if (PSP_CoreParameter().compat.flags().GTAMusicFix) {
|
||||
int forceseekSample = atrac->currentSample * 2 > atrac->endSample ? 0 : atrac->endSample;
|
||||
atrac->SeekToSample(forceseekSample);
|
||||
atrac->SeekToSample(atrac->currentSample == 0 ? 0 : atrac->currentSample + offsetSamples);
|
||||
} else {
|
||||
atrac->SeekToSample(atrac->currentSample);
|
||||
}
|
||||
atrac->SeekToSample(atrac->currentSample);
|
||||
|
||||
AtracDecodeResult res = ATDECODE_FEEDME;
|
||||
while (atrac->FillPacket()) {
|
||||
|
@ -965,7 +911,7 @@ u32 _AtracDecodeData(int atracID, u8 *outbuf, u32 outbufPtr, u32 *SamplesNum, u3
|
|||
}
|
||||
}
|
||||
}
|
||||
if (res == ATDECODE_GOTFRAME) {
|
||||
if (res == ATDECODE_GOTFRAME || res == ATDECODE_BADFRAME) {
|
||||
// We only want one frame per call, let's continue the next time.
|
||||
break;
|
||||
}
|
||||
|
@ -990,11 +936,7 @@ u32 _AtracDecodeData(int atracID, u8 *outbuf, u32 outbufPtr, u32 *SamplesNum, u3
|
|||
int finishFlag = 0;
|
||||
if (atrac->loopNum != 0 && (atrac->currentSample > atrac->loopEndSample ||
|
||||
(numSamples == 0 && atrac->first.size >= atrac->first.filesize))) {
|
||||
if (PSP_CoreParameter().compat.flags().GTAMusicFix) {
|
||||
atrac->currentSample = 0;
|
||||
} else {
|
||||
atrac->SeekToSample(atrac->loopStartSample);
|
||||
}
|
||||
atrac->SeekToSample(atrac->loopStartSample);
|
||||
if (atrac->loopNum > 0)
|
||||
atrac->loopNum --;
|
||||
} else if (atrac->currentSample >= atrac->endSample ||
|
||||
|
@ -1067,7 +1009,7 @@ static u32 sceAtracGetBufferInfoForResetting(int atracID, int sample, u32 buffer
|
|||
return ATRAC_ERROR_BAD_SAMPLE;
|
||||
}
|
||||
|
||||
int Sampleoffset = atrac->getDecodePosBySample(sample);
|
||||
int Sampleoffset = atrac->getFileOffsetBySample(sample);
|
||||
int minWritebytes = std::max(Sampleoffset - (int)atrac->first.size, 0);
|
||||
// Reset temp buf for adding more stream data and set full filled buffer
|
||||
atrac->first.writableBytes = std::min(atrac->first.filesize - atrac->first.size, atrac->atracBufSize);
|
||||
|
@ -1075,10 +1017,6 @@ static u32 sceAtracGetBufferInfoForResetting(int atracID, int sample, u32 buffer
|
|||
// minWritebytes should not be bigger than writeablebytes
|
||||
minWritebytes = std::min(minWritebytes, (int)atrac->first.writableBytes);
|
||||
|
||||
if (atrac->first.fileoffset <= 2*atrac->atracBufSize){
|
||||
Sampleoffset = atrac->first.fileoffset;
|
||||
}
|
||||
|
||||
// If we've already loaded everything, the answer is 0.
|
||||
if (atrac->first.size >= atrac->first.filesize) {
|
||||
Sampleoffset = 0;
|
||||
|
@ -1344,9 +1282,7 @@ static u32 sceAtracGetStreamDataInfo(int atracID, u32 writeAddr, u32 writableByt
|
|||
// Reset temp buf for adding more stream data and set full filled buffer.
|
||||
atrac->first.writableBytes = std::min(atrac->first.filesize - atrac->first.size, atrac->atracBufSize);
|
||||
} else {
|
||||
if (!PSP_CoreParameter().compat.flags().GTAMusicFix) {
|
||||
atrac->first.writableBytes = std::min(atrac->first.filesize - atrac->first.size, atrac->first.writableBytes);
|
||||
}
|
||||
atrac->first.writableBytes = std::min(atrac->first.filesize - atrac->first.size, atrac->first.writableBytes);
|
||||
}
|
||||
|
||||
atrac->first.offset = 0;
|
||||
|
@ -1386,64 +1322,22 @@ static u32 sceAtracResetPlayPosition(int atracID, int sample, int bytesWrittenFi
|
|||
return ATRAC_ERROR_NO_DATA;
|
||||
} else {
|
||||
INFO_LOG(ME, "sceAtracResetPlayPosition(%i, %i, %i, %i)", atracID, sample, bytesWrittenFirstBuf, bytesWrittenSecondBuf);
|
||||
atrac->first.fileoffset = atrac->getFileOffsetBySample(sample);
|
||||
if (bytesWrittenFirstBuf > 0)
|
||||
sceAtracAddStreamData(atracID, bytesWrittenFirstBuf);
|
||||
if (PSP_CoreParameter().compat.flags().GTAMusicFix) {
|
||||
atrac->currentSample = sample;
|
||||
}
|
||||
#ifdef USE_FFMPEG
|
||||
if ((atrac->codecType == PSP_MODE_AT_3 || atrac->codecType == PSP_MODE_AT_3_PLUS) && atrac->pCodecCtx) {
|
||||
atrac->SeekToSample(sample);
|
||||
} else
|
||||
#endif // USE_FFMPEG
|
||||
{
|
||||
if (!PSP_CoreParameter().compat.flags().GTAMusicFix) {
|
||||
atrac->currentSample = sample;
|
||||
}
|
||||
atrac->currentSample = sample;
|
||||
atrac->decodePos = atrac->getDecodePosBySample(sample);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef USE_FFMPEG
|
||||
static int _AtracReadbuffer(void *opaque, uint8_t *buf, int buf_size) {
|
||||
Atrac *atrac = (Atrac *)opaque;
|
||||
if (atrac->bufferPos > atrac->first.filesize)
|
||||
return -1;
|
||||
int size = std::min((int)atrac->atracBufSize, buf_size);
|
||||
size = std::max(std::min(((int)atrac->first.size - (int)atrac->bufferPos), size), 0);
|
||||
if (size > 0)
|
||||
memcpy(buf, atrac->data_buf + atrac->bufferPos, size);
|
||||
atrac->bufferPos += size;
|
||||
return size;
|
||||
}
|
||||
|
||||
static int64_t _AtracSeekbuffer(void *opaque, int64_t offset, int whence) {
|
||||
Atrac *atrac = (Atrac*)opaque;
|
||||
if (offset > atrac->first.filesize)
|
||||
return -1;
|
||||
|
||||
switch (whence) {
|
||||
case SEEK_SET:
|
||||
atrac->bufferPos = (u32)offset;
|
||||
break;
|
||||
case SEEK_CUR:
|
||||
atrac->bufferPos += (u32)offset;
|
||||
break;
|
||||
case SEEK_END:
|
||||
atrac->bufferPos = atrac->first.filesize - (u32)offset;
|
||||
break;
|
||||
#ifdef USE_FFMPEG
|
||||
case AVSEEK_SIZE:
|
||||
return atrac->first.filesize;
|
||||
#endif
|
||||
}
|
||||
return atrac->bufferPos;
|
||||
}
|
||||
|
||||
#endif // USE_FFMPEG
|
||||
|
||||
#ifdef USE_FFMPEG
|
||||
static int __AtracUpdateOutputMode(Atrac *atrac, int wanted_channels) {
|
||||
if (atrac->pSwrCtx && atrac->atracOutputChannels == wanted_channels)
|
||||
|
@ -1483,49 +1377,56 @@ int __AtracSetContext(Atrac *atrac) {
|
|||
|
||||
u8* tempbuf = (u8*)av_malloc(atrac->atracBufSize);
|
||||
|
||||
atrac->pFormatCtx = avformat_alloc_context();
|
||||
atrac->pAVIOCtx = avio_alloc_context(tempbuf, atrac->atracBufSize, 0, (void*)atrac, _AtracReadbuffer, NULL, _AtracSeekbuffer);
|
||||
atrac->pFormatCtx->pb = atrac->pAVIOCtx;
|
||||
|
||||
int ret;
|
||||
// Load audio buffer
|
||||
if((ret = avformat_open_input((AVFormatContext**)&atrac->pFormatCtx, NULL, NULL, NULL)) != 0) {
|
||||
ERROR_LOG(ME, "avformat_open_input: Cannot open input %d", ret);
|
||||
// TODO: This is not exactly correct, but if the header is right and there's not enough data
|
||||
// (which is likely the case here), this is the correct error.
|
||||
return ATRAC_ERROR_ALL_DATA_DECODED;
|
||||
}
|
||||
|
||||
if((ret = avformat_find_stream_info(atrac->pFormatCtx, NULL)) < 0) {
|
||||
ERROR_LOG(ME, "avformat_find_stream_info: Cannot find stream information %d", ret);
|
||||
AVCodecID ff_codec;
|
||||
if (atrac->codecType == PSP_MODE_AT_3) {
|
||||
ff_codec = AV_CODEC_ID_ATRAC3;
|
||||
} else if (atrac->codecType == PSP_MODE_AT_3_PLUS) {
|
||||
ff_codec = AV_CODEC_ID_ATRAC3P;
|
||||
} else {
|
||||
ERROR_LOG_REPORT(ME, "Unexpected codec type %d", atrac->codecType);
|
||||
return -1;
|
||||
}
|
||||
|
||||
AVCodec *pCodec;
|
||||
// select the audio stream
|
||||
ret = av_find_best_stream(atrac->pFormatCtx, AVMEDIA_TYPE_AUDIO, -1, -1, &pCodec, 0);
|
||||
if (ret < 0) {
|
||||
if (ret == AVERROR_DECODER_NOT_FOUND) {
|
||||
ERROR_LOG(HLE, "av_find_best_stream: No appropriate decoder found");
|
||||
} else {
|
||||
ERROR_LOG(HLE, "av_find_best_stream: Cannot find an audio stream in the input file %d", ret);
|
||||
}
|
||||
return -1;
|
||||
const AVCodec *codec = avcodec_find_decoder(ff_codec);
|
||||
atrac->pCodecCtx = avcodec_alloc_context3(codec);
|
||||
|
||||
if (atrac->codecType == PSP_MODE_AT_3) {
|
||||
// For ATRAC3, we need the "extradata" in the RIFF header.
|
||||
atrac->pCodecCtx->extradata = (uint8_t *)av_mallocz(14);
|
||||
atrac->pCodecCtx->extradata_size = 14;
|
||||
|
||||
// We don't pull this from the RIFF so that we can support OMA also.
|
||||
// The only thing that changes are the jointStereo values.
|
||||
atrac->pCodecCtx->extradata[0] = 1;
|
||||
atrac->pCodecCtx->extradata[3] = 0x10;
|
||||
atrac->pCodecCtx->extradata[6] = atrac->jointStereo;
|
||||
atrac->pCodecCtx->extradata[8] = atrac->jointStereo;
|
||||
atrac->pCodecCtx->extradata[10] = 1;
|
||||
}
|
||||
atrac->audio_stream_index = ret;
|
||||
atrac->pCodecCtx = atrac->pFormatCtx->streams[atrac->audio_stream_index]->codec;
|
||||
|
||||
// Appears we need to force mono in some cases. (See CPkmn's comments in issue #4248)
|
||||
if (atrac->atracChannels == 1)
|
||||
if (atrac->atracChannels == 1) {
|
||||
atrac->pCodecCtx->channels = 1;
|
||||
atrac->pCodecCtx->channel_layout = AV_CH_LAYOUT_MONO;
|
||||
} else if (atrac->atracChannels == 2) {
|
||||
atrac->pCodecCtx->channels = 2;
|
||||
atrac->pCodecCtx->channel_layout = AV_CH_LAYOUT_STEREO;
|
||||
} else {
|
||||
ERROR_LOG_REPORT(ME, "Unexpected channel count %d", atrac->atracChannels);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
// Explicitly set the block_align value (needed by newer FFmpeg versions, see #5772.)
|
||||
if (atrac->pCodecCtx->block_align == 0) {
|
||||
atrac->pCodecCtx->block_align = atrac->atracBytesPerFrame;
|
||||
}
|
||||
// Only one supported, it seems?
|
||||
atrac->pCodecCtx->sample_rate = 44100;
|
||||
|
||||
atrac->pCodecCtx->request_sample_fmt = AV_SAMPLE_FMT_S16;
|
||||
if ((ret = avcodec_open2(atrac->pCodecCtx, pCodec, NULL)) < 0) {
|
||||
int ret;
|
||||
if ((ret = avcodec_open2(atrac->pCodecCtx, codec, nullptr)) < 0) {
|
||||
ERROR_LOG(ME, "avcodec_open2: Cannot open audio decoder %d", ret);
|
||||
return -1;
|
||||
}
|
||||
|
@ -1586,7 +1487,6 @@ static int _AtracSetData(Atrac *atrac, u32 buffer, u32 bufferSize) {
|
|||
return __AtracSetContext(atrac);
|
||||
}
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1996,16 +1896,16 @@ void _AtracGenarateContext(Atrac *atrac, SceAtracId *context) {
|
|||
// TODO: Should we just keep this in PSP ram then, or something?
|
||||
} else if (!atrac->data_buf) {
|
||||
// State 1, no buffer yet.
|
||||
context->info.state = 1;
|
||||
context->info.state = ATRAC_STATUS_NO_DATA;
|
||||
} else if (atrac->first.size >= atrac->first.filesize) {
|
||||
// state 2, all data loaded
|
||||
context->info.state = 2;
|
||||
context->info.state = ATRAC_STATUS_ALL_DATA_LOADED;
|
||||
} else if (atrac->loopinfoNum == 0) {
|
||||
// state 3, lack some data, no loop info
|
||||
context->info.state = 3;
|
||||
context->info.state = ATRAC_STATUS_STREAMED_WITHOUT_LOOP;
|
||||
} else {
|
||||
// state 6, lack some data, has loop info
|
||||
context->info.state = 6;
|
||||
context->info.state = ATRAC_STATUS_STREAMED_LOOP_WITH_TRAILER;
|
||||
}
|
||||
context->info.samplesPerChan = (atrac->codecType == PSP_MODE_AT_3_PLUS ? ATRAC3PLUS_MAX_SAMPLES : ATRAC3_MAX_SAMPLES);
|
||||
context->info.sampleSize = atrac->atracBytesPerFrame;
|
||||
|
@ -2136,6 +2036,7 @@ static int sceAtracLowLevelInitDecoder(int atracID, u32 paramsAddr) {
|
|||
atrac->data_buf = new u8[atrac->first.filesize];
|
||||
memcpy(atrac->data_buf, at3Header, headersize);
|
||||
atrac->currentSample = 0;
|
||||
// TODO: Check failure?
|
||||
__AtracSetContext(atrac);
|
||||
return 0;
|
||||
}
|
||||
|
@ -2187,17 +2088,11 @@ static int sceAtracLowLevelDecode(int atracID, u32 sourceAddr, u32 sourceBytesCo
|
|||
}
|
||||
|
||||
int numSamples = 0;
|
||||
if (PSP_CoreParameter().compat.flags().GTAMusicFix) {
|
||||
int forceseekSample = 0x200000;
|
||||
atrac->SeekToSample(forceseekSample);
|
||||
atrac->SeekToSample(atrac->currentSample);
|
||||
} else {
|
||||
atrac->ForceSeekToSample(atrac->currentSample);
|
||||
}
|
||||
atrac->ForceSeekToSample(atrac->currentSample);
|
||||
|
||||
if (!atrac->failedDecode) {
|
||||
AtracDecodeResult res;
|
||||
while (atrac->FillPacket()) {
|
||||
while (atrac->FillLowLevelPacket()) {
|
||||
res = atrac->DecodePacket();
|
||||
if (res == ATDECODE_FAILED) {
|
||||
break;
|
||||
|
@ -2215,6 +2110,8 @@ static int sceAtracLowLevelDecode(int atracID, u32 sourceAddr, u32 sourceBytesCo
|
|||
ERROR_LOG(ME, "swr_convert: Error while converting %d", avret);
|
||||
}
|
||||
break;
|
||||
} else if (res == ATDECODE_BADFRAME) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2222,18 +2119,12 @@ static int sceAtracLowLevelDecode(int atracID, u32 sourceAddr, u32 sourceBytesCo
|
|||
atrac->currentSample += numSamples;
|
||||
numSamples = (atrac->codecType == PSP_MODE_AT_3_PLUS ? ATRAC3PLUS_MAX_SAMPLES : ATRAC3_MAX_SAMPLES);
|
||||
Memory::Write_U32(numSamples * sizeof(s16) * atrac->atracOutputChannels, sampleBytesAddr);
|
||||
if (PSP_CoreParameter().compat.flags().GTAMusicFix) {
|
||||
atrac->SeekToSample(atrac->currentSample);
|
||||
}
|
||||
|
||||
if (atrac->bufferPos >= atrac->first.size) {
|
||||
atrac->first.writableBytes = atrac->atracBytesPerFrame;
|
||||
atrac->first.size = atrac->firstSampleoffset;
|
||||
if (PSP_CoreParameter().compat.flags().GTAMusicFix) {
|
||||
atrac->currentSample = 0;
|
||||
} else {
|
||||
atrac->ForceSeekToSample(0);
|
||||
}
|
||||
atrac->ForceSeekToSample(0);
|
||||
atrac->bufferPos = atrac->dataOff;
|
||||
}
|
||||
else
|
||||
atrac->first.writableBytes = 0;
|
||||
|
|
|
@ -26,6 +26,26 @@ void __AtracInit();
|
|||
void __AtracDoState(PointerWrap &p);
|
||||
void __AtracShutdown();
|
||||
|
||||
enum AtracStatus : u8 {
|
||||
ATRAC_STATUS_NO_DATA = 1,
|
||||
ATRAC_STATUS_ALL_DATA_LOADED = 2,
|
||||
ATRAC_STATUS_HALFWAY_BUFFER = 3,
|
||||
ATRAC_STATUS_STREAMED_WITHOUT_LOOP = 4,
|
||||
ATRAC_STATUS_STREAMED_LOOP_FROM_END = 5,
|
||||
// This means there's additional audio after the loop.
|
||||
// i.e. ~~before loop~~ [ ~~this part loops~~ ] ~~after loop~~
|
||||
// The "fork in the road" means a second buffer is needed for the second path.
|
||||
ATRAC_STATUS_STREAMED_LOOP_WITH_TRAILER = 6,
|
||||
ATRAC_STATUS_LOW_LEVEL = 8,
|
||||
ATRAC_STATUS_FOR_SCESAS = 16,
|
||||
};
|
||||
|
||||
#if COMMON_LITTLE_ENDIAN
|
||||
typedef AtracStatus AtracStatus_le;
|
||||
#else
|
||||
typedef swap_struct_t<AtracStatus, swap_32_t<AtracStatus> > AtracStatus_le;
|
||||
#endif
|
||||
|
||||
typedef struct
|
||||
{
|
||||
u32_le decodePos; // 0
|
||||
|
@ -36,7 +56,7 @@ typedef struct
|
|||
char numFrame; // 20
|
||||
// 2: all the stream data on the buffer
|
||||
// 6: looping -> second buffer needed
|
||||
char state; // 21
|
||||
AtracStatus_le state; // 21
|
||||
char unk22;
|
||||
char numChan; // 23
|
||||
u16_le sampleSize; // 24
|
||||
|
|
|
@ -55,7 +55,7 @@ int SimpleAudio::GetAudioCodecID(int audioType) {
|
|||
SimpleAudio::SimpleAudio(int audioType, int sample_rate, int channels)
|
||||
: ctxPtr(0xFFFFFFFF), audioType(audioType), sample_rate_(sample_rate), channels_(channels),
|
||||
outSamples(0), srcPos(0), wanted_resample_freq(44100), frame_(0), codec_(0), codecCtx_(0), swrCtx_(0),
|
||||
extradata_(0), codecOpen_(false) {
|
||||
codecOpen_(false) {
|
||||
Init();
|
||||
}
|
||||
|
||||
|
@ -114,62 +114,36 @@ bool SimpleAudio::OpenCodec(int block_align) {
|
|||
#endif // USE_FFMPEG
|
||||
}
|
||||
|
||||
bool SimpleAudio::ResetCodecCtx(int channels, int samplerate) {
|
||||
#ifdef USE_FFMPEG
|
||||
if (codecCtx_)
|
||||
avcodec_close(codecCtx_);
|
||||
|
||||
// Find decoder
|
||||
int audioCodecId = GetAudioCodecID(audioType);
|
||||
codec_ = avcodec_find_decoder((AVCodecID)audioCodecId);
|
||||
if (!codec_) {
|
||||
// Eh, we shouldn't even have managed to compile. But meh.
|
||||
ERROR_LOG(ME, "This version of FFMPEG does not support AV_CODEC_ctx for audio (%s). Update your submodule.", GetCodecName(audioType));
|
||||
return false;
|
||||
}
|
||||
|
||||
codecCtx_->channels = channels;
|
||||
codecCtx_->channel_layout = channels==2?AV_CH_LAYOUT_STEREO:AV_CH_LAYOUT_MONO;
|
||||
codecCtx_->sample_rate = samplerate;
|
||||
codecOpen_ = false;
|
||||
return true;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
void SimpleAudio::SetExtraData(u8 *data, int size, int wav_bytes_per_packet) {
|
||||
delete [] extradata_;
|
||||
extradata_ = 0;
|
||||
|
||||
if (data != 0) {
|
||||
extradata_ = new u8[size];
|
||||
memcpy(extradata_, data, size);
|
||||
}
|
||||
|
||||
#ifdef USE_FFMPEG
|
||||
if (codecCtx_) {
|
||||
codecCtx_->extradata = extradata_;
|
||||
codecCtx_->extradata = (uint8_t *)av_mallocz(size);
|
||||
codecCtx_->extradata_size = size;
|
||||
codecCtx_->block_align = wav_bytes_per_packet;
|
||||
codecOpen_ = false;
|
||||
|
||||
if (data != nullptr) {
|
||||
memcpy(codecCtx_->extradata, data, size);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
SimpleAudio::~SimpleAudio() {
|
||||
#ifdef USE_FFMPEG
|
||||
if (swrCtx_)
|
||||
swr_free(&swrCtx_);
|
||||
if (frame_)
|
||||
av_frame_free(&frame_);
|
||||
if (codecCtx_)
|
||||
avcodec_close(codecCtx_);
|
||||
frame_ = 0;
|
||||
codecCtx_ = 0;
|
||||
swr_free(&swrCtx_);
|
||||
av_frame_free(&frame_);
|
||||
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(55, 52, 0)
|
||||
avcodec_free_context(&codecCtx_);
|
||||
#else
|
||||
// Future versions may add other things to free, but avcodec_free_context didn't exist yet here.
|
||||
avcodec_close(codecCtx_);
|
||||
av_freep(&codecCtx_->extradata);
|
||||
av_freep(&codecCtx_->subtitle_header);
|
||||
av_freep(&codecCtx_);
|
||||
#endif
|
||||
codec_ = 0;
|
||||
#endif // USE_FFMPEG
|
||||
delete [] extradata_;
|
||||
extradata_ = 0;
|
||||
}
|
||||
|
||||
bool SimpleAudio::IsOK() const {
|
||||
|
|
|
@ -51,7 +51,6 @@ public:
|
|||
|
||||
int GetOutSamples();
|
||||
int GetSourcePos();
|
||||
bool ResetCodecCtx(int channels, int samplerate);
|
||||
int GetAudioCodecID(int audioType); // Get audioCodecId from audioType
|
||||
|
||||
// Not save stated, only used by UI. Used for ATRAC3 (non+) files.
|
||||
|
@ -82,8 +81,6 @@ private:
|
|||
AVCodecContext *codecCtx_;
|
||||
SwrContext *swrCtx_;
|
||||
|
||||
// Not savestated, only used by UI.
|
||||
u8 *extradata_;
|
||||
bool codecOpen_;
|
||||
};
|
||||
|
||||
|
|
|
@ -73,52 +73,3 @@ PixelDepthRounding = true
|
|||
PixelDepthRounding = true
|
||||
[ULJS00454]
|
||||
PixelDepthRounding = true
|
||||
|
||||
|
||||
# GTA audio issues
|
||||
# These games stream their radio stations from disc as giant audio files. They seek into
|
||||
# the correct position. Our ATRAC3 module (sceAtrac) implementation is still not very accurate
|
||||
# and needs some special handling for this to work correctly, for unknown reasons.
|
||||
|
||||
# GTAMusicFix removes the effect of pull #6976, which fixed many games but broke GTA.
|
||||
# Issue #7863 has some more information.
|
||||
|
||||
# GTA Liberty City Stories
|
||||
[ULUS10041]
|
||||
GTAMusicFix = true
|
||||
[ULES00151]
|
||||
GTAMusicFix = true
|
||||
[ULJM05255]
|
||||
GTAMusicFix = true
|
||||
[ULJM05359]
|
||||
GTAMusicFix = true
|
||||
[ULJM05885]
|
||||
GTAMusicFix = true
|
||||
[NPJH50825]
|
||||
GTAMusicFix = true
|
||||
|
||||
# GTA Vice City Stories
|
||||
[ULES00502]
|
||||
GTAMusicFix = true
|
||||
[ULUS10160]
|
||||
GTAMusicFix = true
|
||||
[ULJM05297]
|
||||
GTAMusicFix = true
|
||||
[ULJM05395]
|
||||
GTAMusicFix = true
|
||||
[ULJM05884]
|
||||
GTAMusicFix = true
|
||||
[NPJH50827]
|
||||
GTAMusicFix = true
|
||||
|
||||
# GTA Chinatown Wars
|
||||
[ULUS10490]
|
||||
GTAMusicFix = true
|
||||
[ULES01347]
|
||||
GTAMusicFix = true
|
||||
[ULJM05604]
|
||||
GTAMusicFix = true
|
||||
[ULJM05804]
|
||||
GTAMusicFix = true
|
||||
[NPJH00138]
|
||||
GTAMusicFix = true
|
||||
|
|
Loading…
Add table
Reference in a new issue