mirror of
https://github.com/RPCSX/rpcsx.git
synced 2025-12-06 07:12:14 +01:00
rpcsx: ajm decode draft
This commit is contained in:
parent
5ce8d5147a
commit
0a898a507a
2
3rdparty/CMakeLists.txt
vendored
2
3rdparty/CMakeLists.txt
vendored
|
|
@ -36,7 +36,7 @@ add_subdirectory(LibAtrac9)
|
||||||
set(FFMPEG_PATH ${CMAKE_CURRENT_SOURCE_DIR}/FFmpeg)
|
set(FFMPEG_PATH ${CMAKE_CURRENT_SOURCE_DIR}/FFmpeg)
|
||||||
add_custom_command(
|
add_custom_command(
|
||||||
OUTPUT ${FFMPEG_PATH}/config.h
|
OUTPUT ${FFMPEG_PATH}/config.h
|
||||||
COMMAND ./configure
|
COMMAND ./configure --disable-libdrm --disable-vaapi --disable-vdpau --disable-zlib --disable-lzma
|
||||||
COMMENT "Configuring FFmpeg..."
|
COMMENT "Configuring FFmpeg..."
|
||||||
WORKING_DIRECTORY ${FFMPEG_PATH}
|
WORKING_DIRECTORY ${FFMPEG_PATH}
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -78,6 +78,7 @@ PUBLIC
|
||||||
ffmpeg::avcodec
|
ffmpeg::avcodec
|
||||||
ffmpeg::swresample
|
ffmpeg::swresample
|
||||||
ffmpeg::avutil
|
ffmpeg::avutil
|
||||||
|
Atrac9
|
||||||
rpcsx-gpu
|
rpcsx-gpu
|
||||||
orbis::kernel
|
orbis::kernel
|
||||||
rx
|
rx
|
||||||
|
|
|
||||||
|
|
@ -1,25 +1,661 @@
|
||||||
|
#include "ajm.hpp"
|
||||||
#include "io-device.hpp"
|
#include "io-device.hpp"
|
||||||
|
#include "libatrac9/libatrac9.h"
|
||||||
|
#include "orbis-config.hpp"
|
||||||
#include "orbis/KernelAllocator.hpp"
|
#include "orbis/KernelAllocator.hpp"
|
||||||
#include "orbis/file.hpp"
|
#include "orbis/file.hpp"
|
||||||
#include "orbis/thread/Thread.hpp"
|
#include "orbis/thread/Thread.hpp"
|
||||||
#include "orbis/utils/Logs.hpp"
|
#include "orbis/utils/Logs.hpp"
|
||||||
|
#include <cstdint>
|
||||||
|
#include <map>
|
||||||
|
#include <rx/hexdump.hpp>
|
||||||
|
extern "C" {
|
||||||
|
#include <libatrac9/decoder.h>
|
||||||
|
#include <libavcodec/avcodec.h>
|
||||||
|
#include <libavcodec/codec_internal.h>
|
||||||
|
#include <libavcodec/packet.h>
|
||||||
|
#include <libavformat/avformat.h>
|
||||||
|
#include <libavutil/mem.h>
|
||||||
|
#include <libavutil/opt.h>
|
||||||
|
#include <libavutil/samplefmt.h>
|
||||||
|
#include <libswresample/swresample.h>
|
||||||
|
}
|
||||||
|
|
||||||
struct AjmFile : orbis::File {};
|
struct AjmFile : orbis::File {};
|
||||||
|
|
||||||
|
uint batchId = 1;
|
||||||
|
|
||||||
|
orbis::uint32_t at9InstanceId = 0;
|
||||||
|
orbis::uint32_t mp3InstanceId = 0;
|
||||||
|
orbis::uint32_t aacInstanceId = 0;
|
||||||
|
orbis::uint32_t unimplementedInstanceId = 0;
|
||||||
|
std::map<orbis::int32_t, Instance> instanceMap;
|
||||||
|
|
||||||
|
AVSampleFormat ajmToAvFormat(AJMFormat ajmFormat) {
|
||||||
|
switch (ajmFormat) {
|
||||||
|
case AJM_FORMAT_S16:
|
||||||
|
return AV_SAMPLE_FMT_S16;
|
||||||
|
case AJM_FORMAT_S32:
|
||||||
|
return AV_SAMPLE_FMT_S32;
|
||||||
|
case AJM_FORMAT_FLOAT:
|
||||||
|
return AV_SAMPLE_FMT_FLTP;
|
||||||
|
default:
|
||||||
|
return AV_SAMPLE_FMT_NONE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static orbis::ErrorCode ajm_ioctl(orbis::File *file, std::uint64_t request,
|
static orbis::ErrorCode ajm_ioctl(orbis::File *file, std::uint64_t request,
|
||||||
void *argp, orbis::Thread *thread) {
|
void *argp, orbis::Thread *thread) {
|
||||||
|
|
||||||
|
// 0xc0288900 - finalize
|
||||||
// 0xc0288903 - module register
|
// 0xc0288903 - module register
|
||||||
// 0xc0288904 - module unregister
|
// 0xc0288904 - module unregister
|
||||||
if (request == 0xc0288903 || request == 0xc0288904) {
|
// 0xc0288905 - instance create
|
||||||
|
// 0xc0288906 - instance destroy
|
||||||
|
// 0xc028890a - instance extend
|
||||||
|
// 0xc028890b - intasnce switch
|
||||||
|
// 0xc0288907 - start batch buffer
|
||||||
|
// 0xc0288908 - wait batch buffer
|
||||||
|
// 0xc0288900 - unregister context
|
||||||
|
if (request == 0xc0288906) {
|
||||||
|
struct InstanceDestroyArgs {
|
||||||
|
orbis::uint32_t result;
|
||||||
|
orbis::uint32_t unk0;
|
||||||
|
orbis::uint32_t instanceId;
|
||||||
|
};
|
||||||
|
auto args = reinterpret_cast<InstanceDestroyArgs *>(argp);
|
||||||
|
auto it = instanceMap.find(args->instanceId);
|
||||||
|
if (it != instanceMap.end()) {
|
||||||
|
auto &instance = instanceMap[args->instanceId];
|
||||||
|
if (instance.resampler) {
|
||||||
|
swr_free(&instance.resampler);
|
||||||
|
avcodec_free_context(&instance.codecCtx);
|
||||||
|
}
|
||||||
|
instanceMap.erase(args->instanceId);
|
||||||
|
}
|
||||||
|
args->result = 0;
|
||||||
|
}
|
||||||
|
if (request == 0xc0288903 || request == 0xc0288904 || request == 0xc0288900) {
|
||||||
auto arg = reinterpret_cast<std::uint32_t *>(argp)[2];
|
auto arg = reinterpret_cast<std::uint32_t *>(argp)[2];
|
||||||
ORBIS_LOG_ERROR(__FUNCTION__, request, arg);
|
ORBIS_LOG_ERROR(__FUNCTION__, request, arg);
|
||||||
*reinterpret_cast<std::uint64_t *>(argp) = 0;
|
*reinterpret_cast<std::uint64_t *>(argp) = 0;
|
||||||
// return{};
|
// return{};
|
||||||
}
|
}
|
||||||
|
if (request == 0xc0288905) {
|
||||||
|
struct InstanceCreateArgs {
|
||||||
|
orbis::uint32_t result;
|
||||||
|
orbis::uint32_t unk0;
|
||||||
|
orbis::uint64_t flags;
|
||||||
|
orbis::uint32_t codec;
|
||||||
|
orbis::uint32_t instanceId;
|
||||||
|
};
|
||||||
|
auto args = reinterpret_cast<InstanceCreateArgs *>(argp);
|
||||||
|
AJMCodecs codecId = AJMCodecs(args->codec);
|
||||||
|
auto codecOffset = codecId << 0xe;
|
||||||
|
if (codecId >= 0 && codecId <= 2) {
|
||||||
|
args->result = 0;
|
||||||
|
if (codecId == AJM_CODEC_At9) {
|
||||||
|
args->instanceId = codecOffset + at9InstanceId++;
|
||||||
|
} else if (codecId == AJM_CODEC_MP3) {
|
||||||
|
args->instanceId = codecOffset + mp3InstanceId++;
|
||||||
|
} else if (codecId == AJM_CODEC_AAC) {
|
||||||
|
args->instanceId = codecOffset + aacInstanceId++;
|
||||||
|
}
|
||||||
|
Instance instance;
|
||||||
|
instance.codec = codecId;
|
||||||
|
instance.outputChannels =
|
||||||
|
AJMChannels(((args->flags & ~7) & (0xFF & ~0b11)) >> 3);
|
||||||
|
instance.outputFormat = AJMFormat((args->flags & ~7) & 0b11);
|
||||||
|
if (codecId == AJM_CODEC_At9) {
|
||||||
|
instance.at9.handle = Atrac9GetHandle();
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
if (!codec) {
|
||||||
|
ORBIS_LOG_FATAL("Codec not found", (orbis::uint32_t)codecId);
|
||||||
|
std::abort();
|
||||||
|
}
|
||||||
|
AVCodecContext *codecCtx = avcodec_alloc_context3(codec);
|
||||||
|
if (!codecCtx) {
|
||||||
|
ORBIS_LOG_FATAL("Failed to allocate codec context");
|
||||||
|
std::abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (int err = avcodec_open2(codecCtx, codec, NULL) < 0) {
|
||||||
|
ORBIS_LOG_FATAL("Could not open codec");
|
||||||
|
std::abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
instance.codecCtx = codecCtx;
|
||||||
|
}
|
||||||
|
instanceMap.insert({
|
||||||
|
args->instanceId,
|
||||||
|
instance,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
args->instanceId = codecOffset + unimplementedInstanceId++;
|
||||||
|
}
|
||||||
|
ORBIS_LOG_ERROR(__FUNCTION__, request, args->result, args->unk0,
|
||||||
|
args->flags, args->codec, args->instanceId);
|
||||||
|
} else if (request == 0xc0288907) {
|
||||||
|
struct StartBatchBufferArgs {
|
||||||
|
orbis::uint32_t result;
|
||||||
|
orbis::uint32_t unk0;
|
||||||
|
std::byte *pBatch;
|
||||||
|
orbis::uint32_t batchSize;
|
||||||
|
orbis::uint32_t priority;
|
||||||
|
orbis::uint64_t batchError;
|
||||||
|
orbis::uint32_t batchId;
|
||||||
|
};
|
||||||
|
auto args = reinterpret_cast<StartBatchBufferArgs *>(argp);
|
||||||
|
args->result = 0;
|
||||||
|
args->batchId = batchId;
|
||||||
|
ORBIS_LOG_ERROR(__FUNCTION__, request, args->result, args->unk0,
|
||||||
|
args->pBatch, args->batchSize, args->priority,
|
||||||
|
args->batchError, args->batchId);
|
||||||
|
batchId += 1;
|
||||||
|
|
||||||
|
auto ptr = args->pBatch;
|
||||||
|
auto endPtr = args->pBatch + args->batchSize;
|
||||||
|
|
||||||
|
while (ptr < endPtr) {
|
||||||
|
auto header = (InstructionHeader *)ptr;
|
||||||
|
auto instanceId = (header->id >> 6) & 0xfffff;
|
||||||
|
auto jobPtr = ptr + sizeof(InstructionHeader);
|
||||||
|
auto endJobPtr = ptr + header->len;
|
||||||
|
// TODO: handle unimplemented codecs, so auto create instance for now
|
||||||
|
auto &instance = instanceMap[instanceId];
|
||||||
|
RunJob runJob;
|
||||||
|
while (jobPtr < endJobPtr) {
|
||||||
|
auto typed = (OpcodeHeader *)jobPtr;
|
||||||
|
switch (typed->getOpcode()) {
|
||||||
|
case Opcode::ReturnAddress: {
|
||||||
|
ReturnAddress *ra = (ReturnAddress *)jobPtr;
|
||||||
|
ORBIS_LOG_ERROR(__FUNCTION__, request, "return address", ra->opcode,
|
||||||
|
ra->unk, ra->returnAddress);
|
||||||
|
jobPtr += sizeof(ReturnAddress);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Opcode::ControlBufferRa: {
|
||||||
|
runJob.control = true;
|
||||||
|
BatchJobControlBufferRa *ctrl = (BatchJobControlBufferRa *)jobPtr;
|
||||||
|
AJMSidebandResult *result =
|
||||||
|
reinterpret_cast<AJMSidebandResult *>(ctrl->pSidebandOutput);
|
||||||
|
result->result = 0;
|
||||||
|
result->codecResult = 0;
|
||||||
|
ORBIS_LOG_ERROR(__FUNCTION__, request, "control buffer", ctrl->opcode,
|
||||||
|
ctrl->commandId, ctrl->flagsHi, ctrl->flagsLo,
|
||||||
|
ctrl->sidebandInputSize, ctrl->sidebandOutputSize);
|
||||||
|
if ((ctrl->flagsLo & ~7) & CONTROL_INITIALIZE) {
|
||||||
|
if (instance.codec == AJM_CODEC_At9) {
|
||||||
|
struct InitalizeBuffer {
|
||||||
|
orbis::uint32_t configData;
|
||||||
|
orbis::int32_t unk0[2];
|
||||||
|
};
|
||||||
|
InitalizeBuffer *initializeBuffer =
|
||||||
|
(InitalizeBuffer *)ctrl->pSidebandInput;
|
||||||
|
int err = Atrac9InitDecoder(
|
||||||
|
instance.at9.handle,
|
||||||
|
reinterpret_cast<uint8_t *>(&initializeBuffer->configData));
|
||||||
|
if (err < 0) {
|
||||||
|
ORBIS_LOG_FATAL("AT9 Init Decoder error", err);
|
||||||
|
rx::hexdump({(std::byte *)ctrl->pSidebandInput,
|
||||||
|
ctrl->sidebandInputSize});
|
||||||
|
std::abort();
|
||||||
|
}
|
||||||
|
Atrac9CodecInfo pCodecInfo;
|
||||||
|
Atrac9GetCodecInfo(instance.at9.handle, &pCodecInfo);
|
||||||
|
|
||||||
|
instance.at9.frameSamples = pCodecInfo.frameSamples;
|
||||||
|
instance.at9.inputChannels = pCodecInfo.channels;
|
||||||
|
instance.at9.framesInSuperframe = pCodecInfo.framesInSuperframe;
|
||||||
|
instance.at9.superFrameDataIdx = 0;
|
||||||
|
instance.at9.superFrameSize = pCodecInfo.superframeSize;
|
||||||
|
instance.at9.superFrameDataLeft = pCodecInfo.superframeSize;
|
||||||
|
instance.at9.sampleRate = pCodecInfo.samplingRate;
|
||||||
|
|
||||||
|
orbis::uint32_t outputChannels =
|
||||||
|
instance.outputChannels == AJM_DEFAULT
|
||||||
|
? instance.at9.inputChannels
|
||||||
|
: instance.outputChannels;
|
||||||
|
if (instance.at9.inputChannels != outputChannels ||
|
||||||
|
instance.outputFormat != AJM_FORMAT_S16) {
|
||||||
|
instance.resampler = swr_alloc();
|
||||||
|
if (!instance.resampler) {
|
||||||
|
ORBIS_LOG_FATAL("Could not allocate resampler context");
|
||||||
|
std::abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
AVChannelLayout inputChLayout;
|
||||||
|
av_channel_layout_default(&inputChLayout,
|
||||||
|
instance.at9.inputChannels);
|
||||||
|
|
||||||
|
AVChannelLayout outputChLayout;
|
||||||
|
av_channel_layout_default(&outputChLayout, outputChannels);
|
||||||
|
|
||||||
|
av_opt_set_chlayout(instance.resampler, "in_chlayout",
|
||||||
|
&inputChLayout, 0);
|
||||||
|
av_opt_set_chlayout(instance.resampler, "out_chlayout",
|
||||||
|
&outputChLayout, 0);
|
||||||
|
av_opt_set_int(instance.resampler, "in_sample_rate",
|
||||||
|
pCodecInfo.samplingRate, 0);
|
||||||
|
av_opt_set_int(instance.resampler, "out_sample_rate",
|
||||||
|
pCodecInfo.samplingRate, 0);
|
||||||
|
av_opt_set_sample_fmt(instance.resampler, "in_sample_fmt",
|
||||||
|
ajmToAvFormat(AJM_FORMAT_S16), 0);
|
||||||
|
av_opt_set_sample_fmt(instance.resampler, "out_sample_fmt",
|
||||||
|
ajmToAvFormat(instance.outputFormat), 0);
|
||||||
|
if (swr_init(instance.resampler) < 0) {
|
||||||
|
ORBIS_LOG_FATAL(
|
||||||
|
"Failed to initialize the resampling context");
|
||||||
|
std::abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (instance.codec == AJM_CODEC_AAC) {
|
||||||
|
struct InitalizeBuffer {
|
||||||
|
orbis::uint32_t headerIndex;
|
||||||
|
orbis::uint32_t sampleRateIndex;
|
||||||
|
};
|
||||||
|
InitalizeBuffer *initializeBuffer =
|
||||||
|
(InitalizeBuffer *)ctrl->pSidebandInput;
|
||||||
|
instance.aac.headerType =
|
||||||
|
AACHeaderType(initializeBuffer->headerIndex);
|
||||||
|
instance.aac.sampleRate =
|
||||||
|
AACFreq[initializeBuffer->sampleRateIndex];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
jobPtr += sizeof(BatchJobControlBufferRa);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Opcode::RunBufferRa: {
|
||||||
|
BatchJobInputBufferRa *job = (BatchJobInputBufferRa *)jobPtr;
|
||||||
|
ORBIS_LOG_ERROR(__FUNCTION__, request, "BatchJobInputBufferRa",
|
||||||
|
job->opcode, job->szInputSize, job->pInput);
|
||||||
|
runJob.pInput = job->pInput;
|
||||||
|
runJob.inputSize = job->szInputSize;
|
||||||
|
jobPtr += sizeof(BatchJobInputBufferRa);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Opcode::Flags: {
|
||||||
|
BatchJobFlagsRa *job = (BatchJobFlagsRa *)jobPtr;
|
||||||
|
ORBIS_LOG_ERROR(__FUNCTION__, request, "BatchJobFlagsRa",
|
||||||
|
job->flagsHi, job->flagsLo);
|
||||||
|
runJob.flags = ((uint64_t)job->flagsHi << 0x1a) | job->flagsLo;
|
||||||
|
jobPtr += sizeof(BatchJobFlagsRa);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Opcode::JobBufferOutputRa: {
|
||||||
|
BatchJobOutputBufferRa *job = (BatchJobOutputBufferRa *)jobPtr;
|
||||||
|
ORBIS_LOG_ERROR(__FUNCTION__, request, "BatchJobOutputBufferRa",
|
||||||
|
job->opcode, job->szOutputSize, job->pOutput);
|
||||||
|
runJob.pOutput = job->pOutput;
|
||||||
|
runJob.outputSize = job->szOutputSize;
|
||||||
|
jobPtr += sizeof(BatchJobOutputBufferRa);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Opcode::JobBufferSidebandRa: {
|
||||||
|
BatchJobSidebandBufferRa *job = (BatchJobSidebandBufferRa *)jobPtr;
|
||||||
|
ORBIS_LOG_ERROR(__FUNCTION__, request, "BatchJobSidebandBufferRa",
|
||||||
|
job->opcode, job->sidebandSize, job->pSideband);
|
||||||
|
runJob.pSideband = job->pSideband;
|
||||||
|
runJob.sidebandSize = job->sidebandSize;
|
||||||
|
jobPtr += sizeof(BatchJobSidebandBufferRa);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
jobPtr = endJobPtr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ptr = jobPtr;
|
||||||
|
if (!runJob.control && instanceId >= 0xC000) {
|
||||||
|
AJMSidebandResult *result =
|
||||||
|
reinterpret_cast<AJMSidebandResult *>(runJob.pSideband);
|
||||||
|
result->result = 0;
|
||||||
|
result->codecResult = 0;
|
||||||
|
if (runJob.flags & SIDEBAND_STREAM) {
|
||||||
|
AJMSidebandStream *stream =
|
||||||
|
reinterpret_cast<AJMSidebandStream *>(runJob.pSideband + 8);
|
||||||
|
stream->inputSize = runJob.inputSize;
|
||||||
|
stream->outputSize = runJob.outputSize;
|
||||||
|
}
|
||||||
|
} else if (!runJob.control) {
|
||||||
|
orbis::uint32_t outputChannels = instance.outputChannels == AJM_DEFAULT
|
||||||
|
? 2
|
||||||
|
: instance.outputChannels;
|
||||||
|
AJMSidebandResult *result =
|
||||||
|
reinterpret_cast<AJMSidebandResult *>(runJob.pSideband);
|
||||||
|
result->result = 0;
|
||||||
|
result->codecResult = 0;
|
||||||
|
|
||||||
|
uint32_t inputReaded = 0;
|
||||||
|
uint32_t outputWritten = 0;
|
||||||
|
uint32_t framesProcessed = 0;
|
||||||
|
uint32_t channels = 0;
|
||||||
|
uint32_t sampleRate = 0;
|
||||||
|
if (runJob.inputSize != 0 && runJob.outputSize != 0) {
|
||||||
|
while (inputReaded < runJob.inputSize &&
|
||||||
|
outputWritten < runJob.outputSize) {
|
||||||
|
// TODO: initialize if not
|
||||||
|
if (instance.at9.frameSamples == 0 &&
|
||||||
|
instance.codec == AJM_CODEC_At9) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (instance.codec == AJM_CODEC_At9) {
|
||||||
|
outputChannels = instance.outputChannels == AJM_DEFAULT
|
||||||
|
? instance.at9.inputChannels
|
||||||
|
: instance.outputChannels;
|
||||||
|
orbis::int32_t outputBufferSize = av_samples_get_buffer_size(
|
||||||
|
nullptr, outputChannels, instance.at9.frameSamples,
|
||||||
|
ajmToAvFormat(instance.resampler ? AJM_FORMAT_S16
|
||||||
|
: instance.outputFormat),
|
||||||
|
0);
|
||||||
|
|
||||||
|
orbis::uint8_t *tempBuffer =
|
||||||
|
instance.resampler ? (uint8_t *)av_malloc(outputBufferSize)
|
||||||
|
: reinterpret_cast<orbis::uint8_t *>(
|
||||||
|
runJob.pOutput + outputWritten);
|
||||||
|
orbis::int32_t bytesUsed = 0;
|
||||||
|
int err =
|
||||||
|
Atrac9Decode(instance.at9.handle, runJob.pInput + inputReaded,
|
||||||
|
tempBuffer, kAtrac9FormatS16, &bytesUsed);
|
||||||
|
if (err != ERR_SUCCESS) {
|
||||||
|
ORBIS_LOG_FATAL("Could not decode frame", err);
|
||||||
|
std::abort();
|
||||||
|
}
|
||||||
|
if (instance.resampler) {
|
||||||
|
auto outputBuffer = reinterpret_cast<orbis::uint8_t *>(
|
||||||
|
runJob.pOutput + outputWritten);
|
||||||
|
|
||||||
|
int nb_samples =
|
||||||
|
swr_convert(instance.resampler, &outputBuffer,
|
||||||
|
instance.at9.frameSamples, &tempBuffer,
|
||||||
|
instance.at9.frameSamples);
|
||||||
|
if (nb_samples < 0) {
|
||||||
|
ORBIS_LOG_FATAL("Error while resampling");
|
||||||
|
std::abort();
|
||||||
|
}
|
||||||
|
av_freep(&tempBuffer);
|
||||||
|
}
|
||||||
|
instance.at9.estimatedSizeUsed = static_cast<uint32_t>(bytesUsed);
|
||||||
|
instance.at9.superFrameDataLeft -= bytesUsed;
|
||||||
|
instance.at9.superFrameDataIdx++;
|
||||||
|
if (instance.at9.superFrameDataIdx ==
|
||||||
|
instance.at9.framesInSuperframe) {
|
||||||
|
instance.at9.estimatedSizeUsed +=
|
||||||
|
instance.at9.superFrameDataLeft;
|
||||||
|
instance.at9.superFrameDataIdx = 0;
|
||||||
|
instance.at9.superFrameDataLeft = instance.at9.superFrameSize;
|
||||||
|
}
|
||||||
|
channels = instance.at9.inputChannels;
|
||||||
|
sampleRate = instance.at9.sampleRate;
|
||||||
|
inputReaded += instance.at9.estimatedSizeUsed;
|
||||||
|
outputWritten +=
|
||||||
|
std::max((uint32_t)outputBufferSize, runJob.outputSize);
|
||||||
|
framesProcessed += 1;
|
||||||
|
} else if (instance.codec == AJM_CODEC_MP3) {
|
||||||
|
ORBIS_LOG_FATAL("Pre get mp3 data size info", runJob.inputSize,
|
||||||
|
runJob.outputSize, runJob.sidebandSize,
|
||||||
|
runJob.flags);
|
||||||
|
auto realInputSize =
|
||||||
|
get_mp3_data_size((uint8_t *)(runJob.pInput + inputReaded));
|
||||||
|
if (realInputSize == 0) {
|
||||||
|
realInputSize = runJob.inputSize;
|
||||||
|
} else {
|
||||||
|
realInputSize = std::min(realInputSize, runJob.inputSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inputReaded + realInputSize > runJob.inputSize) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// rx::hexdump(
|
||||||
|
// {(std::byte *)(runJob.pInput + inputReaded),
|
||||||
|
// realInputSize});
|
||||||
|
|
||||||
|
AVPacket *pkt = av_packet_alloc();
|
||||||
|
AVFrame *frame = av_frame_alloc();
|
||||||
|
pkt->data = (uint8_t *)(runJob.pInput + inputReaded);
|
||||||
|
pkt->size = realInputSize;
|
||||||
|
int ret = avcodec_send_packet(instance.codecCtx, pkt);
|
||||||
|
if (ret < 0) {
|
||||||
|
ORBIS_LOG_FATAL("Error sending packet for decoding", ret);
|
||||||
|
std::abort();
|
||||||
|
}
|
||||||
|
ret = avcodec_receive_frame(instance.codecCtx, frame);
|
||||||
|
if (ret < 0) {
|
||||||
|
ORBIS_LOG_FATAL("Error during decoding");
|
||||||
|
std::abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto resampler = swr_alloc();
|
||||||
|
if (!resampler) {
|
||||||
|
ORBIS_LOG_FATAL("Could not allocate resampler context");
|
||||||
|
std::abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
AVChannelLayout inputChLayout;
|
||||||
|
av_channel_layout_default(&inputChLayout,
|
||||||
|
frame->ch_layout.nb_channels);
|
||||||
|
|
||||||
|
AVChannelLayout outputChLayout;
|
||||||
|
av_channel_layout_default(&outputChLayout, outputChannels);
|
||||||
|
|
||||||
|
av_opt_set_chlayout(resampler, "in_chlayout", &inputChLayout, 0);
|
||||||
|
av_opt_set_chlayout(resampler, "out_chlayout", &outputChLayout,
|
||||||
|
0);
|
||||||
|
av_opt_set_int(resampler, "in_sample_rate", frame->sample_rate,
|
||||||
|
0);
|
||||||
|
av_opt_set_int(resampler, "out_sample_rate", frame->sample_rate,
|
||||||
|
0);
|
||||||
|
av_opt_set_sample_fmt(resampler, "in_sample_fmt",
|
||||||
|
ajmToAvFormat(AJM_FORMAT_FLOAT), 0);
|
||||||
|
av_opt_set_sample_fmt(resampler, "out_sample_fmt",
|
||||||
|
ajmToAvFormat(instance.outputFormat), 0);
|
||||||
|
if (swr_init(resampler) < 0) {
|
||||||
|
ORBIS_LOG_FATAL("Failed to initialize the resampling context");
|
||||||
|
std::abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t *outputBuffer = NULL;
|
||||||
|
int outputBufferSize = av_samples_alloc(
|
||||||
|
&outputBuffer, NULL, frame->ch_layout.nb_channels,
|
||||||
|
frame->nb_samples, ajmToAvFormat(instance.outputFormat), 0);
|
||||||
|
if (outputBufferSize < 0) {
|
||||||
|
ORBIS_LOG_FATAL("Could not allocate output buffer");
|
||||||
|
std::abort();
|
||||||
|
}
|
||||||
|
ORBIS_LOG_TODO("output buffer info", frame->ch_layout.nb_channels,
|
||||||
|
frame->nb_samples, (int32_t)instance.outputFormat,
|
||||||
|
outputBufferSize);
|
||||||
|
|
||||||
|
if (outputWritten + outputBufferSize > runJob.outputSize) {
|
||||||
|
ORBIS_LOG_TODO("overwriting", outputWritten, outputBufferSize,
|
||||||
|
outputWritten + outputBufferSize,
|
||||||
|
runJob.outputSize);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
int nb_samples =
|
||||||
|
swr_convert(resampler, &outputBuffer, frame->nb_samples,
|
||||||
|
(const uint8_t **)frame->data, frame->nb_samples);
|
||||||
|
if (nb_samples < 0) {
|
||||||
|
ORBIS_LOG_FATAL("Error while converting");
|
||||||
|
std::abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(runJob.pOutput + outputWritten, outputBuffer,
|
||||||
|
outputBufferSize);
|
||||||
|
channels = frame->ch_layout.nb_channels;
|
||||||
|
sampleRate = frame->sample_rate;
|
||||||
|
inputReaded += realInputSize;
|
||||||
|
outputWritten += outputBufferSize;
|
||||||
|
framesProcessed += 1;
|
||||||
|
av_freep(&outputBuffer);
|
||||||
|
swr_free(&resampler);
|
||||||
|
av_frame_free(&frame);
|
||||||
|
av_packet_free(&pkt);
|
||||||
|
} else if (instance.codec == AJM_CODEC_AAC) {
|
||||||
|
AVPacket *pkt = av_packet_alloc();
|
||||||
|
AVFrame *frame = av_frame_alloc();
|
||||||
|
pkt->data = (uint8_t *)runJob.pInput + inputReaded;
|
||||||
|
pkt->size = runJob.inputSize;
|
||||||
|
|
||||||
|
// HACK: to avoid writing a bunch of useless calls
|
||||||
|
// we simply call this method directly (but it can be very
|
||||||
|
// unstable)
|
||||||
|
int gotFrame;
|
||||||
|
int len =
|
||||||
|
ffcodec(instance.codecCtx->codec)
|
||||||
|
->cb.decode(instance.codecCtx, frame, &gotFrame, pkt);
|
||||||
|
|
||||||
|
orbis::uint32_t outputChannels =
|
||||||
|
instance.outputChannels == AJM_DEFAULT
|
||||||
|
? frame->ch_layout.nb_channels
|
||||||
|
: instance.outputChannels;
|
||||||
|
|
||||||
|
ORBIS_LOG_TODO("aac decode", len, gotFrame,
|
||||||
|
frame->ch_layout.nb_channels, frame->sample_rate,
|
||||||
|
instance.aac.sampleRate, outputChannels,
|
||||||
|
(orbis::uint32_t)instance.outputChannels);
|
||||||
|
|
||||||
|
auto resampler = swr_alloc();
|
||||||
|
if (!resampler) {
|
||||||
|
ORBIS_LOG_FATAL("Could not allocate resampler context");
|
||||||
|
std::abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
AVChannelLayout inputChLayout;
|
||||||
|
av_channel_layout_default(&inputChLayout,
|
||||||
|
frame->ch_layout.nb_channels);
|
||||||
|
|
||||||
|
AVChannelLayout outputChLayout;
|
||||||
|
av_channel_layout_default(&outputChLayout, outputChannels);
|
||||||
|
|
||||||
|
av_opt_set_chlayout(resampler, "in_chlayout", &inputChLayout, 0);
|
||||||
|
av_opt_set_chlayout(resampler, "out_chlayout", &outputChLayout,
|
||||||
|
0);
|
||||||
|
av_opt_set_int(resampler, "in_sample_rate",
|
||||||
|
instance.aac.sampleRate, 0);
|
||||||
|
av_opt_set_int(resampler, "out_sample_rate",
|
||||||
|
instance.aac.sampleRate, 0);
|
||||||
|
av_opt_set_sample_fmt(resampler, "in_sample_fmt",
|
||||||
|
ajmToAvFormat(AJM_FORMAT_FLOAT), 0);
|
||||||
|
av_opt_set_sample_fmt(resampler, "out_sample_fmt",
|
||||||
|
ajmToAvFormat(instance.outputFormat), 0);
|
||||||
|
if (swr_init(resampler) < 0) {
|
||||||
|
ORBIS_LOG_FATAL("Failed to initialize the resampling context");
|
||||||
|
std::abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t *outputBuffer = NULL;
|
||||||
|
int outputBufferSize = av_samples_alloc(
|
||||||
|
&outputBuffer, NULL, outputChannels, frame->nb_samples,
|
||||||
|
ajmToAvFormat(instance.outputFormat), 0);
|
||||||
|
if (outputBufferSize < 0) {
|
||||||
|
ORBIS_LOG_FATAL("Could not allocate output buffer");
|
||||||
|
std::abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
int nb_samples =
|
||||||
|
swr_convert(resampler, &outputBuffer, frame->nb_samples,
|
||||||
|
frame->extended_data, frame->nb_samples);
|
||||||
|
if (nb_samples < 0) {
|
||||||
|
ORBIS_LOG_FATAL("Error while converting");
|
||||||
|
std::abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(runJob.pOutput + outputWritten, outputBuffer,
|
||||||
|
outputBufferSize);
|
||||||
|
channels = frame->ch_layout.nb_channels;
|
||||||
|
sampleRate = frame->sample_rate;
|
||||||
|
inputReaded += len;
|
||||||
|
outputWritten += outputBufferSize;
|
||||||
|
framesProcessed += 1;
|
||||||
|
av_frame_free(&frame);
|
||||||
|
av_packet_free(&pkt);
|
||||||
|
swr_free(&resampler);
|
||||||
|
}
|
||||||
|
if (!(runJob.flags & RUN_MULTIPLE_FRAMES)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
orbis::int64_t currentSize = sizeof(AJMSidebandResult);
|
||||||
|
|
||||||
|
if (runJob.flags & SIDEBAND_STREAM) {
|
||||||
|
ORBIS_LOG_TODO("SIDEBAND_STREAM", currentSize, inputReaded,
|
||||||
|
outputWritten);
|
||||||
|
AJMSidebandStream *stream = reinterpret_cast<AJMSidebandStream *>(
|
||||||
|
runJob.pSideband + currentSize);
|
||||||
|
stream->inputSize = inputReaded;
|
||||||
|
stream->outputSize = outputWritten;
|
||||||
|
currentSize += sizeof(AJMSidebandStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (runJob.flags & SIDEBAND_FORMAT) {
|
||||||
|
ORBIS_LOG_TODO("SIDEBAND_FORMAT", currentSize);
|
||||||
|
AJMSidebandFormat *format = reinterpret_cast<AJMSidebandFormat *>(
|
||||||
|
runJob.pSideband + currentSize);
|
||||||
|
format->channels = AJMChannels(channels);
|
||||||
|
format->sampleRate = sampleRate;
|
||||||
|
format->sampleFormat = AJM_FORMAT_FLOAT;
|
||||||
|
currentSize += sizeof(AJMSidebandFormat);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
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);
|
||||||
|
currentSize += sizeof(AJMMP3CodecInfoSideband);
|
||||||
|
} else if (instance.codec == AJM_CODEC_AAC) {
|
||||||
|
// TODO
|
||||||
|
AJMAACCodecInfoSideband *info =
|
||||||
|
reinterpret_cast<AJMAACCodecInfoSideband *>(runJob.pSideband +
|
||||||
|
currentSize);
|
||||||
|
currentSize += sizeof(AJMAACCodecInfoSideband);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (runJob.flags & RUN_MULTIPLE_FRAMES) {
|
||||||
|
ORBIS_LOG_TODO("RUN_MULTIPLE_FRAMES", currentSize);
|
||||||
|
AJMSidebandMultipleFrames *multipleFrames =
|
||||||
|
reinterpret_cast<AJMSidebandMultipleFrames *>(runJob.pSideband +
|
||||||
|
currentSize);
|
||||||
|
multipleFrames->framesProcessed = framesProcessed;
|
||||||
|
currentSize += sizeof(AJMSidebandMultipleFrames);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (request == 0xc0288908) {
|
||||||
|
struct Args {
|
||||||
|
orbis::uint32_t unk0;
|
||||||
|
orbis::uint32_t unk1;
|
||||||
|
orbis::uint32_t batchId;
|
||||||
|
orbis::uint32_t timeout;
|
||||||
|
orbis::uint64_t batchError;
|
||||||
|
};
|
||||||
|
auto args = reinterpret_cast<Args *>(argp);
|
||||||
|
args->unk0 = 0;
|
||||||
|
ORBIS_LOG_ERROR(__FUNCTION__, request, args->unk0, args->unk1,
|
||||||
|
args->batchId, args->timeout, args->batchError);
|
||||||
|
} else {
|
||||||
ORBIS_LOG_FATAL("Unhandled AJM ioctl", request);
|
ORBIS_LOG_FATAL("Unhandled AJM ioctl", request);
|
||||||
thread->where();
|
thread->where();
|
||||||
|
}
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
322
rpcsx/iodev/ajm.hpp
Normal file
322
rpcsx/iodev/ajm.hpp
Normal file
|
|
@ -0,0 +1,322 @@
|
||||||
|
#include "orbis-config.hpp"
|
||||||
|
#include "orbis/utils/Logs.hpp"
|
||||||
|
#include <cstdint>
|
||||||
|
extern "C" {
|
||||||
|
#include <libavcodec/avcodec.h>
|
||||||
|
#include <libavutil/samplefmt.h>
|
||||||
|
#include <libswresample/swresample.h>
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class Opcode : std::uint8_t {
|
||||||
|
RunBufferRa = 1,
|
||||||
|
ControlBufferRa = 2,
|
||||||
|
Flags = 4,
|
||||||
|
ReturnAddress = 6,
|
||||||
|
JobBufferOutputRa = 17,
|
||||||
|
JobBufferSidebandRa = 18,
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct InstructionHeader {
|
||||||
|
orbis::uint32_t id;
|
||||||
|
orbis::uint32_t len;
|
||||||
|
} InstructionHeader;
|
||||||
|
|
||||||
|
static_assert(sizeof(InstructionHeader) == 0x8);
|
||||||
|
|
||||||
|
typedef struct OpcodeHeader {
|
||||||
|
orbis::uint32_t opcode;
|
||||||
|
|
||||||
|
Opcode getOpcode() const {
|
||||||
|
ORBIS_LOG_ERROR(__FUNCTION__, opcode);
|
||||||
|
if (auto loType = static_cast<Opcode>(opcode & 0xf);
|
||||||
|
loType == Opcode::ReturnAddress || loType == Opcode::Flags) {
|
||||||
|
return loType;
|
||||||
|
}
|
||||||
|
|
||||||
|
return static_cast<Opcode>(opcode & 0x1f);
|
||||||
|
}
|
||||||
|
} OpcodeHeader;
|
||||||
|
|
||||||
|
typedef struct ReturnAddress {
|
||||||
|
orbis::uint32_t opcode;
|
||||||
|
orbis::uint32_t unk; // 0, padding?
|
||||||
|
orbis::ptr<void> returnAddress;
|
||||||
|
} ReturnAddress;
|
||||||
|
static_assert(sizeof(ReturnAddress) == 0x10);
|
||||||
|
|
||||||
|
typedef struct BatchJobControlBufferRa {
|
||||||
|
orbis::uint32_t opcode;
|
||||||
|
orbis::uint32_t sidebandInputSize;
|
||||||
|
std::byte* pSidebandInput;
|
||||||
|
orbis::uint32_t flagsHi;
|
||||||
|
orbis::uint32_t flagsLo;
|
||||||
|
orbis::uint32_t commandId;
|
||||||
|
orbis::uint32_t sidebandOutputSize;
|
||||||
|
std::byte* pSidebandOutput;
|
||||||
|
} BatchJobControlBufferRa;
|
||||||
|
static_assert(sizeof(BatchJobControlBufferRa) == 0x28);
|
||||||
|
|
||||||
|
typedef struct BatchJobInputBufferRa {
|
||||||
|
orbis::uint32_t opcode;
|
||||||
|
orbis::uint32_t szInputSize;
|
||||||
|
std::byte* pInput;
|
||||||
|
} BatchJobInputBufferRa;
|
||||||
|
static_assert(sizeof(BatchJobInputBufferRa) == 0x10);
|
||||||
|
|
||||||
|
typedef struct BatchJobFlagsRa {
|
||||||
|
orbis::uint32_t flagsHi;
|
||||||
|
orbis::uint32_t flagsLo;
|
||||||
|
} BatchJobFlagsRa;
|
||||||
|
|
||||||
|
static_assert(sizeof(BatchJobFlagsRa) == 0x8);
|
||||||
|
|
||||||
|
typedef struct BatchJobOutputBufferRa {
|
||||||
|
orbis::uint32_t opcode;
|
||||||
|
orbis::uint32_t szOutputSize;
|
||||||
|
std::byte* pOutput;
|
||||||
|
} BatchJobOutputBufferRa;
|
||||||
|
static_assert(sizeof(BatchJobOutputBufferRa) == 0x10);
|
||||||
|
|
||||||
|
typedef struct BatchJobSidebandBufferRa {
|
||||||
|
orbis::uint32_t opcode;
|
||||||
|
orbis::uint32_t sidebandSize;
|
||||||
|
std::byte* pSideband;
|
||||||
|
} BatchJobSidebandBufferRa;
|
||||||
|
static_assert(sizeof(BatchJobSidebandBufferRa) == 0x10);
|
||||||
|
|
||||||
|
typedef struct RunJob {
|
||||||
|
orbis::uint64_t flags;
|
||||||
|
orbis::uint32_t inputSize;
|
||||||
|
std::byte* pInput;
|
||||||
|
orbis::uint32_t outputSize;
|
||||||
|
std::byte* pOutput;
|
||||||
|
orbis::uint32_t sidebandSize;
|
||||||
|
std::byte* pSideband;
|
||||||
|
bool control;
|
||||||
|
} RunJob;
|
||||||
|
|
||||||
|
// 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};
|
||||||
|
|
||||||
|
// Layers - use [layer]
|
||||||
|
const uint8_t mpeg_layers[4] = {0, 3, 2, 1};
|
||||||
|
|
||||||
|
// Bitrates - use [version][layer][bitrate]
|
||||||
|
const 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
|
||||||
|
{0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160,
|
||||||
|
0}, // Layer 3
|
||||||
|
{0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160,
|
||||||
|
0}, // Layer 2
|
||||||
|
{0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256,
|
||||||
|
0} // Layer 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// Reserved
|
||||||
|
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, // Invalid
|
||||||
|
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, // Invalid
|
||||||
|
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, // Invalid
|
||||||
|
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} // Invalid
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// Version 2
|
||||||
|
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, // Reserved
|
||||||
|
{0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160,
|
||||||
|
0}, // Layer 3
|
||||||
|
{0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160,
|
||||||
|
0}, // Layer 2
|
||||||
|
{0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256,
|
||||||
|
0} // Layer 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// Version 1
|
||||||
|
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, // Reserved
|
||||||
|
{0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320,
|
||||||
|
0}, // Layer 3
|
||||||
|
{0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384,
|
||||||
|
0}, // Layer 2
|
||||||
|
{0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448,
|
||||||
|
0}, // Layer 1
|
||||||
|
}};
|
||||||
|
|
||||||
|
// Sample rates - use [version][srate]
|
||||||
|
const uint16_t mpeg_srates[4][4] = {
|
||||||
|
{11025, 12000, 8000, 0}, // MPEG 2.5
|
||||||
|
{0, 0, 0, 0}, // Reserved
|
||||||
|
{22050, 24000, 16000, 0}, // MPEG 2
|
||||||
|
{44100, 48000, 32000, 0} // MPEG 1
|
||||||
|
};
|
||||||
|
|
||||||
|
// Samples per frame - use [version][layer]
|
||||||
|
const 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
|
||||||
|
{0, 576, 1152, 384}, // 2
|
||||||
|
{0, 1152, 1152, 384} // 1
|
||||||
|
};
|
||||||
|
|
||||||
|
// Slot size (MPEG unit of measurement) - use [layer]
|
||||||
|
const uint8_t mpeg_slot_size[4] = {0, 1, 1, 4}; // Rsvd, 3, 2, 1
|
||||||
|
|
||||||
|
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
|
||||||
|
|| ((data[1] & 0x06) == 0x00) // Layer rsvd
|
||||||
|
|| ((data[2] & 0xF0) == 0xF0) // Bitrate rsvd
|
||||||
|
) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Data to be extracted from the header
|
||||||
|
uint8_t ver = (data[1] & 0x18) >> 3; // Version index
|
||||||
|
uint8_t lyr = (data[1] & 0x06) >> 1; // Layer index
|
||||||
|
uint8_t pad = (data[2] & 0x02) >> 1; // Padding? 0/1
|
||||||
|
uint8_t brx = (data[2] & 0xf0) >> 4; // Bitrate index
|
||||||
|
uint8_t srx = (data[2] & 0x0c) >> 2; // SampRate index
|
||||||
|
|
||||||
|
// Lookup real values of these fields
|
||||||
|
uint32_t bitrate = mpeg_bitrates[ver][lyr][brx] * 1000;
|
||||||
|
uint32_t samprate = mpeg_srates[ver][srx];
|
||||||
|
uint16_t samples = mpeg_frame_samples[ver][lyr];
|
||||||
|
uint8_t slot_size = mpeg_slot_size[lyr];
|
||||||
|
|
||||||
|
// In-between calculations
|
||||||
|
float bps = static_cast<float>(samples) / 8.0f;
|
||||||
|
float fsize =
|
||||||
|
((bps * static_cast<float>(bitrate)) / static_cast<float>(samprate)) +
|
||||||
|
((pad) ? slot_size : 0);
|
||||||
|
|
||||||
|
ORBIS_LOG_TODO("get_mp3_data_size", (uint16_t)ver, (uint16_t)lyr,
|
||||||
|
(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
|
||||||
|
return static_cast<uint16_t>(fsize);
|
||||||
|
}
|
||||||
|
|
||||||
|
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};
|
||||||
|
|
||||||
|
enum AJMCodecs : orbis::uint32_t {
|
||||||
|
AJM_CODEC_MP3 = 0,
|
||||||
|
AJM_CODEC_At9 = 1,
|
||||||
|
AJM_CODEC_AAC = 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum AJMChannels : orbis::uint32_t {
|
||||||
|
AJM_DEFAULT = 0,
|
||||||
|
AJM_CHANNEL_1 = 1,
|
||||||
|
AJM_CHANNEL_2 = 2,
|
||||||
|
AJM_CHANNEL_3 = 3,
|
||||||
|
AJM_CHANNEL_4 = 4,
|
||||||
|
AJM_CHANNEL_5 = 5,
|
||||||
|
AJM_CHANNEL_6 = 6,
|
||||||
|
AJM_CHANNEL_8 = 8,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum AJMFormat : orbis::uint32_t {
|
||||||
|
AJM_FORMAT_S16 = 0, // default
|
||||||
|
AJM_FORMAT_S32 = 1,
|
||||||
|
AJM_FORMAT_FLOAT = 2
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct At9Instance {
|
||||||
|
orbis::ptr<void> handle;
|
||||||
|
orbis::uint32_t inputChannels;
|
||||||
|
orbis::uint32_t framesInSuperframe;
|
||||||
|
orbis::uint32_t frameSamples;
|
||||||
|
orbis::uint32_t superFrameDataLeft;
|
||||||
|
orbis::uint32_t superFrameDataIdx;
|
||||||
|
orbis::uint32_t superFrameSize;
|
||||||
|
orbis::uint32_t estimatedSizeUsed;
|
||||||
|
orbis::uint32_t sampleRate;
|
||||||
|
} At9Instance;
|
||||||
|
|
||||||
|
typedef struct AACInstance {
|
||||||
|
AACHeaderType headerType;
|
||||||
|
orbis::uint32_t sampleRate;
|
||||||
|
} AACInstance;
|
||||||
|
|
||||||
|
typedef struct Instance {
|
||||||
|
AJMCodecs codec;
|
||||||
|
AJMChannels outputChannels;
|
||||||
|
AJMFormat outputFormat;
|
||||||
|
At9Instance at9;
|
||||||
|
AACInstance aac;
|
||||||
|
AVCodecContext *codecCtx;
|
||||||
|
SwrContext *resampler;
|
||||||
|
orbis::uint32_t lastBatchId;
|
||||||
|
} Instance;
|
||||||
|
|
||||||
|
typedef struct AJMSidebandResult {
|
||||||
|
orbis::int32_t result;
|
||||||
|
orbis::int32_t codecResult;
|
||||||
|
} AJMSidebandResult;
|
||||||
|
|
||||||
|
typedef struct AJMSidebandStream {
|
||||||
|
orbis::int32_t inputSize;
|
||||||
|
orbis::int32_t outputSize;
|
||||||
|
orbis::uint64_t unk0;
|
||||||
|
} AJMSidebandStream;
|
||||||
|
|
||||||
|
typedef struct AJMSidebandMultipleFrames {
|
||||||
|
orbis::uint32_t framesProcessed;
|
||||||
|
orbis::uint32_t unk0;
|
||||||
|
} AJMSidebandMultipleFrames;
|
||||||
|
|
||||||
|
typedef struct AJMSidebandFormat {
|
||||||
|
AJMChannels channels;
|
||||||
|
orbis::uint32_t unk0; // maybe channel mask?
|
||||||
|
orbis::uint32_t sampleRate;
|
||||||
|
AJMFormat sampleFormat;
|
||||||
|
uint32_t bitrate;
|
||||||
|
uint32_t unk1;
|
||||||
|
} AJMSidebandFormat;
|
||||||
|
|
||||||
|
typedef struct AJMAt9CodecInfoSideband {
|
||||||
|
orbis::uint32_t superFrameSize;
|
||||||
|
orbis::uint32_t framesInSuperFrame;
|
||||||
|
orbis::uint32_t unk0;
|
||||||
|
orbis::uint32_t frameSamples;
|
||||||
|
} AJMAt9CodecInfoSideband;
|
||||||
|
|
||||||
|
typedef struct AJMMP3CodecInfoSideband {
|
||||||
|
orbis::uint32_t header;
|
||||||
|
orbis::uint8_t unk0;
|
||||||
|
orbis::uint8_t unk1;
|
||||||
|
orbis::uint8_t unk2;
|
||||||
|
orbis::uint8_t unk3;
|
||||||
|
orbis::uint8_t unk4;
|
||||||
|
orbis::uint8_t unk5;
|
||||||
|
orbis::uint16_t unk6;
|
||||||
|
orbis::uint16_t unk7;
|
||||||
|
orbis::uint16_t unk8;
|
||||||
|
} AJMMP3CodecInfoSideband;
|
||||||
|
|
||||||
|
typedef struct AJMAACCodecInfoSideband {
|
||||||
|
orbis::uint32_t heaac;
|
||||||
|
orbis::uint32_t unk0;
|
||||||
|
} AJMAACCodecInfoSideband;
|
||||||
|
|
||||||
|
enum ControlFlags {
|
||||||
|
CONTROL_INITIALIZE = 0x4000,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum RunFlags {
|
||||||
|
RUN_MULTIPLE_FRAMES = 0x1000,
|
||||||
|
RUN_GET_CODEC_INFO = 0x800,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum SidebandFlags {
|
||||||
|
SIDEBAND_STREAM = 0x800000000000,
|
||||||
|
SIDEBAND_FORMAT = 0x400000000000
|
||||||
|
};
|
||||||
Loading…
Reference in a new issue