mirror of
https://github.com/RPCSX/rpcsx.git
synced 2025-12-06 07:12:14 +01:00
ajm: add support of multiple input/output buffers
This commit is contained in:
parent
30f7ea2724
commit
ad56633980
|
|
@ -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 <cstdint>
|
||||
#include <cstring>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <rx/atScopeExit.hpp>
|
||||
#include <rx/hexdump.hpp>
|
||||
|
||||
extern "C" {
|
||||
#include <libatrac9/decoder.h>
|
||||
#include <libatrac9/libatrac9.h>
|
||||
#include <libavcodec/avcodec.h>
|
||||
#include <libavcodec/codec_internal.h>
|
||||
#include <libavcodec/packet.h>
|
||||
|
|
@ -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<std::byte> inputBuffer;
|
||||
orbis::uint32_t inputSize = 0;
|
||||
orbis::kvector<std::byte> 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<orbis::int32_t, Instance> instanceMap;
|
||||
orbis::ErrorCode open(orbis::Ref<orbis::File> *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<AjmDevice *>(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<InstanceDestroyArgs *>(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<std::uint32_t *>(argp)[2];
|
||||
ORBIS_LOG_ERROR(__FUNCTION__, request, arg);
|
||||
*reinterpret_cast<std::uint64_t *>(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<InstanceCreateArgs *>(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<StartBatchBufferArgs *>(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<AJMSidebandResult *>(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<AJMSidebandResult *>(runJob.pSideband);
|
||||
auto *result = reinterpret_cast<AJMSidebandResult *>(runJob.pSideband);
|
||||
result->result = 0;
|
||||
result->codecResult = 0;
|
||||
if (runJob.flags & SIDEBAND_STREAM) {
|
||||
AJMSidebandStream *stream =
|
||||
auto *stream =
|
||||
reinterpret_cast<AJMSidebandStream *>(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<AJMSidebandResult *>(runJob.pSideband);
|
||||
auto *result = reinterpret_cast<AJMSidebandResult *>(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<std::uint32_t>(
|
||||
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<orbis::uint32_t>(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<orbis::uint8_t *>(device->tempBuffer.data());
|
||||
auto *outputBuffer = reinterpret_cast<orbis::uint8_t *>(
|
||||
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<AJMSidebandStream *>(
|
||||
auto *stream = reinterpret_cast<AJMSidebandStream *>(
|
||||
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<AJMSidebandFormat *>(
|
||||
auto *format = reinterpret_cast<AJMSidebandFormat *>(
|
||||
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<AJMSidebandGaplessDecode *>(runJob.pSideband +
|
||||
currentSize);
|
||||
auto *gapless = reinterpret_cast<AJMSidebandGaplessDecode *>(
|
||||
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<AJMAt9CodecInfoSideband *>(runJob.pSideband +
|
||||
currentSize);
|
||||
auto *info = reinterpret_cast<AJMAt9CodecInfoSideband *>(
|
||||
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<AJMMP3CodecInfoSideband *>(runJob.pSideband +
|
||||
currentSize);
|
||||
auto *info = reinterpret_cast<AJMMP3CodecInfoSideband *>(
|
||||
runJob.pSideband + currentSize);
|
||||
currentSize += sizeof(AJMMP3CodecInfoSideband);
|
||||
} else if (instance.codec == AJM_CODEC_AAC) {
|
||||
// TODO
|
||||
AJMAACCodecInfoSideband *info =
|
||||
reinterpret_cast<AJMAACCodecInfoSideband *>(runJob.pSideband +
|
||||
currentSize);
|
||||
auto *info = reinterpret_cast<AJMAACCodecInfoSideband *>(
|
||||
runJob.pSideband + currentSize);
|
||||
currentSize += sizeof(AJMAACCodecInfoSideband);
|
||||
}
|
||||
}
|
||||
|
||||
if (runJob.flags & RUN_MULTIPLE_FRAMES) {
|
||||
// ORBIS_LOG_TODO("RUN_MULTIPLE_FRAMES", framesProcessed);
|
||||
AJMSidebandMultipleFrames *multipleFrames =
|
||||
reinterpret_cast<AJMSidebandMultipleFrames *>(runJob.pSideband +
|
||||
currentSize);
|
||||
auto *multipleFrames = reinterpret_cast<AJMSidebandMultipleFrames *>(
|
||||
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<orbis::File> *file,
|
|||
newFile->ops = &fileOps;
|
||||
newFile->device = this;
|
||||
|
||||
inputBuffer.reserve(32 * 1024);
|
||||
tempBuffer.reserve(32 * 1024);
|
||||
|
||||
*file = newFile;
|
||||
return {};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 <cstdint>
|
||||
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<std::byte> pSidebandInput;
|
||||
orbis::uint32_t flagsHi;
|
||||
orbis::uint32_t flagsLo;
|
||||
orbis::uint32_t commandId;
|
||||
orbis::uint32_t sidebandOutputSize;
|
||||
std::byte *pSidebandOutput;
|
||||
orbis::ptr<std::byte> 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<std::byte> 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<std::byte> pOutput;
|
||||
};
|
||||
static_assert(sizeof(BatchJobOutputBufferRa) == 0x10);
|
||||
|
||||
struct BatchJobSidebandBufferRa {
|
||||
orbis::uint32_t opcode;
|
||||
orbis::uint32_t sidebandSize;
|
||||
std::byte *pSideband;
|
||||
orbis::ptr<std::byte> pSideband;
|
||||
};
|
||||
static_assert(sizeof(BatchJobSidebandBufferRa) == 0x10);
|
||||
|
||||
|
||||
struct AjmOutputBuffer {
|
||||
orbis::ptr<std::byte> 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<AjmOutputBuffer> outputBuffers;
|
||||
orbis::ptr<std::byte> 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<uint16_t>(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<std::byte> inputBuffer;
|
||||
orbis::kvector<std::byte> 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,
|
||||
};
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in a new issue