Merge pull request #19053 from hrydgard/remove-ffmpeg-from-sceatrac

Remove ffmpeg use from the sceAtrac HLE module
This commit is contained in:
Henrik Rydgård 2024-04-13 09:03:18 +02:00 committed by GitHub
commit c3485d7f6a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 74 additions and 348 deletions

View File

@ -29,6 +29,7 @@
#include "Core/Debugger/MemBlockInfo.h"
#include "Core/HW/MediaEngine.h"
#include "Core/HW/BufferQueue.h"
#include "Core/HW/Atrac3Standalone.h"
#include "Core/HLE/sceKernel.h"
#include "Core/HLE/sceUtility.h"
@ -120,19 +121,6 @@ const size_t overAllocBytes = 16384;
static const int atracDecodeDelay = 2300;
#ifdef USE_FFMPEG
extern "C" {
#include "libavformat/avformat.h"
#include "libswresample/swresample.h"
#include "libavutil/samplefmt.h"
#include "libavcodec/avcodec.h"
#include "libavutil/version.h"
}
#include "Core/FFMPEGCompat.h"
#endif // USE_FFMPEG
enum AtracDecodeResult {
ATDECODE_FAILED = -1,
ATDECODE_FEEDME = 0,
@ -190,9 +178,8 @@ struct Atrac {
}
void ResetData() {
#ifdef USE_FFMPEG
ReleaseFFMPEGContext();
#endif // USE_FFMPEG
delete decoder_;
decoder_ = nullptr;
if (dataBuf_)
delete [] dataBuf_;
@ -391,6 +378,10 @@ struct Atrac {
}
int atracID_ = -1;
// TODO: Save the internal state of this, now technically possible.
AudioDecoder *decoder_ = nullptr;
u8 *dataBuf_ = nullptr;
u32 decodePos_ = 0;
@ -430,48 +421,10 @@ struct Atrac {
PSPPointer<SceAtracId> context_;
#ifdef USE_FFMPEG
AVCodecContext *codecCtx_ = nullptr;
SwrContext *swrCtx_ = nullptr;
AVFrame *frame_ = nullptr;
AVPacket *packet_ = nullptr;
#endif // USE_FFMPEG
#ifdef USE_FFMPEG
void ReleaseFFMPEGContext() {
// All of these allow null pointers.
av_freep(&frame_);
swr_free(&swrCtx_);
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(55, 52, 0)
// If necessary, extradata is automatically freed.
avcodec_free_context(&codecCtx_);
#else
// Future versions may add other things to free, but avcodec_free_context didn't exist yet here.
// Some old versions crash when we try to free extradata and subtitle_header, so let's not. A minor
// leak is better than a segfault.
// av_freep(&codecCtx_->extradata);
// av_freep(&codecCtx_->subtitle_header);
avcodec_close(codecCtx_);
av_freep(&codecCtx_);
#endif
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 12, 100)
av_packet_free(&packet_);
#else
av_free_packet(packet_);
delete packet_;
packet_ = nullptr;
#endif
}
#endif // USE_FFMPEG
void ForceSeekToSample(int sample) {
#ifdef USE_FFMPEG
avcodec_flush_buffers(codecCtx_);
// Discard any pending packet data.
packet_->size = 0;
#endif
if (decoder_) {
decoder_->FlushBuffers();
}
currentSample_ = sample;
}
@ -480,18 +433,14 @@ struct Atrac {
}
void SeekToSample(int sample) {
#ifdef USE_FFMPEG
// Discard any pending packet data.
packet_->size = 0;
// It seems like the PSP aligns the sample position to 0x800...?
const u32 offsetSamples = firstSampleOffset_ + FirstOffsetExtra();
const u32 unalignedSamples = (offsetSamples + sample) % SamplesPerFrame();
int seekFrame = sample + offsetSamples - unalignedSamples;
if ((sample != currentSample_ || sample == 0) && codecCtx_ != nullptr) {
if ((sample != currentSample_ || sample == 0) && decoder_ != nullptr) {
// Prefill the decode buffer with packets before the first sample offset.
avcodec_flush_buffers(codecCtx_);
decoder_->FlushBuffers();
int adjust = 0;
if (sample == 0) {
@ -501,17 +450,11 @@ struct Atrac {
const u32 off = FileOffsetBySample(sample + adjust);
const u32 backfill = bytesPerFrame_ * 2;
const u32 start = off - dataOff_ < backfill ? dataOff_ : off - backfill;
for (u32 pos = start; pos < off; pos += bytesPerFrame_) {
av_init_packet(packet_);
packet_->data = BufferStart() + pos;
packet_->size = bytesPerFrame_;
packet_->pos = pos;
// Process the packet, we don't care about success.
DecodePacket();
for (u32 pos = start; pos < off; pos += bytesPerFrame_) {
decoder_->Decode(BufferStart() + pos, bytesPerFrame_, nullptr, nullptr, nullptr);
}
}
#endif // USE_FFMPEG
currentSample_ = sample;
}
@ -525,85 +468,6 @@ struct Atrac {
return 0;
}
bool FillPacket(int adjust = 0) {
u32 off = FileOffsetBySample(currentSample_ + adjust);
if (off < first_.size) {
#ifdef USE_FFMPEG
av_init_packet(packet_);
packet_->data = BufferStart() + off;
packet_->size = std::min((u32)bytesPerFrame_, first_.size - off);
packet_->pos = off;
#endif // USE_FFMPEG
return true;
} else {
return false;
}
return true;
}
AtracDecodeResult DecodeLowLevelPacket(u8 *ptr) {
#ifdef USE_FFMPEG
av_init_packet(packet_);
packet_->data = ptr;
packet_->size = bytesPerFrame_;
packet_->pos = 0;
#endif // USE_FFMPEG
return DecodePacket();
}
AtracDecodeResult DecodePacket() {
#ifdef USE_FFMPEG
if (codecCtx_ == nullptr) {
return ATDECODE_FAILED;
}
int got_frame = 0;
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 48, 101)
if (packet_->size != 0) {
int err = avcodec_send_packet(codecCtx_, packet_);
if (err < 0) {
ERROR_LOG_REPORT(ME, "avcodec_send_packet: Error decoding audio %d / %08x", err, err);
failedDecode_ = true;
return ATDECODE_FAILED;
}
}
int err = avcodec_receive_frame(codecCtx_, frame_);
int bytes_read = 0;
if (err >= 0) {
bytes_read = frame_->pkt_size;
got_frame = 1;
} else if (err != AVERROR(EAGAIN)) {
bytes_read = err;
}
#else
int bytes_read = avcodec_decode_audio4(codecCtx_, frame_, &got_frame, packet_);
#endif
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 12, 100)
av_packet_unref(packet_);
#else
av_free_packet(packet_);
#endif
if (bytes_read == AVERROR_PATCHWELCOME) {
ERROR_LOG(ME, "Unsupported feature in ATRAC audio.");
// Let's try the next packet.
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;
}
return got_frame ? ATDECODE_GOTFRAME : ATDECODE_FEEDME;
#else
return ATDECODE_BADFRAME;
#endif // USE_FFMPEG
}
void CalculateStreamInfo(u32 *readOffset);
u32 StreamBufferEnd() const {
@ -663,15 +527,6 @@ void __AtracInit() {
atracIDTypes[3] = PSP_MODE_AT_3;
atracIDTypes[4] = 0;
atracIDTypes[5] = 0;
#ifdef USE_FFMPEG
#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(58, 18, 100)
avcodec_register_all();
#endif
#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(58, 12, 100)
av_register_all();
#endif
#endif // USE_FFMPEG
}
void __AtracLoadModule(int version, u32 crc) {
@ -1252,29 +1107,26 @@ u32 _AtracDecodeData(int atracID, u8 *outbuf, u32 outbufPtr, u32 *SamplesNum, u3
atrac->SeekToSample(atrac->currentSample_);
AtracDecodeResult res = ATDECODE_FEEDME;
while (atrac->FillPacket(-skipSamples)) {
uint32_t packetAddr = atrac->CurBufferAddress(-skipSamples);
#ifdef USE_FFMPEG
int packetSize = atrac->packet_->size;
#endif // USE_FFMPEG
res = atrac->DecodePacket();
if (res == ATDECODE_FEEDME) {
continue;
} else if (res == ATDECODE_FAILED) {
u32 off = atrac->FileOffsetBySample(atrac->currentSample_ - skipSamples);
if (off < atrac->first_.size) {
uint8_t *indata = atrac->BufferStart() + off;
int bytesConsumed = 0;
int outBytes = 0;
if (!atrac->decoder_->Decode(indata, atrac->bytesPerFrame_, &bytesConsumed,
outbuf, &outBytes)) {
// Decode failed.
*SamplesNum = 0;
*finish = 1;
return ATRAC_ERROR_ALL_DATA_DECODED;
} else if (res == ATDECODE_BADFRAME) {
// Retry next time.
break;
}
_dbg_assert_(res == ATDECODE_GOTFRAME);
#ifdef USE_FFMPEG
// got a frame
int skipped = std::min(skipSamples, atrac->frame_->nb_samples);
skipSamples -= skipped;
numSamples = atrac->frame_->nb_samples - skipped;
res = ATDECODE_GOTFRAME;
numSamples = outBytes / 4;
uint32_t packetAddr = atrac->CurBufferAddress(-skipSamples);
// got a frame
int skipped = std::min((u32)skipSamples, numSamples);
skipSamples -= skipped;
numSamples = numSamples - skipped;
// If we're at the end, clamp to samples we want. It always returns a full chunk.
numSamples = std::min(maxSamples, numSamples);
@ -1283,38 +1135,15 @@ u32 _AtracDecodeData(int atracID, u8 *outbuf, u32 outbufPtr, u32 *SamplesNum, u3
res = ATDECODE_FEEDME;
}
if (outbuf != NULL && numSamples != 0) {
int inbufOffset = 0;
if (skipped != 0) {
AVSampleFormat fmt = (AVSampleFormat)atrac->frame_->format;
// We want the offset per channel.
inbufOffset = av_samples_get_buffer_size(NULL, 1, skipped, fmt, 1);
}
u8 *out = outbuf;
const u8 *inbuf[2] = {
atrac->frame_->extended_data[0] + inbufOffset,
atrac->frame_->extended_data[1] + inbufOffset,
};
int avret = swr_convert(atrac->swrCtx_, &out, numSamples, inbuf, numSamples);
if (outbufPtr != 0) {
u32 outBytes = numSamples * atrac->outputChannels_ * sizeof(s16);
if (packetAddr != 0 && MemBlockInfoDetailed()) {
char tagData[128];
size_t tagSize = FormatMemWriteTagAt(tagData, sizeof(tagData), "AtracDecode/", packetAddr, packetSize);
NotifyMemInfo(MemBlockFlags::READ, packetAddr, packetSize, tagData, tagSize);
NotifyMemInfo(MemBlockFlags::WRITE, outbufPtr, outBytes, tagData, tagSize);
} else {
NotifyMemInfo(MemBlockFlags::WRITE, outbufPtr, outBytes, "AtracDecode");
}
}
if (avret < 0) {
ERROR_LOG(ME, "swr_convert: Error while converting %d", avret);
}
if (packetAddr != 0 && MemBlockInfoDetailed()) {
char tagData[128];
size_t tagSize = FormatMemWriteTagAt(tagData, sizeof(tagData), "AtracDecode/", packetAddr, atrac->bytesPerFrame_);
NotifyMemInfo(MemBlockFlags::READ, packetAddr, atrac->bytesPerFrame_, tagData, tagSize);
NotifyMemInfo(MemBlockFlags::WRITE, outbufPtr, outBytes, tagData, tagSize);
} else {
NotifyMemInfo(MemBlockFlags::WRITE, outbufPtr, outBytes, "AtracDecode");
}
// We only want one frame per call, let's continue the next time.
break;
#endif // USE_FFMPEG
}
if (res != ATDECODE_GOTFRAME && atrac->currentSample_ < atrac->endSample_) {
@ -1812,116 +1641,33 @@ static u32 sceAtracResetPlayPosition(int atracID, int sample, int bytesWrittenFi
return hleDelayResult(hleLogSuccessInfoI(ME, 0), "reset play pos", 3000);
}
#ifdef USE_FFMPEG
static int __AtracUpdateOutputMode(Atrac *atrac, int wanted_channels) {
if (atrac->swrCtx_ && atrac->outputChannels_ == wanted_channels)
return 0;
atrac->outputChannels_ = wanted_channels;
int64_t wanted_channel_layout = av_get_default_channel_layout(wanted_channels);
int64_t dec_channel_layout = av_get_default_channel_layout(atrac->channels_);
// extraData should be 14 zeroed bytes.
void InitAT3ExtraData(Atrac *atrac, uint8_t *extraData) {
// We don't pull this from the RIFF so that we can support OMA also.
atrac->swrCtx_ =
swr_alloc_set_opts
(
atrac->swrCtx_,
wanted_channel_layout,
AV_SAMPLE_FMT_S16,
atrac->codecCtx_->sample_rate,
dec_channel_layout,
atrac->codecCtx_->sample_fmt,
atrac->codecCtx_->sample_rate,
0,
NULL
);
if (!atrac->swrCtx_) {
ERROR_LOG(ME, "swr_alloc_set_opts: Could not allocate resampler context");
return -1;
}
if (swr_init(atrac->swrCtx_) < 0) {
ERROR_LOG(ME, "swr_init: Failed to initialize the resampling context");
return -1;
}
return 0;
// The only thing that changes are the jointStereo_ values.
extraData[0] = 1;
extraData[3] = atrac->channels_ << 3;
extraData[6] = atrac->jointStereo_;
extraData[8] = atrac->jointStereo_;
extraData[10] = 1;
}
#endif // USE_FFMPEG
int __AtracSetContext(Atrac *atrac) {
#ifdef USE_FFMPEG
InitFFmpeg();
if (atrac->decoder_) {
delete atrac->decoder_;
}
AVCodecID ff_codec;
// First, init the standalone decoder. Only used for low-level-decode initially, but simple.
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;
uint8_t extraData[14]{};
InitAT3ExtraData(atrac, extraData);
atrac->decoder_ = CreateAtrac3Audio(atrac->channels_, atrac->bytesPerFrame_, extraData, sizeof(extraData));
} else {
return hleReportError(ME, ATRAC_ERROR_UNKNOWN_FORMAT, "unknown codec type in set context");
atrac->decoder_ = CreateAtrac3PlusAudio(atrac->channels_, atrac->bytesPerFrame_);
}
if (atrac->codecCtx_) {
// Shouldn't happen, but just in case.
atrac->ReleaseFFMPEGContext();
}
AVCodec *codec = avcodec_find_decoder(ff_codec);
atrac->codecCtx_ = avcodec_alloc_context3(codec);
if (atrac->codecType_ == PSP_MODE_AT_3) {
// For ATRAC3, we need the "extradata" in the RIFF header.
atrac->codecCtx_->extradata = (uint8_t *)av_mallocz(14);
atrac->codecCtx_->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->codecCtx_->extradata[0] = 1;
atrac->codecCtx_->extradata[3] = atrac->channels_ << 3;
atrac->codecCtx_->extradata[6] = atrac->jointStereo_;
atrac->codecCtx_->extradata[8] = atrac->jointStereo_;
atrac->codecCtx_->extradata[10] = 1;
}
// Appears we need to force mono in some cases. (See CPkmn's comments in issue #4248)
if (atrac->channels_ == 1) {
atrac->codecCtx_->channels = 1;
atrac->codecCtx_->channel_layout = AV_CH_LAYOUT_MONO;
} else if (atrac->channels_ == 2) {
atrac->codecCtx_->channels = 2;
atrac->codecCtx_->channel_layout = AV_CH_LAYOUT_STEREO;
} else {
return hleReportError(ME, ATRAC_ERROR_UNKNOWN_FORMAT, "unknown channel layout in set context");
}
// Explicitly set the block_align value (needed by newer FFmpeg versions, see #5772.)
if (atrac->codecCtx_->block_align == 0) {
atrac->codecCtx_->block_align = atrac->bytesPerFrame_;
}
// Only one supported, it seems?
atrac->codecCtx_->sample_rate = 44100;
atrac->codecCtx_->request_sample_fmt = AV_SAMPLE_FMT_S16;
int ret;
if ((ret = avcodec_open2(atrac->codecCtx_, codec, nullptr)) < 0) {
// This can mean that the frame size is wrong or etc.
return hleLogError(ME, ATRAC_ERROR_BAD_CODEC_PARAMS, "failed to open decoder %d", ret);
}
if ((ret = __AtracUpdateOutputMode(atrac, atrac->outputChannels_)) < 0)
return hleLogError(ME, ret, "failed to set the output mode");
// alloc audio frame
atrac->frame_ = av_frame_alloc();
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 12, 100)
atrac->packet_ = av_packet_alloc();
#else
atrac->packet_ = new AVPacket;
av_init_packet(atrac->packet_);
atrac->packet_->data = nullptr;
atrac->packet_->size = 0;
#endif
// reinit decodePos, because ffmpeg had changed it.
atrac->decodePos_ = 0;
#endif
return 0;
}
@ -2321,11 +2067,6 @@ static int sceAtracSetAA3DataAndGetID(u32 buffer, u32 bufferSize, u32 fileSize,
int _AtracGetIDByContext(u32 contextAddr) {
int atracID = (int)Memory::Read_U32(contextAddr + 0xfc);
#ifdef USE_FFMPEG
Atrac *atrac = getAtrac(atracID);
if (atrac)
__AtracUpdateOutputMode(atrac, 1);
#endif // USE_FFMPEG
return atracID;
}
@ -2466,9 +2207,6 @@ static int sceAtracLowLevelInitDecoder(int atracID, u32 paramsAddr) {
atrac->first_.size = 0;
atrac->first_.filesize = atrac->bytesPerFrame_;
atrac->bufferState_ = ATRAC_STATUS_LOW_LEVEL;
atrac->dataBuf_ = new u8[atrac->first_.filesize + overAllocBytes];
memset(atrac->dataBuf_, 0, atrac->first_.filesize + overAllocBytes);
atrac->currentSample_ = 0;
int ret = __AtracSetContext(atrac);
@ -2500,30 +2238,13 @@ static int sceAtracLowLevelDecode(int atracID, u32 sourceAddr, u32 sourceBytesCo
}
int numSamples = (atrac->codecType_ == PSP_MODE_AT_3_PLUS ? ATRAC3PLUS_MAX_SAMPLES : ATRAC3_MAX_SAMPLES);
int bytesConsumed = 0;
int bytesWritten = 0;
atrac->decoder_->Decode(srcp, atrac->bytesPerFrame_, &bytesConsumed, outp, &bytesWritten);
*srcConsumed = bytesConsumed;
*outWritten = bytesWritten;
if (!atrac->failedDecode_) {
AtracDecodeResult res = atrac->DecodeLowLevelPacket(srcp);
if (res == ATDECODE_GOTFRAME) {
#ifdef USE_FFMPEG
// got a frame
numSamples = atrac->frame_->nb_samples;
u8 *out = outp;
int avret = swr_convert(atrac->swrCtx_, &out, numSamples,
(const u8**)atrac->frame_->extended_data, numSamples);
u32 outBytes = numSamples * atrac->outputChannels_ * sizeof(s16);
NotifyMemInfo(MemBlockFlags::WRITE, samplesAddr, outBytes, "AtracLowLevelDecode");
if (avret < 0) {
ERROR_LOG(ME, "swr_convert: Error while converting %d", avret);
}
#endif // USE_FFMPEG
} else {
// TODO: Error code otherwise?
}
}
*outWritten = numSamples * atrac->outputChannels_ * sizeof(s16);
*srcConsumed = atrac->bytesPerFrame_;
NotifyMemInfo(MemBlockFlags::WRITE, samplesAddr, bytesWritten, "AtracLowLevelDecode");
return hleLogDebug(ME, hleDelayResult(0, "low level atrac decode data", atracDecodeDelay));
}

View File

@ -75,20 +75,25 @@ public:
*outbytes = 0;
return false;
}
*inbytesConsumed = result;
if (inbytesConsumed) {
*inbytesConsumed = result;
}
outSamples_ = nb_samples;
if (nb_samples > 0) {
*outbytes = nb_samples * 2 * 2;
// Convert frame to outbuf. TODO: Very SIMDable, though hardly hot.
for (int channel = 0; channel < 2; channel++) {
int16_t *output = (int16_t *)outbuf;
for (int i = 0; i < nb_samples; i++) {
output[i * 2] = clamp16(buffers_[0][i]);
output[i * 2 + 1] = clamp16(buffers_[1][i]);
if (outbytes) {
*outbytes = nb_samples * 2 * 2;
}
if (outbuf) {
// Convert frame to outbuf. TODO: Very SIMDable, though hardly hot.
for (int channel = 0; channel < 2; channel++) {
int16_t *output = (int16_t *)outbuf;
for (int i = 0; i < nb_samples; i++) {
output[i * 2] = clamp16(buffers_[0][i]);
output[i * 2 + 1] = clamp16(buffers_[1][i]);
}
}
}
} else {
} else if (outbytes) {
*outbytes = 0;
}
return true;