diff --git a/rpcsx/iodev/ajm.cpp b/rpcsx/iodev/ajm.cpp index 2ed668349..ad77ec6ac 100644 --- a/rpcsx/iodev/ajm.cpp +++ b/rpcsx/iodev/ajm.cpp @@ -1,6 +1,5 @@ #include "ajm.hpp" #include "io-device.hpp" -#include "libatrac9/libatrac9.h" #include "orbis-config.hpp" #include "orbis/KernelAllocator.hpp" #include "orbis/file.hpp" @@ -8,12 +7,12 @@ #include "orbis/utils/Logs.hpp" #include #include -#include -#include #include #include + extern "C" { #include +#include #include #include #include @@ -26,19 +25,16 @@ extern "C" { struct AjmFile : orbis::File {}; -struct AjmDevice : IoDevice { - // TODO: remove when moving input and temp buffers to instance - orbis::shared_mutex mtx; - // TODO: move to intsance - orbis::kvector inputBuffer; - orbis::uint32_t inputSize = 0; - orbis::kvector tempBuffer; +enum { + AJM_RESULT_PARTIAL_INPUT = 0x8, + AJM_RESULT_NOT_ENOUGH_ROOM = 0x10, +}; +struct AjmDevice : IoDevice { + orbis::shared_mutex mtx; orbis::uint32_t batchId = 1; // temp - orbis::uint32_t at9InstanceId = 0; - orbis::uint32_t mp3InstanceId = 0; - orbis::uint32_t aacInstanceId = 0; + orbis::uint32_t instanceIds[AJM_CODEC_COUNT]{}; orbis::uint32_t unimplementedInstanceId = 0; orbis::kmap instanceMap; orbis::ErrorCode open(orbis::Ref *file, const char *path, @@ -101,7 +97,6 @@ static orbis::ErrorCode ajm_ioctl(orbis::File *file, std::uint64_t request, void *argp, orbis::Thread *thread) { auto device = static_cast(file->device.get()); - std::lock_guard lock(device->mtx); // 0xc0288900 - finalize // 0xc0288903 - module register // 0xc0288904 - module unregister @@ -119,25 +114,21 @@ static orbis::ErrorCode ajm_ioctl(orbis::File *file, std::uint64_t request, orbis::uint32_t instanceId; }; auto args = reinterpret_cast(argp); - auto it = device->instanceMap.find(args->instanceId); - if (it != device->instanceMap.end()) { - auto &instance = device->instanceMap[args->instanceId]; - if (instance.resampler) { - swr_free(&instance.resampler); - } - if (instance.codecCtx) { - avcodec_free_context(&instance.codecCtx); - } - device->instanceMap.erase(it); + if (device->instanceMap.erase(args->instanceId) == 0) { + return orbis::ErrorCode::INVAL; } + args->result = 0; + return {}; } + if (request == 0xc0288903 || request == 0xc0288904 || request == 0xc0288900) { auto arg = reinterpret_cast(argp)[2]; ORBIS_LOG_ERROR(__FUNCTION__, request, arg); *reinterpret_cast(argp) = 0; - // return{}; + return {}; } + if (request == 0xc0288905) { struct InstanceCreateArgs { orbis::uint32_t result; @@ -146,19 +137,18 @@ static orbis::ErrorCode ajm_ioctl(orbis::File *file, std::uint64_t request, orbis::uint32_t codec; orbis::uint32_t instanceId; }; + auto args = reinterpret_cast(argp); - AJMCodecs codecId = AJMCodecs(args->codec); + auto codecId = AJMCodecs(args->codec); auto codecOffset = codecId << 0xe; - if (codecId >= 0 && codecId <= 2) { + if (codecId < AJM_CODEC_COUNT) { args->result = 0; - if (codecId == AJM_CODEC_At9) { - args->instanceId = codecOffset + device->at9InstanceId++; - } else if (codecId == AJM_CODEC_MP3) { - args->instanceId = codecOffset + device->mp3InstanceId++; - } else if (codecId == AJM_CODEC_AAC) { - args->instanceId = codecOffset + device->aacInstanceId++; - } - Instance instance; + args->instanceId = codecOffset + device->instanceIds[codecId]++; + + auto [it, inserted] = device->instanceMap.try_emplace(args->instanceId); + + assert(inserted); + auto &instance = it->second; instance.codec = codecId; instance.maxChannels = AJMChannels(((args->flags & ~7) & (0xFF & ~0b11)) >> 3); @@ -172,9 +162,12 @@ static orbis::ErrorCode ajm_ioctl(orbis::File *file, std::uint64_t request, } else if (instance.outputFormat == AJM_FORMAT_FLOAT) { instance.at9.outputFormat = kAtrac9FormatF32; } else { - // TODO: throw error + ORBIS_LOG_FATAL("Unexpected AT9 output format", + (std::uint32_t)instance.outputFormat); + return orbis::ErrorCode::INVAL; } } + if (codecId == AJM_CODEC_AAC || codecId == AJM_CODEC_MP3) { const AVCodec *codec = avcodec_find_decoder( codecId == AJM_CODEC_AAC ? AV_CODEC_ID_AAC : AV_CODEC_ID_MP3); @@ -188,23 +181,29 @@ static orbis::ErrorCode ajm_ioctl(orbis::File *file, std::uint64_t request, std::abort(); } - if (int err = avcodec_open2(codecCtx, codec, NULL) < 0) { - ORBIS_LOG_FATAL("Could not open codec"); + if (int err = avcodec_open2(codecCtx, codec, nullptr); err < 0) { + ORBIS_LOG_FATAL("Could not open codec", err); std::abort(); } instance.codecCtx = codecCtx; } - device->instanceMap.insert({ - args->instanceId, - instance, - }); + + ORBIS_LOG_ERROR(__FUNCTION__, request, args->result, args->unk0, + args->flags, args->codec, args->instanceId, + (std::uint32_t)instance.outputFormat); + } else { args->instanceId = codecOffset + device->unimplementedInstanceId++; + ORBIS_LOG_ERROR(__FUNCTION__, request, "unimplemented codec", + args->result, args->unk0, args->flags, args->codec, + args->instanceId); } - ORBIS_LOG_ERROR(__FUNCTION__, request, args->result, args->unk0, - args->flags, args->codec, args->instanceId); - } else if (request == 0xc0288907) { + + return {}; + } + + if (request == 0xc0288907) { struct StartBatchBufferArgs { orbis::uint32_t result; orbis::uint32_t unk0; @@ -216,11 +215,10 @@ static orbis::ErrorCode ajm_ioctl(orbis::File *file, std::uint64_t request, }; auto args = reinterpret_cast(argp); args->result = 0; - args->batchId = device->batchId; + args->batchId = device->batchId++; // ORBIS_LOG_ERROR(__FUNCTION__, request, args->result, args->unk0, // args->pBatch, args->batchSize, args->priority, // args->batchError, args->batchId); - device->batchId += 1; // thread->where(); auto ptr = args->pBatch; @@ -233,8 +231,9 @@ static orbis::ErrorCode ajm_ioctl(orbis::File *file, std::uint64_t request, auto endJobPtr = ptr + header->len; // TODO: handle unimplemented codecs, so auto create instance for now auto &instance = device->instanceMap[instanceId]; + + instance.inputBuffer.clear(); RunJob runJob{}; - device->inputSize = 0; while (jobPtr < endJobPtr) { auto typed = (OpcodeHeader *)jobPtr; switch (typed->getOpcode()) { @@ -248,11 +247,11 @@ static orbis::ErrorCode ajm_ioctl(orbis::File *file, std::uint64_t request, } case Opcode::ControlBufferRa: { runJob.control = true; - BatchJobControlBufferRa *ctrl = (BatchJobControlBufferRa *)jobPtr; - AJMSidebandResult *result = + auto *ctrl = (BatchJobControlBufferRa *)jobPtr; + auto *result = reinterpret_cast(ctrl->pSidebandOutput); - result->result = 0; - result->codecResult = 0; + *result = {}; + ORBIS_LOG_ERROR(__FUNCTION__, request, "control buffer", ctrl->opcode, ctrl->commandId, ctrl->flagsHi, ctrl->flagsLo, ctrl->sidebandInputSize, ctrl->sidebandOutputSize); @@ -269,8 +268,7 @@ static orbis::ErrorCode ajm_ioctl(orbis::File *file, std::uint64_t request, orbis::uint32_t configData; orbis::int32_t unk0[2]; }; - InitalizeBuffer *initializeBuffer = - (InitalizeBuffer *)ctrl->pSidebandInput; + auto *initializeBuffer = (InitalizeBuffer *)ctrl->pSidebandInput; instance.at9.configData = initializeBuffer->configData; reset(&instance); resetAt9(&instance); @@ -290,12 +288,11 @@ static orbis::ErrorCode ajm_ioctl(orbis::File *file, std::uint64_t request, outputChannels, initializeBuffer->configData, (orbis::uint32_t)instance.outputFormat); } else if (instance.codec == AJM_CODEC_AAC) { - struct InitalizeBuffer { + struct InitializeBuffer { orbis::uint32_t headerIndex; orbis::uint32_t sampleRateIndex; }; - InitalizeBuffer *initializeBuffer = - (InitalizeBuffer *)ctrl->pSidebandInput; + auto *initializeBuffer = (InitializeBuffer *)ctrl->pSidebandInput; instance.aac.headerType = AACHeaderType(initializeBuffer->headerIndex); instance.aac.sampleRate = @@ -303,13 +300,13 @@ static orbis::ErrorCode ajm_ioctl(orbis::File *file, std::uint64_t request, } } if (ctrl->getFlags() & SIDEBAND_GAPLESS_DECODE) { - struct InitalizeBuffer { + struct InitializeBuffer { orbis::uint32_t totalSamples; orbis::uint16_t skipSamples; orbis::uint16_t totalSkippedSamples; }; - InitalizeBuffer *initializeBuffer = - (InitalizeBuffer *)ctrl->pSidebandInput; + + auto *initializeBuffer = (InitializeBuffer *)ctrl->pSidebandInput; if (initializeBuffer->totalSamples > 0) { instance.gapless.totalSamples = initializeBuffer->totalSamples; } @@ -324,21 +321,21 @@ static orbis::ErrorCode ajm_ioctl(orbis::File *file, std::uint64_t request, break; } case Opcode::RunBufferRa: { - BatchJobInputBufferRa *job = (BatchJobInputBufferRa *)jobPtr; + auto *job = (BatchJobInputBufferRa *)jobPtr; // ORBIS_LOG_ERROR(__FUNCTION__, request, "BatchJobInputBufferRa", // job->opcode, job->szInputSize, job->pInput); - // We don't support split buffers for now, so ignore new buffers - if (device->inputSize == 0) { - std::memcpy(device->inputBuffer.data(), job->pInput, - job->szInputSize); - device->inputSize += job->szInputSize; - } + + auto offset = instance.inputBuffer.size(); + instance.inputBuffer.resize(offset + job->szInputSize); + + std::memcpy(instance.inputBuffer.data() + offset, job->pInput, + job->szInputSize); // rx::hexdump({(std::byte*) job->pInput, job->szInputSize}); jobPtr += sizeof(BatchJobInputBufferRa); break; } case Opcode::Flags: { - BatchJobFlagsRa *job = (BatchJobFlagsRa *)jobPtr; + auto *job = (BatchJobFlagsRa *)jobPtr; // ORBIS_LOG_ERROR(__FUNCTION__, request, "BatchJobFlagsRa", // job->flagsHi, job->flagsLo); runJob.flags = ((orbis::uint64_t)job->flagsHi << 0x1a) | job->flagsLo; @@ -346,16 +343,16 @@ static orbis::ErrorCode ajm_ioctl(orbis::File *file, std::uint64_t request, break; } case Opcode::JobBufferOutputRa: { - BatchJobOutputBufferRa *job = (BatchJobOutputBufferRa *)jobPtr; + auto *job = (BatchJobOutputBufferRa *)jobPtr; // ORBIS_LOG_ERROR(__FUNCTION__, request, "BatchJobOutputBufferRa", // job->opcode, job->outputSize, job->pOutput); - runJob.pOutput = job->pOutput; - runJob.outputSize = job->outputSize; + runJob.outputBuffers.push_back({job->pOutput, job->outputSize}); + runJob.totalOutputSize += job->outputSize; jobPtr += sizeof(BatchJobOutputBufferRa); break; } case Opcode::JobBufferSidebandRa: { - BatchJobSidebandBufferRa *job = (BatchJobSidebandBufferRa *)jobPtr; + auto *job = (BatchJobSidebandBufferRa *)jobPtr; // ORBIS_LOG_ERROR(__FUNCTION__, request, "BatchJobSidebandBufferRa", // job->opcode, job->sidebandSize, job->pSideband); runJob.pSideband = job->pSideband; @@ -365,70 +362,113 @@ static orbis::ErrorCode ajm_ioctl(orbis::File *file, std::uint64_t request, } default: jobPtr = endJobPtr; + break; } } ptr = jobPtr; + if (!runJob.control && instanceId >= 0xC000) { - AJMSidebandResult *result = - reinterpret_cast(runJob.pSideband); + auto *result = reinterpret_cast(runJob.pSideband); result->result = 0; result->codecResult = 0; if (runJob.flags & SIDEBAND_STREAM) { - AJMSidebandStream *stream = + auto *stream = reinterpret_cast(runJob.pSideband + 8); - stream->inputSize = device->inputSize; - stream->outputSize = runJob.outputSize; + stream->inputSize = instance.inputBuffer.size(); + stream->outputSize = runJob.totalOutputSize; } } else if (!runJob.control) { // orbis::uint32_t maxChannels = // instance.maxChannels == AJM_CHANNEL_DEFAULT ? 2 // : // instance.maxChannels; - AJMSidebandResult *result = - reinterpret_cast(runJob.pSideband); + auto *result = reinterpret_cast(runJob.pSideband); result->result = 0; result->codecResult = 0; - orbis::uint32_t inputReaded = 0; + orbis::uint32_t totalDecodedBytes = 0; orbis::uint32_t outputWritten = 0; orbis::uint32_t framesProcessed = 0; orbis::uint32_t samplesCount = 0; - if (device->inputSize != 0 && runJob.outputSize != 0) { + if (!instance.inputBuffer.empty() && runJob.totalOutputSize != 0) { + instance.inputBuffer.reserve(instance.inputBuffer.size() + + AV_INPUT_BUFFER_PADDING_SIZE); + + AVPacket *pkt = av_packet_alloc(); + rx::atScopeExit _free_pkt([&] { av_packet_free(&pkt); }); + do { if (instance.codec == AJM_CODEC_At9 && instance.at9.frameSamples == 0) { break; } - if (inputReaded >= device->inputSize || - outputWritten >= runJob.outputSize) { + if (totalDecodedBytes >= instance.inputBuffer.size() || + outputWritten >= runJob.totalOutputSize) { break; } AVFrame *frame = av_frame_alloc(); rx::atScopeExit _free_frame([&] { av_frame_free(&frame); }); - orbis::uint32_t readed = 0; - orbis::uint32_t outputBufferSize = 0; + std::uint32_t inputFrameSize = 0; + std::uint32_t outputBufferSize = 0; + if (instance.codec == AJM_CODEC_At9) { - orbis::int32_t bytesUsed = 0; + inputFrameSize = 4; outputBufferSize = av_samples_get_buffer_size( nullptr, instance.at9.inputChannels, instance.at9.frameSamples, ajmToAvFormat(instance.outputFormat), 0); - int err = Atrac9Decode(instance.at9.handle, - &device->inputBuffer[inputReaded], - device->tempBuffer.data(), - instance.at9.outputFormat, &bytesUsed); + } else if (instance.codec == AJM_CODEC_MP3) { + inputFrameSize = get_mp3_data_size( + (orbis::uint8_t *)(instance.inputBuffer.data() + + totalDecodedBytes)); + if (inputFrameSize == 0) { + inputFrameSize = + instance.inputBuffer.size() - totalDecodedBytes; + } else { + inputFrameSize = std::min( + inputFrameSize, + instance.inputBuffer.size() - totalDecodedBytes); + } + } else if (instance.codec == AJM_CODEC_AAC) { + inputFrameSize = instance.inputBuffer.size() - totalDecodedBytes; + } + + if (inputFrameSize > + instance.inputBuffer.size() - totalDecodedBytes) { + result->result = AJM_RESULT_PARTIAL_INPUT; + break; + } + + if (outputBufferSize > runJob.totalOutputSize - outputWritten) { + result->result = AJM_RESULT_NOT_ENOUGH_ROOM; + break; + } + + pkt->data = (orbis::uint8_t *)(instance.inputBuffer.data() + + totalDecodedBytes); + pkt->size = inputFrameSize; + + if (instance.codec == AJM_CODEC_At9) { + orbis::int32_t bytesUsed = 0; + instance.outputBuffer.resize(outputBufferSize); + int err = + Atrac9Decode(instance.at9.handle, + instance.inputBuffer.data() + totalDecodedBytes, + instance.outputBuffer.data(), + instance.at9.outputFormat, &bytesUsed); if (err != ERR_SUCCESS) { - rx::hexdump({(std::byte *)&device->inputBuffer[inputReaded], - device->inputSize}); + rx::hexdump( + std::span(instance.inputBuffer).subspan(totalDecodedBytes)); ORBIS_LOG_FATAL("Could not decode frame", err, instance.at9.estimatedSizeUsed, instance.at9.superFrameSize, instance.at9.frameSamples, instance.at9.handle, - inputReaded, outputWritten); + totalDecodedBytes, outputWritten); std::abort(); } + instance.at9.estimatedSizeUsed = static_cast(bytesUsed); instance.at9.superFrameDataLeft -= bytesUsed; @@ -441,7 +481,7 @@ static orbis::ErrorCode ajm_ioctl(orbis::File *file, std::uint64_t request, instance.at9.superFrameDataLeft = instance.at9.superFrameSize; } samplesCount = instance.at9.frameSamples; - readed = instance.at9.estimatedSizeUsed; + inputFrameSize = instance.at9.estimatedSizeUsed; instance.lastDecode.channels = AJMChannels(instance.at9.inputChannels); instance.lastDecode.sampleRate = instance.at9.sampleRate; @@ -450,17 +490,6 @@ static orbis::ErrorCode ajm_ioctl(orbis::File *file, std::uint64_t request, // instance.at9.superFrameDataIdx, // instance.at9.framesInSuperframe); } else if (instance.codec == AJM_CODEC_MP3) { - auto frameSize = get_mp3_data_size( - (orbis::uint8_t *)(&device->inputBuffer[inputReaded])); - if (frameSize == 0) { - frameSize = device->inputSize; - } else { - frameSize = std::min(frameSize, device->inputSize); - } - AVPacket *pkt = av_packet_alloc(); - rx::atScopeExit _free_pkt([&] { av_packet_free(&pkt); }); - pkt->data = (orbis::uint8_t *)(&device->inputBuffer[inputReaded]); - pkt->size = frameSize; int ret = avcodec_send_packet(instance.codecCtx, pkt); if (ret < 0) { ORBIS_LOG_FATAL("Error sending packet for decoding", ret); @@ -474,17 +503,12 @@ static orbis::ErrorCode ajm_ioctl(orbis::File *file, std::uint64_t request, outputBufferSize = av_samples_get_buffer_size( nullptr, frame->ch_layout.nb_channels, frame->nb_samples, ajmToAvFormat(instance.outputFormat), 0); + samplesCount = frame->nb_samples; - readed = frameSize; instance.lastDecode.channels = AJMChannels(frame->ch_layout.nb_channels); instance.lastDecode.sampleRate = frame->sample_rate; } else if (instance.codec == AJM_CODEC_AAC) { - AVPacket *pkt = av_packet_alloc(); - rx::atScopeExit _free_pkt([&] { av_packet_free(&pkt); }); - pkt->data = (orbis::uint8_t *)(&device->inputBuffer[inputReaded]); - pkt->size = device->inputSize; - // HACK: to avoid writing a bunch of useless calls // we simply call this method directly (but it can be very // unstable) @@ -500,13 +524,25 @@ static orbis::ErrorCode ajm_ioctl(orbis::File *file, std::uint64_t request, nullptr, frame->ch_layout.nb_channels, frame->nb_samples, ajmToAvFormat(instance.outputFormat), 0); samplesCount = frame->nb_samples; - readed = len; + inputFrameSize = len; instance.lastDecode.channels = AJMChannels(frame->ch_layout.nb_channels); instance.lastDecode.sampleRate = frame->sample_rate; } - framesProcessed += 1; - inputReaded += readed; + + if (inputFrameSize > + instance.inputBuffer.size() - totalDecodedBytes) { + result->result = AJM_RESULT_PARTIAL_INPUT; + break; + } + + if (outputBufferSize > runJob.totalOutputSize - outputWritten) { + result->result = AJM_RESULT_NOT_ENOUGH_ROOM; + break; + } + + framesProcessed++; + totalDecodedBytes += inputFrameSize; if (instance.gapless.skipSamples > 0 || instance.gapless.totalSamples > 0) { @@ -521,6 +557,8 @@ static orbis::ErrorCode ajm_ioctl(orbis::File *file, std::uint64_t request, // and mp3 support sample rate resample (TODO), so made resampling // with swr if (instance.codec != AJM_CODEC_At9) { + instance.outputBuffer.resize(outputBufferSize); + auto resampler = swr_alloc(); AVChannelLayout chLayout; av_channel_layout_default(&chLayout, @@ -539,8 +577,8 @@ static orbis::ErrorCode ajm_ioctl(orbis::File *file, std::uint64_t request, ORBIS_LOG_FATAL("Failed to initialize the resampling context"); std::abort(); } - orbis::uint8_t *outputBuffer = - reinterpret_cast(device->tempBuffer.data()); + auto *outputBuffer = reinterpret_cast( + instance.outputBuffer.data()); int nb_samples = swr_convert(resampler, &outputBuffer, frame->nb_samples, (const orbis::uint8_t **)frame->extended_data, @@ -550,9 +588,31 @@ static orbis::ErrorCode ajm_ioctl(orbis::File *file, std::uint64_t request, std::abort(); } } - memcpy(runJob.pOutput + outputWritten, device->tempBuffer.data(), - outputBufferSize); - outputWritten += outputBufferSize; + + std::uint32_t bufferOutputWritten = 0; + for (std::size_t bufferOffset = 0; + auto buffer : runJob.outputBuffers) { + if (bufferOffset <= outputWritten) { + auto byteOffset = outputWritten - bufferOffset; + auto size = std::min(buffer.size - byteOffset, + instance.outputBuffer.size() - + bufferOutputWritten); + + ORBIS_RET_ON_ERROR(orbis::uwrite( + buffer.pOutput + byteOffset, + instance.outputBuffer.data() + bufferOutputWritten, size)); + + bufferOutputWritten += size; + outputWritten += size; + + if (bufferOutputWritten >= instance.outputBuffer.size()) { + break; + } + } + + bufferOffset += buffer.size; + } + instance.processedSamples += samplesCount; } while ((runJob.flags & RUN_MULTIPLE_FRAMES) != 0); } @@ -562,9 +622,9 @@ static orbis::ErrorCode ajm_ioctl(orbis::File *file, std::uint64_t request, if (runJob.flags & SIDEBAND_STREAM) { // ORBIS_LOG_TODO("SIDEBAND_STREAM", currentSize, inputReaded, // outputWritten, instance.processedSamples); - AJMSidebandStream *stream = reinterpret_cast( + auto *stream = reinterpret_cast( runJob.pSideband + currentSize); - stream->inputSize = inputReaded; + stream->inputSize = totalDecodedBytes; stream->outputSize = outputWritten; stream->decodedSamples = instance.processedSamples; currentSize += sizeof(AJMSidebandStream); @@ -572,7 +632,7 @@ static orbis::ErrorCode ajm_ioctl(orbis::File *file, std::uint64_t request, if (runJob.flags & SIDEBAND_FORMAT) { // ORBIS_LOG_TODO("SIDEBAND_FORMAT", currentSize); - AJMSidebandFormat *format = reinterpret_cast( + auto *format = reinterpret_cast( runJob.pSideband + currentSize); format->channels = AJMChannels(instance.lastDecode.channels); format->sampleRate = instance.lastDecode.sampleRate; @@ -583,9 +643,8 @@ static orbis::ErrorCode ajm_ioctl(orbis::File *file, std::uint64_t request, if (runJob.flags & SIDEBAND_GAPLESS_DECODE) { // ORBIS_LOG_TODO("SIDEBAND_GAPLESS_DECODE", currentSize); - AJMSidebandGaplessDecode *gapless = - reinterpret_cast(runJob.pSideband + - currentSize); + auto *gapless = reinterpret_cast( + runJob.pSideband + currentSize); gapless->skipSamples = instance.gapless.skipSamples; gapless->totalSamples = instance.gapless.totalSamples; gapless->totalSkippedSamples = instance.gapless.totalSkippedSamples; @@ -595,39 +654,39 @@ static orbis::ErrorCode ajm_ioctl(orbis::File *file, std::uint64_t request, if (runJob.flags & RUN_GET_CODEC_INFO) { // ORBIS_LOG_TODO("RUN_GET_CODEC_INFO"); if (instance.codec == AJM_CODEC_At9) { - AJMAt9CodecInfoSideband *info = - reinterpret_cast(runJob.pSideband + - currentSize); + auto *info = reinterpret_cast( + runJob.pSideband + currentSize); info->superFrameSize = instance.at9.superFrameSize; info->framesInSuperFrame = instance.at9.framesInSuperframe; info->frameSamples = instance.at9.frameSamples; currentSize += sizeof(AJMAt9CodecInfoSideband); } else if (instance.codec == AJM_CODEC_MP3) { // TODO - AJMMP3CodecInfoSideband *info = - reinterpret_cast(runJob.pSideband + - currentSize); + auto *info = reinterpret_cast( + runJob.pSideband + currentSize); currentSize += sizeof(AJMMP3CodecInfoSideband); } else if (instance.codec == AJM_CODEC_AAC) { // TODO - AJMAACCodecInfoSideband *info = - reinterpret_cast(runJob.pSideband + - currentSize); + auto *info = reinterpret_cast( + runJob.pSideband + currentSize); currentSize += sizeof(AJMAACCodecInfoSideband); } } if (runJob.flags & RUN_MULTIPLE_FRAMES) { // ORBIS_LOG_TODO("RUN_MULTIPLE_FRAMES", framesProcessed); - AJMSidebandMultipleFrames *multipleFrames = - reinterpret_cast(runJob.pSideband + - currentSize); + auto *multipleFrames = reinterpret_cast( + runJob.pSideband + currentSize); multipleFrames->framesProcessed = framesProcessed; currentSize += sizeof(AJMSidebandMultipleFrames); } } } - } else if (request == 0xc0288908) { + + return {}; + } + + if (request == 0xc0288908) { struct Args { orbis::uint32_t unk0; orbis::uint32_t unk1; @@ -640,10 +699,11 @@ static orbis::ErrorCode ajm_ioctl(orbis::File *file, std::uint64_t request, // ORBIS_LOG_ERROR(__FUNCTION__, request, args->unk0, args->unk1, // args->batchId, args->timeout, args->batchError); // thread->where(); - } else { - ORBIS_LOG_FATAL("Unhandled AJM ioctl", request); - thread->where(); + return {}; } + + ORBIS_LOG_FATAL("Unhandled AJM ioctl", request); + thread->where(); return {}; } @@ -658,9 +718,6 @@ orbis::ErrorCode AjmDevice::open(orbis::Ref *file, newFile->ops = &fileOps; newFile->device = this; - inputBuffer.reserve(32 * 1024); - tempBuffer.reserve(32 * 1024); - *file = newFile; return {}; } diff --git a/rpcsx/iodev/ajm.hpp b/rpcsx/iodev/ajm.hpp index 8ec3a7a65..53857dcc8 100644 --- a/rpcsx/iodev/ajm.hpp +++ b/rpcsx/iodev/ajm.hpp @@ -2,6 +2,8 @@ #include "libatrac9/libatrac9.h" #include "orbis-config.hpp" +#include "orbis/KernelAllocator.hpp" +#include "orbis/utils/SharedMutex.hpp" // #include "orbis/utils/Logs.hpp" #include extern "C" { @@ -50,12 +52,12 @@ static_assert(sizeof(ReturnAddress) == 0x10); struct BatchJobControlBufferRa { orbis::uint32_t opcode; orbis::uint32_t sidebandInputSize; - std::byte *pSidebandInput; + orbis::ptr pSidebandInput; orbis::uint32_t flagsHi; orbis::uint32_t flagsLo; orbis::uint32_t commandId; orbis::uint32_t sidebandOutputSize; - std::byte *pSidebandOutput; + orbis::ptr pSidebandOutput; std::uint64_t getFlags() { return ((uint64_t)flagsHi << 0x1a) | flagsLo; } }; @@ -64,7 +66,7 @@ static_assert(sizeof(BatchJobControlBufferRa) == 0x28); struct BatchJobInputBufferRa { orbis::uint32_t opcode; orbis::uint32_t szInputSize; - std::byte *pInput; + orbis::ptr pInput; }; static_assert(sizeof(BatchJobInputBufferRa) == 0x10); @@ -78,36 +80,41 @@ static_assert(sizeof(BatchJobFlagsRa) == 0x8); struct BatchJobOutputBufferRa { orbis::uint32_t opcode; orbis::uint32_t outputSize; - std::byte *pOutput; + orbis::ptr pOutput; }; static_assert(sizeof(BatchJobOutputBufferRa) == 0x10); struct BatchJobSidebandBufferRa { orbis::uint32_t opcode; orbis::uint32_t sidebandSize; - std::byte *pSideband; + orbis::ptr pSideband; }; static_assert(sizeof(BatchJobSidebandBufferRa) == 0x10); + +struct AjmOutputBuffer { + orbis::ptr pOutput; + orbis::size_t size; +}; struct RunJob { - orbis::uint64_t flags; - orbis::uint32_t outputSize; - std::byte *pOutput; - orbis::uint32_t sidebandSize; - std::byte *pSideband; + std::uint64_t flags; + std::uint32_t sidebandSize; + std::uint32_t totalOutputSize; + std::vector outputBuffers; + orbis::ptr pSideband; bool control; }; // Thanks to mystical SirNickity with 1 post // https://hydrogenaud.io/index.php?topic=85125.msg747716#msg747716 -const uint8_t mpeg_versions[4] = {25, 0, 2, 1}; +inline constexpr uint8_t mpeg_versions[4] = {25, 0, 2, 1}; // Layers - use [layer] -const uint8_t mpeg_layers[4] = {0, 3, 2, 1}; +inline constexpr uint8_t mpeg_layers[4] = {0, 3, 2, 1}; // Bitrates - use [version][layer][bitrate] -const uint16_t mpeg_bitrates[4][4][16] = { +inline constexpr uint16_t mpeg_bitrates[4][4][16] = { { // Version 2.5 {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, // Reserved @@ -147,7 +154,7 @@ const uint16_t mpeg_bitrates[4][4][16] = { }}; // Sample rates - use [version][srate] -const uint16_t mpeg_srates[4][4] = { +inline constexpr uint16_t mpeg_srates[4][4] = { {11025, 12000, 8000, 0}, // MPEG 2.5 {0, 0, 0, 0}, // Reserved {22050, 24000, 16000, 0}, // MPEG 2 @@ -155,7 +162,7 @@ const uint16_t mpeg_srates[4][4] = { }; // Samples per frame - use [version][layer] -const uint16_t mpeg_frame_samples[4][4] = { +inline constexpr uint16_t mpeg_frame_samples[4][4] = { // Rsvd 3 2 1 < Layer v Version {0, 576, 1152, 384}, // 2.5 {0, 0, 0, 0}, // Reserved @@ -164,9 +171,9 @@ const uint16_t mpeg_frame_samples[4][4] = { }; // Slot size (MPEG unit of measurement) - use [layer] -const uint8_t mpeg_slot_size[4] = {0, 1, 1, 4}; // Rsvd, 3, 2, 1 +inline constexpr uint8_t mpeg_slot_size[4] = {0, 1, 1, 4}; // Rsvd, 3, 2, 1 -uint32_t get_mp3_data_size(const uint8_t *data) { +constexpr uint32_t get_mp3_data_size(const uint8_t *data) { // Quick validity check if (((data[0] & 0xFF) != 0xFF) || ((data[1] & 0xE0) != 0xE0) // 3 sync bits || ((data[1] & 0x18) == 0x08) // Version rsvd @@ -196,8 +203,8 @@ uint32_t get_mp3_data_size(const uint8_t *data) { ((pad) ? slot_size : 0); // ORBIS_LOG_TODO(__FUNCTION__, (uint16_t)ver, (uint16_t)lyr, - // (uint16_t)pad, (uint16_t)brx, (uint16_t)srx, bitrate, samprate, - // samples, (uint16_t)slot_size, bps, fsize, + // (uint16_t)pad, (uint16_t)brx, (uint16_t)srx, bitrate, + // samprate, samples, (uint16_t)slot_size, bps, fsize, // static_cast(fsize)); // Frame sizes are truncated integers @@ -206,13 +213,16 @@ uint32_t get_mp3_data_size(const uint8_t *data) { enum AACHeaderType { AAC_ADTS = 1, AAC_RAW = 2 }; -orbis::uint32_t AACFreq[12] = {96000, 88200, 64000, 48000, 44100, 32000, - 24000, 22050, 16000, 12000, 11025, 8000}; +inline constexpr orbis::uint32_t AACFreq[12] = {96000, 88200, 64000, 48000, + 44100, 32000, 24000, 22050, + 16000, 12000, 11025, 8000}; enum AJMCodecs : orbis::uint32_t { AJM_CODEC_MP3 = 0, AJM_CODEC_At9 = 1, AJM_CODEC_AAC = 2, + + AJM_CODEC_COUNT }; enum AJMChannels : orbis::uint32_t { @@ -244,6 +254,12 @@ struct At9Instance { orbis::uint32_t sampleRate{}; Atrac9Format outputFormat{}; orbis::uint32_t configData; + + ~At9Instance() { + if (handle) { + Atrac9ReleaseHandle(handle); + } + } }; struct AACInstance { @@ -308,11 +324,15 @@ struct AJMAACCodecInfoSideband { }; struct Instance { + orbis::shared_mutex mtx; AJMCodecs codec; AJMChannels maxChannels; AJMFormat outputFormat; At9Instance at9; AACInstance aac; + orbis::kvector inputBuffer; + orbis::kvector outputBuffer; + AVCodecContext *codecCtx; SwrContext *resampler; orbis::uint32_t lastBatchId; @@ -320,6 +340,15 @@ struct Instance { AJMSidebandGaplessDecode gapless; orbis::uint32_t processedSamples; AJMSidebandFormat lastDecode; + + ~Instance() { + if (resampler) { + swr_free(&resampler); + } + if (codecCtx) { + avcodec_free_context(&codecCtx); + } + } }; enum ControlFlags { @@ -336,4 +365,4 @@ enum SidebandFlags { SIDEBAND_STREAM = 0x800000000000, SIDEBAND_FORMAT = 0x400000000000, SIDEBAND_GAPLESS_DECODE = 0x200000000000, -}; \ No newline at end of file +};