#include "common/assert.h" #include "common/common.h" #include "common/logging/log.h" #include "libs/ajm/aac_decoder.h" #include "libs/ajm/atrac9_decoder.h" #include "libs/ajm/decoder.h" #include "libs/ajm/mp3_decoder.h" #include "libs/audio.h" #include "libs/errno.h" #include "libs/libs.h" #include #include #include #include #include #include #include #include #include #include #include #include namespace Libs::Audio::Ajm { LIB_NAME("Ajm", "Ajm"); struct AjmBatchInfo { void* buffer; size_t offset; size_t size; void* last_good_job; const void* last_good_job_ra; }; struct AjmBatchError { int error_code; const void* job_addr; uint32_t cmd_offset; const void* job_ra; }; struct AjmBuffer { void* address; size_t size; }; struct AjmSidebandResult { int32_t result; int32_t internal_result; }; struct AjmSidebandStream { int32_t size_consumed; int32_t size_produced; uint64_t total_decoded_samples; }; struct AjmSidebandMFrame { uint32_t num_frames; uint32_t reserved; }; struct AjmSidebandResampleInfo { float ratio; int32_t num_samples; uint32_t reserved[8]; }; struct AjmGetStatisticsResult { AjmSidebandResult result; float engine_usage_batch; float engine_usage_interval[3]; uint32_t memory[6]; }; struct AjmDecOpusInitializeParameters { uint32_t channel_num; uint32_t sample_rate; uint32_t mapping_family; }; struct AjmSidebandDecOpusCodecInfo { uint32_t frames_per_packet; }; enum class AjmCodec : uint32_t { DecMp3 = 0, DecAt9 = 1, DecM4aac = 2, DecUnknownNewABI = 14, DecOpus = 24, }; constexpr int32_t AJM_ERROR_INVALID_CONTEXT = static_cast(0x80930002u); constexpr int32_t AJM_ERROR_INVALID_PARAMETER = static_cast(0x80930005u); constexpr int32_t AJM_ERROR_CODEC_NOT_SUPPORTED = static_cast(0x80930008u); constexpr int32_t AJM_ERROR_JOB_CREATION = static_cast(0x80930012u); constexpr size_t AJM_JOB_CONTROL_SIZE = 48; constexpr size_t AJM_JOB_RUN_SIZE = 64; constexpr size_t AJM_JOB_GET_STATISTICS_SIZE = sizeof(uint32_t) + sizeof(float) * 3 + 8 + AJM_JOB_RUN_SIZE; constexpr size_t AJM_JOB_SET_RESAMPLE_PARAMETERS_SIZE = sizeof(float) * 2 + sizeof(uint32_t) + 12 + AJM_JOB_CONTROL_SIZE; constexpr size_t AJM_SIDEBAND_RESULT_SIZE = sizeof(AjmSidebandResult); constexpr size_t AJM_SIDEBAND_STREAM_SIZE = sizeof(AjmSidebandStream); constexpr size_t AJM_SIDEBAND_MFRAME_SIZE = sizeof(AjmSidebandMFrame); constexpr size_t AJM_SIDEBAND_FORMAT_SIZE = sizeof(AjmSidebandFormat); constexpr size_t AJM_SIDEBAND_GAPLESS_SIZE = sizeof(AjmSidebandGaplessDecode); constexpr size_t AJM_SIDEBAND_RESAMPLE_INFO_SIZE = sizeof(AjmSidebandResampleInfo); constexpr uint64_t AJM_FLAG_RUN_GET_CODEC_INFO = 1ull << 11u; constexpr uint64_t AJM_FLAG_RUN_MULTIPLE_FRAMES = 1ull << 12u; constexpr uint64_t AJM_FLAG_CONTROL_RESET = 1ull << 13u; constexpr uint64_t AJM_FLAG_CONTROL_INITIALIZE = 1ull << 14u; constexpr uint64_t AJM_FLAG_SIDEBAND_RESAMPLE_INFO = 1ull << 44u; constexpr uint64_t AJM_FLAG_SIDEBAND_GAPLESS = 1ull << 45u; constexpr uint64_t AJM_FLAG_SIDEBAND_FORMAT = 1ull << 46u; constexpr uint64_t AJM_FLAG_SIDEBAND_STREAM = 1ull << 47u; constexpr uint64_t AJM_INSTANCE_FLAG_FORMAT_OFFSET = 7u; constexpr uint64_t AJM_INSTANCE_FLAG_FORMAT_MASK = 0x7u; constexpr uint64_t AJM_INSTANCE_FLAG_MAX_CHANNEL_MASK = 0x7fu; constexpr uint32_t AJM_DEC_OPUS_MAX_CHANNELS_FOR_10CH = 10; constexpr uint32_t AJM_DEC_OPUS_FRAME_SAMPLES = 960; static std::atomic_uint32_t g_ajm_next_instance {1}; static std::atomic_uint32_t g_ajm_next_batch {1}; static uint32_t AjmGetFlagChannelCount(uint64_t flags) { return static_cast(flags & AJM_INSTANCE_FLAG_MAX_CHANNEL_MASK); } static AjmSampleEncoding AjmGetFlagSampleEncoding(uint64_t flags) { const auto encoding = static_cast((flags >> AJM_INSTANCE_FLAG_FORMAT_OFFSET) & AJM_INSTANCE_FLAG_FORMAT_MASK); switch (encoding) { case static_cast(AjmSampleEncoding::S16): return AjmSampleEncoding::S16; case static_cast(AjmSampleEncoding::S32): return AjmSampleEncoding::S32; case static_cast(AjmSampleEncoding::Float): return AjmSampleEncoding::Float; default: EXIT("unsupported AJM PCM sample encoding %u\n", encoding); } return AjmSampleEncoding::S16; } static const char* AjmCodecName(uint32_t codec) { switch (codec) { case static_cast(AjmCodec::DecMp3): return "MP3 decoder"; case static_cast(AjmCodec::DecAt9): return "ATRAC9 decoder"; case static_cast(AjmCodec::DecM4aac): return "MPEG4-AAC decoder"; case 3: return "CELP8 decoder"; case 4: return "CELP8 encoder"; case 12: return "CELP16 decoder"; case 13: return "CELP16 encoder"; case 22: return "HE-VAG decoder"; case 23: return "LPCM decoder"; case static_cast(AjmCodec::DecOpus): return "Opus decoder"; default: return "unknown"; } } static_assert(sizeof(AjmDecAt9ConfigDataInfo) == 20); int KYTY_SYSV_ABI AjmDecAt9ParseConfigData(const void* config_data, AjmDecAt9ConfigDataInfo* config_info) { PRINT_NAME(); if (config_data == nullptr || config_info == nullptr) { return AJM_ERROR_INVALID_PARAMETER; } auto* handle = Atrac9GetHandle(); if (handle == nullptr) { return AJM_ERROR_INVALID_PARAMETER; } uint8_t config[ATRAC9_CONFIG_DATA_SIZE] {}; std::memcpy(config, config_data, sizeof(config)); Atrac9CodecInfo codec_info {}; const int init_result = Atrac9InitDecoder(handle, config); const int info_result = init_result == 0 ? Atrac9GetCodecInfo(handle, &codec_info) : init_result; Atrac9ReleaseHandle(handle); if (info_result != 0 || codec_info.channels <= 0 || codec_info.samplingRate <= 0 || codec_info.frameSamples <= 0 || codec_info.framesInSuperframe <= 0 || codec_info.superframeSize <= 0) { return AJM_ERROR_INVALID_PARAMETER; } config_info->channels = static_cast(codec_info.channels); config_info->sample_rate = static_cast(codec_info.samplingRate); config_info->frame_samples_per_channel = static_cast(codec_info.frameSamples); config_info->superframe_samples_per_channel = static_cast(codec_info.frameSamples) * static_cast(codec_info.framesInSuperframe); config_info->superframe_size = static_cast(codec_info.superframeSize); return OK; } class AjmOpusDecoder final: public AjmDecoder { public: AjmOpusDecoder(uint32_t channels, uint32_t sample_rate, AjmSampleEncoding encoding, uint64_t flags) : AjmDecoder(channels, sample_rate, encoding) { (void)flags; } AjmDecodeResult Initialize(const void* codec_parameters, size_t codec_parameters_size) override { auto result = MakeResult(); if (codec_parameters == nullptr || codec_parameters_size < sizeof(AjmDecOpusInitializeParameters)) { result.result = AJM_RESULT_INVALID_PARAMETER; return result; } const auto* params = static_cast(codec_parameters); if (params->channel_num == 0 || params->channel_num > AJM_DEC_OPUS_MAX_CHANNELS_FOR_10CH || params->sample_rate == 0) { result.result = AJM_RESULT_INVALID_PARAMETER; return result; } SetFormat(params->channel_num, params->sample_rate, m_sample_encoding); m_mapping_family = params->mapping_family; m_total_decoded_samples = 0; m_is_initialized = true; m_frames_per_packet = 1; LOGF("AJM Opus initialized: %" PRIu32 " Hz, %" PRIu32 " ch, mapping=%" PRIu32 "\n", params->sample_rate, params->channel_num, params->mapping_family); return MakeResult(); } void Reset() override { m_total_decoded_samples = 0; } AjmDecodeResult Decode(const void* input, size_t input_size, void* output, size_t output_size, bool multiple_frames, AjmGaplessState* gapless) override { (void)multiple_frames; auto result = MakeResult(); if (input == nullptr || input_size == 0) { result.result = AJM_RESULT_PARTIAL_INPUT; return result; } if (!m_is_initialized) { result.result = AJM_RESULT_NOT_INITIALIZED; return result; } auto samples_to_write = AJM_DEC_OPUS_FRAME_SAMPLES; auto skip_samples = 0u; if (gapless != nullptr) { skip_samples = std::min(samples_to_write, gapless->current.skip_samples); samples_to_write -= skip_samples; if (gapless->HasSampleLimit()) { samples_to_write = std::min(samples_to_write, gapless->current.total_samples); } } const auto output_bytes = static_cast(samples_to_write) * static_cast(m_channels) * AjmBytesPerSample(m_sample_encoding); if (output_bytes != 0 && output == nullptr) { result.result = AJM_RESULT_NOT_ENOUGH_ROOM; return result; } if (output_bytes > output_size) { result.result = AJM_RESULT_NOT_ENOUGH_ROOM; return result; } if (output_bytes != 0) { std::memset(output, 0, output_bytes); } if (gapless != nullptr) { gapless->current.skip_samples = static_cast(gapless->current.skip_samples - skip_samples); if (gapless->HasSampleLimit()) { gapless->current.total_samples = (gapless->current.total_samples > samples_to_write ? gapless->current.total_samples - samples_to_write : 0); } const auto skipped_samples = AJM_DEC_OPUS_FRAME_SAMPLES - samples_to_write; const auto skipped_total = std::min( std::numeric_limits::max(), static_cast(gapless->current.skipped_samples) + skipped_samples); gapless->current.skipped_samples = static_cast(skipped_total); } result.input_consumed = input_size; result.output_written = output_bytes; result.frames = 1; result.frames_per_packet = m_frames_per_packet; m_total_decoded_samples += samples_to_write; result.total_decoded_samples = m_total_decoded_samples; result.format = GetFormat(); return result; } void WriteCodecInfo(void* output, size_t output_size, const AjmDecodeResult& result) const override { (void)result; if (output == nullptr || output_size < sizeof(AjmSidebandDecOpusCodecInfo)) { return; } auto* info = static_cast(output); info->frames_per_packet = m_frames_per_packet; } [[nodiscard]] size_t CodecInfoSize() const override { return sizeof(AjmSidebandDecOpusCodecInfo); } private: bool m_is_initialized = false; uint32_t m_mapping_family = 0; uint32_t m_frames_per_packet = 1; }; struct AjmInstanceState { uint32_t context = 0; uint32_t codec = 0; uint64_t flags = 0; AjmGaplessState gapless; std::unique_ptr decoder; }; static std::mutex g_ajm_instances_mutex; static std::unordered_map g_ajm_instances; static bool AjmCodecIsSupported(uint32_t codec) { return codec == static_cast(AjmCodec::DecMp3) || codec == static_cast(AjmCodec::DecAt9) || codec == static_cast(AjmCodec::DecM4aac) || codec == static_cast(AjmCodec::DecOpus) || codec == static_cast(AjmCodec::DecUnknownNewABI); } static bool AjmLogCodecSupport(uint32_t codec) { const auto supported = AjmCodecIsSupported(codec); if (supported) { static std::atomic_bool logged {false}; if (!logged.exchange(true, std::memory_order_relaxed)) { std::printf("AJM codec=%u, name=%s\n", codec, AjmCodecName(codec)); } } else { std::printf("AJM codec=%u, name=%s\n", codec, AjmCodecName(codec)); std::printf("AJM unsupported codec=%u (%s)\n", codec, AjmCodecName(codec)); } return supported; } static std::unique_ptr AjmCreateDecoder(uint32_t codec, uint64_t flags) { if (!AjmCodecIsSupported(codec)) { return {}; } auto channels = AjmGetFlagChannelCount(flags); if (channels == 0) { channels = 2; } const auto encoding = AjmGetFlagSampleEncoding(flags); switch (codec) { case static_cast(AjmCodec::DecMp3): return std::make_unique(channels, 48000, encoding, flags); case static_cast(AjmCodec::DecAt9): return std::make_unique(channels, 48000, encoding, flags); case static_cast(AjmCodec::DecM4aac): return std::make_unique(channels, 48000, encoding, flags); case static_cast(AjmCodec::DecOpus): return std::make_unique(channels, 48000, encoding, flags); default: break; } return {}; } static AjmInstanceState* AjmFindInstanceLocked(uint32_t instance) { const auto it = g_ajm_instances.find(instance); if (it == g_ajm_instances.end()) { return nullptr; } return &it->second; } static uint8_t* AjmReserveJob(AjmBatchInfo* info, size_t job_size) { EXIT_NOT_IMPLEMENTED(info == nullptr); if (info->offset + job_size > info->size) { return nullptr; } auto* job = static_cast(info->buffer) + info->offset; info->last_good_job = job; info->last_good_job_ra = nullptr; info->offset += job_size; std::memset(job, 0, job_size); return job; } static int AjmAppendJob(AjmBatchInfo* info, size_t job_size, const char* name) { auto* job = AjmReserveJob(info, job_size); if (job == nullptr) { LOGF("\t %s: not enough batch space, offset=0x%016" PRIx64 ", size=0x%016" PRIx64 ", job=0x%016" PRIx64 "\n", name, static_cast(info->offset), static_cast(info->size), static_cast(job_size)); return AJM_ERROR_JOB_CREATION; } return OK; } static int32_t AjmClampInt32(size_t value) { return static_cast( std::min(value, static_cast(std::numeric_limits::max()))); } static void AjmWriteResult(void* result, size_t size, const AjmDecodeResult& decode_result = {}) { if (result == nullptr || size == 0) { return; } std::memset(result, 0, size); if (size >= AJM_SIDEBAND_RESULT_SIZE) { auto* sideband = static_cast(result); sideband->result = decode_result.result; sideband->internal_result = decode_result.internal_result; } } static void AjmWriteStreamResult(void* result, size_t result_size, const AjmDecodeResult& decode_result, bool multiple_frames) { if (result == nullptr) { return; } AjmWriteResult(result, result_size, decode_result); if (result_size >= AJM_SIDEBAND_RESULT_SIZE + AJM_SIDEBAND_STREAM_SIZE) { auto* stream = reinterpret_cast(static_cast(result) + AJM_SIDEBAND_RESULT_SIZE); stream->size_consumed = AjmClampInt32(decode_result.input_consumed); stream->size_produced = AjmClampInt32(decode_result.output_written); stream->total_decoded_samples = decode_result.total_decoded_samples; } if (multiple_frames && result_size >= AJM_SIDEBAND_RESULT_SIZE + AJM_SIDEBAND_STREAM_SIZE + AJM_SIDEBAND_MFRAME_SIZE) { auto* mframe = reinterpret_cast( static_cast(result) + AJM_SIDEBAND_RESULT_SIZE + AJM_SIDEBAND_STREAM_SIZE); mframe->num_frames = decode_result.frames; } } static void AjmWriteFormat(void* result, size_t result_size, const AjmSidebandFormat& format, const AjmDecodeResult& decode_result = {}) { if (result == nullptr || result_size < AJM_SIDEBAND_RESULT_SIZE + AJM_SIDEBAND_FORMAT_SIZE) { AjmWriteResult(result, result_size, decode_result); return; } AjmWriteResult(result, result_size, decode_result); auto* format_output = reinterpret_cast(static_cast(result) + AJM_SIDEBAND_RESULT_SIZE); *format_output = format; } static void AjmWriteCodecInfo(AjmDecoder* decoder, void* result, size_t result_size, const AjmDecodeResult& decode_result) { if (decoder == nullptr || result == nullptr || result_size <= AJM_SIDEBAND_RESULT_SIZE) { return; } decoder->WriteCodecInfo(static_cast(result) + AJM_SIDEBAND_RESULT_SIZE, result_size - AJM_SIDEBAND_RESULT_SIZE, decode_result); } static void AjmWriteSideband(uint64_t flags, void* sideband_output, size_t sideband_output_size, AjmDecoder* decoder, const AjmGaplessState* gapless, const AjmDecodeResult& decode_result) { if (sideband_output == nullptr || sideband_output_size == 0) { return; } AjmWriteResult(sideband_output, sideband_output_size, decode_result); size_t offset = AJM_SIDEBAND_RESULT_SIZE; if ((flags & AJM_FLAG_RUN_GET_CODEC_INFO) != 0 && decoder != nullptr) { const auto codec_info_size = decoder->CodecInfoSize(); if (offset + codec_info_size <= sideband_output_size) { decoder->WriteCodecInfo(static_cast(sideband_output) + offset, sideband_output_size - offset, decode_result); offset += codec_info_size; } } if ((flags & AJM_FLAG_SIDEBAND_RESAMPLE_INFO) != 0 && offset + AJM_SIDEBAND_RESAMPLE_INFO_SIZE <= sideband_output_size) { auto* info = reinterpret_cast( static_cast(sideband_output) + offset); info->ratio = 1.0f; offset += AJM_SIDEBAND_RESAMPLE_INFO_SIZE; } if ((flags & AJM_FLAG_SIDEBAND_GAPLESS) != 0 && offset + AJM_SIDEBAND_GAPLESS_SIZE <= sideband_output_size) { auto* gapless_output = reinterpret_cast( static_cast(sideband_output) + offset); if (gapless != nullptr) { *gapless_output = gapless->current; } offset += AJM_SIDEBAND_GAPLESS_SIZE; } if ((flags & AJM_FLAG_SIDEBAND_FORMAT) != 0 && offset + AJM_SIDEBAND_FORMAT_SIZE <= sideband_output_size) { auto* format = reinterpret_cast(static_cast(sideband_output) + offset); *format = decode_result.format; offset += AJM_SIDEBAND_FORMAT_SIZE; } if ((flags & AJM_FLAG_SIDEBAND_STREAM) != 0 && offset + AJM_SIDEBAND_STREAM_SIZE <= sideband_output_size) { auto* stream = reinterpret_cast(static_cast(sideband_output) + offset); stream->size_consumed = AjmClampInt32(decode_result.input_consumed); stream->size_produced = AjmClampInt32(decode_result.output_written); stream->total_decoded_samples = decode_result.total_decoded_samples; offset += AJM_SIDEBAND_STREAM_SIZE; } if ((flags & AJM_FLAG_RUN_MULTIPLE_FRAMES) != 0 && offset + AJM_SIDEBAND_MFRAME_SIZE <= sideband_output_size) { auto* mframe = reinterpret_cast(static_cast(sideband_output) + offset); mframe->num_frames = decode_result.frames; } } static bool AjmCodecIsValid(uint32_t codec) { switch (codec) { case static_cast(AjmCodec::DecMp3): case static_cast(AjmCodec::DecAt9): case static_cast(AjmCodec::DecM4aac): case 3: case 4: case 12: case 13: case static_cast(AjmCodec::DecUnknownNewABI): case 22: case 23: case static_cast(AjmCodec::DecOpus): return true; default: return false; } } static AjmDecodeResult AjmDecodeInstance(uint32_t instance, const void* input, size_t input_size, void* output, size_t output_size, bool multiple_frames) { std::scoped_lock lock(g_ajm_instances_mutex); auto* state = AjmFindInstanceLocked(instance); if (state == nullptr || state->decoder == nullptr) { AjmDecodeResult result {}; result.result = AJM_RESULT_INVALID_PARAMETER; return result; } return state->decoder->Decode(input, input_size, output, output_size, multiple_frames, &state->gapless); } static AjmDecodeResult AjmDecodeSplitInstance(uint32_t instance, const AjmBuffer* input_buffers, size_t input_buffers_num, const AjmBuffer* output_buffers, size_t output_buffers_num, bool multiple_frames) { size_t input_size = 0; size_t output_size = 0; for (size_t i = 0; i < input_buffers_num && input_buffers != nullptr; i++) { input_size += input_buffers[i].size; } for (size_t i = 0; i < output_buffers_num && output_buffers != nullptr; i++) { output_size += output_buffers[i].size; } std::vector input(input_size); size_t input_offset = 0; for (size_t i = 0; i < input_buffers_num && input_buffers != nullptr; i++) { if (input_buffers[i].address != nullptr && input_buffers[i].size != 0) { std::memcpy(input.data() + input_offset, input_buffers[i].address, input_buffers[i].size); } input_offset += input_buffers[i].size; } std::vector output(output_size); auto result = AjmDecodeInstance(instance, input.data(), input.size(), output.data(), output.size(), multiple_frames); size_t output_offset = 0; for (size_t i = 0; i < output_buffers_num && output_buffers != nullptr; i++) { const auto bytes = std::min( output_buffers[i].size, (result.output_written > output_offset ? result.output_written - output_offset : 0)); if (output_buffers[i].address != nullptr && bytes != 0) { std::memcpy(output_buffers[i].address, output.data() + output_offset, bytes); } output_offset += output_buffers[i].size; } return result; } static AjmDecodeResult AjmInitializeInstance(uint32_t instance, const void* codec_parameters, size_t codec_parameters_size) { std::scoped_lock lock(g_ajm_instances_mutex); auto* state = AjmFindInstanceLocked(instance); if (state == nullptr || state->decoder == nullptr) { AjmDecodeResult result {}; result.result = AJM_RESULT_INVALID_PARAMETER; return result; } state->gapless.Reset(); return state->decoder->Initialize(codec_parameters, codec_parameters_size); } static AjmDecodeResult AjmControlInstance(uint32_t instance, uint64_t flags, const void* sideband_input, size_t sideband_input_size, AjmDecoder** out_decoder, AjmGaplessState** out_gapless) { std::scoped_lock lock(g_ajm_instances_mutex); auto* state = AjmFindInstanceLocked(instance); if (state == nullptr || state->decoder == nullptr) { AjmDecodeResult result {}; result.result = AJM_RESULT_INVALID_PARAMETER; return result; } auto* decoder = state->decoder.get(); if (out_decoder != nullptr) { *out_decoder = decoder; } if (out_gapless != nullptr) { *out_gapless = &state->gapless; } AjmDecodeResult result = decoder->MakeResult(); if ((flags & AJM_FLAG_SIDEBAND_GAPLESS) != 0) { if (sideband_input == nullptr || sideband_input_size < AJM_SIDEBAND_GAPLESS_SIZE) { result.result = AJM_RESULT_INVALID_PARAMETER; return result; } state->gapless.Set(*static_cast(sideband_input), (flags & AJM_FLAG_CONTROL_RESET) != 0); sideband_input = static_cast(sideband_input) + AJM_SIDEBAND_GAPLESS_SIZE; sideband_input_size -= AJM_SIDEBAND_GAPLESS_SIZE; } if ((flags & AJM_FLAG_CONTROL_RESET) != 0) { state->gapless.Reset(); decoder->Reset(); result = decoder->MakeResult(); } if ((flags & AJM_FLAG_CONTROL_INITIALIZE) != 0) { state->gapless.Reset(); result = decoder->Initialize(sideband_input, sideband_input_size); } return result; } static AjmDecodeResult AjmSetGaplessDecode(uint32_t instance, const void* gapless_decode, int reset) { const auto flags = AJM_FLAG_SIDEBAND_GAPLESS | (reset != 0 ? AJM_FLAG_CONTROL_RESET : 0); return AjmControlInstance(instance, flags, gapless_decode, sizeof(AjmSidebandGaplessDecode), nullptr, nullptr); } static AjmDecodeResult AjmGetGaplessDecode(uint32_t instance, AjmSidebandGaplessDecode* gapless_decode) { std::scoped_lock lock(g_ajm_instances_mutex); auto* state = AjmFindInstanceLocked(instance); if (state == nullptr || state->decoder == nullptr) { AjmDecodeResult result {}; result.result = AJM_RESULT_INVALID_PARAMETER; return result; } if (gapless_decode != nullptr) { *gapless_decode = state->gapless.current; } return state->decoder->MakeResult(); } static AjmSidebandFormat AjmGetInstanceFormat(uint32_t instance, AjmDecodeResult* out_result = nullptr, AjmDecoder** out_decoder = nullptr, AjmGaplessState** out_gapless = nullptr) { std::scoped_lock lock(g_ajm_instances_mutex); auto* state = AjmFindInstanceLocked(instance); if (state == nullptr || state->decoder == nullptr) { if (out_result != nullptr) { out_result->result = AJM_RESULT_INVALID_PARAMETER; } return AjmMakeFormat(2, 48000, AjmSampleEncoding::S16); } if (out_decoder != nullptr) { *out_decoder = state->decoder.get(); } if (out_gapless != nullptr) { *out_gapless = &state->gapless; } if (out_result != nullptr) { *out_result = state->decoder->MakeResult(); out_result->format = state->decoder->GetFormat(); } return state->decoder->GetFormat(); } int KYTY_SYSV_ABI AjmInitialize(int64_t reserved, uint32_t* context) { PRINT_NAME(); EXIT_NOT_IMPLEMENTED(context == nullptr); if (reserved != 0) { LOGF("\t ignoring reserved = 0x%016" PRIx64 "\n", static_cast(reserved)); } *context = 1; return OK; } int KYTY_SYSV_ABI AjmFinalize(uint32_t context) { PRINT_NAME(); LOGF("\t context = %" PRIu32 "\n", context); return OK; } int KYTY_SYSV_ABI AjmModuleRegister(uint32_t context, uint32_t codec, int64_t reserved) { PRINT_NAME(); if (reserved != 0) { LOGF("\t ignoring reserved = 0x%016" PRIx64 "\n", static_cast(reserved)); } LOGF("\t codec = %u\n", codec); LOGF("\t %s\n", AjmCodecName(codec)); const auto valid = AjmCodecIsValid(codec); const auto supported = valid && AjmLogCodecSupport(codec); int result = OK; if (context != 1) { result = AJM_ERROR_INVALID_CONTEXT; } else if (!valid) { result = AJM_ERROR_INVALID_PARAMETER; } else if (!supported) { result = AJM_ERROR_CODEC_NOT_SUPPORTED; } if (!supported) { LOGF("\t codec is not implemented\n"); } return result; } int KYTY_SYSV_ABI AjmModuleUnregister(uint32_t context, uint32_t codec) { PRINT_NAME(); LOGF("\t context = %" PRIu32 ", codec = %" PRIu32 "\n", context, codec); return OK; } int KYTY_SYSV_ABI AjmInstanceCreate(uint32_t context, uint32_t codec, uint64_t flags, uint32_t* instance) { PRINT_NAME(); EXIT_NOT_IMPLEMENTED(instance == nullptr); const auto supported = AjmLogCodecSupport(codec); const auto slot = (g_ajm_next_instance.fetch_add(1, std::memory_order_relaxed) & 0x3fffu); *instance = (codec << 14u) | slot; auto state = AjmInstanceState {}; state.context = context; state.codec = codec; state.flags = flags; state.decoder = AjmCreateDecoder(codec, flags); { std::scoped_lock lock(g_ajm_instances_mutex); g_ajm_instances[*instance] = std::move(state); } LOGF("\t context = %" PRIu32 "\n" "\t codec = %" PRIu32 "\n" "\t flags = 0x%016" PRIx64 "\n" "\t instance = 0x%08" PRIx32 "\n", context, codec, flags, *instance); if (!supported) { LOGF("\t unsupported codec instance created without decoder\n"); } return OK; } int KYTY_SYSV_ABI AjmInstanceDestroy(uint32_t context, uint32_t instance) { PRINT_NAME(); LOGF("\t context = %" PRIu32 ", instance = 0x%08" PRIx32 "\n", context, instance); std::scoped_lock lock(g_ajm_instances_mutex); g_ajm_instances.erase(instance); return OK; } int KYTY_SYSV_ABI AjmMemoryRegister(uint32_t context, void* ptr, size_t pages) { PRINT_NAME(); LOGF("\t context = %" PRIu32 ", ptr = 0x%016" PRIx64 ", pages = 0x%016" PRIx64 "\n", context, reinterpret_cast(ptr), static_cast(pages)); return OK; } int KYTY_SYSV_ABI AjmMemoryUnregister(uint32_t context, void* ptr) { PRINT_NAME(); LOGF("\t context = %" PRIu32 ", ptr = 0x%016" PRIx64 "\n", context, reinterpret_cast(ptr)); return OK; } int KYTY_SYSV_ABI AjmBatchInitialize(void* buffer, size_t size, AjmBatchInfo* info) { PRINT_NAME(); if (buffer == nullptr || info == nullptr) { return AJM_ERROR_INVALID_PARAMETER; } info->buffer = buffer; info->offset = 0; info->size = size; info->last_good_job = nullptr; info->last_good_job_ra = nullptr; return OK; } int KYTY_SYSV_ABI AjmBatchStart(uint32_t context, const AjmBatchInfo* info, int priority, AjmBatchError* error, uint32_t* batch) { PRINT_NAME(); EXIT_NOT_IMPLEMENTED(info == nullptr); EXIT_NOT_IMPLEMENTED(batch == nullptr); (void)context; (void)priority; if (error != nullptr) { std::memset(error, 0, sizeof(AjmBatchError)); } *batch = g_ajm_next_batch.fetch_add(1, std::memory_order_relaxed); return OK; } int KYTY_SYSV_ABI AjmBatchWait(uint32_t context, uint32_t batch, uint32_t timeout, AjmBatchError* error) { (void)context; (void)batch; (void)timeout; if (error != nullptr) { std::memset(error, 0, sizeof(AjmBatchError)); } return OK; } int KYTY_SYSV_ABI AjmBatchCancel(uint32_t context, uint32_t batch) { PRINT_NAME(); LOGF("\t context = %" PRIu32 ", batch = %" PRIu32 "\n", context, batch); return OK; } int KYTY_SYSV_ABI AjmBatchErrorDump(const AjmBatchInfo* info, AjmBatchError* error) { PRINT_NAME(); if (error != nullptr) { std::memset(error, 0, sizeof(AjmBatchError)); } LOGF("\t info = 0x%016" PRIx64 "\n", reinterpret_cast(info)); return OK; } int KYTY_SYSV_ABI AjmBatchJobInitialize(AjmBatchInfo* info, uint32_t instance, const void* codec_parameters, size_t codec_parameters_size, void* result) { PRINT_NAME(); EXIT_NOT_IMPLEMENTED(info == nullptr); const auto decode_result = AjmInitializeInstance(instance, codec_parameters, codec_parameters_size); AjmWriteResult(result, AJM_SIDEBAND_RESULT_SIZE, decode_result); LOGF("\t instance = 0x%08" PRIx32 ", codec_parameters = 0x%016" PRIx64 ", size = 0x%016" PRIx64 "\n", instance, reinterpret_cast(codec_parameters), static_cast(codec_parameters_size)); return AjmAppendJob(info, AJM_JOB_CONTROL_SIZE, "initialize"); } int KYTY_SYSV_ABI AjmBatchJobClearContext(AjmBatchInfo* info, uint32_t instance, void* result) { PRINT_NAME(); EXIT_NOT_IMPLEMENTED(info == nullptr); const auto decode_result = AjmControlInstance(instance, AJM_FLAG_CONTROL_RESET, nullptr, 0, nullptr, nullptr); AjmWriteResult(result, AJM_SIDEBAND_RESULT_SIZE, decode_result); LOGF("\t instance = 0x%08" PRIx32 "\n", instance); return AjmAppendJob(info, AJM_JOB_CONTROL_SIZE, "clear-context"); } int KYTY_SYSV_ABI AjmBatchJobDecode(AjmBatchInfo* info, uint32_t instance, const void* bitstream_input, size_t bitstream_input_size, void* pcm_output, size_t pcm_output_size, void* result) { PRINT_NAME(); EXIT_NOT_IMPLEMENTED(info == nullptr); const auto decode_result = AjmDecodeInstance(instance, bitstream_input, bitstream_input_size, pcm_output, pcm_output_size, true); AjmWriteStreamResult( result, AJM_SIDEBAND_RESULT_SIZE + AJM_SIDEBAND_STREAM_SIZE + AJM_SIDEBAND_MFRAME_SIZE, decode_result, true); LOGF("\t instance = 0x%08" PRIx32 ", input = 0x%016" PRIx64 ", input_size = 0x%016" PRIx64 ", output = 0x%016" PRIx64 ", output_size = 0x%016" PRIx64 "\n", instance, reinterpret_cast(bitstream_input), static_cast(bitstream_input_size), reinterpret_cast(pcm_output), static_cast(pcm_output_size)); return AjmAppendJob(info, AJM_JOB_RUN_SIZE, "decode"); } int KYTY_SYSV_ABI AjmBatchJobDecodeSingle(AjmBatchInfo* info, uint32_t instance, const void* bitstream_input, size_t bitstream_input_size, void* pcm_output, size_t pcm_output_size, void* result) { PRINT_NAME(); EXIT_NOT_IMPLEMENTED(info == nullptr); const auto decode_result = AjmDecodeInstance(instance, bitstream_input, bitstream_input_size, pcm_output, pcm_output_size, false); AjmWriteStreamResult(result, AJM_SIDEBAND_RESULT_SIZE + AJM_SIDEBAND_STREAM_SIZE, decode_result, false); LOGF("\t instance = 0x%08" PRIx32 "\n", instance); return AjmAppendJob(info, AJM_JOB_RUN_SIZE, "decode-single"); } int KYTY_SYSV_ABI AjmBatchJobDecodeSplit(AjmBatchInfo* info, uint32_t instance, const AjmBuffer* input_buffers, size_t input_buffers_num, const AjmBuffer* output_buffers, size_t output_buffers_num, void* result) { PRINT_NAME(); EXIT_NOT_IMPLEMENTED(info == nullptr); const auto decode_result = AjmDecodeSplitInstance(instance, input_buffers, input_buffers_num, output_buffers, output_buffers_num, true); AjmWriteStreamResult( result, AJM_SIDEBAND_RESULT_SIZE + AJM_SIDEBAND_STREAM_SIZE + AJM_SIDEBAND_MFRAME_SIZE, decode_result, true); LOGF("\t instance = 0x%08" PRIx32 ", input_buffers = 0x%016" PRIx64 ", input_num = 0x%016" PRIx64 ", output_buffers = 0x%016" PRIx64 ", output_num = 0x%016" PRIx64 "\n", instance, reinterpret_cast(input_buffers), static_cast(input_buffers_num), reinterpret_cast(output_buffers), static_cast(output_buffers_num)); return AjmAppendJob(info, 16 * (input_buffers_num + output_buffers_num) + 32, "decode-split"); } int KYTY_SYSV_ABI AjmBatchJobEncode(AjmBatchInfo* info, uint32_t instance, const void* pcm_input, size_t pcm_input_size, void* bitstream_output, size_t bitstream_output_size, void* result) { PRINT_NAME(); EXIT_NOT_IMPLEMENTED(info == nullptr); AjmDecodeResult encode_result {}; encode_result.input_consumed = pcm_input_size; encode_result.output_written = bitstream_output_size; encode_result.frames = (pcm_input_size != 0 || bitstream_output_size != 0 ? 1u : 0u); encode_result.total_decoded_samples = bitstream_output_size; if (bitstream_output != nullptr && bitstream_output_size != 0) { std::memset(bitstream_output, 0, bitstream_output_size); } AjmWriteStreamResult( result, AJM_SIDEBAND_RESULT_SIZE + AJM_SIDEBAND_STREAM_SIZE + AJM_SIDEBAND_MFRAME_SIZE, encode_result, true); LOGF("\t instance = 0x%08" PRIx32 ", input = 0x%016" PRIx64 ", output = 0x%016" PRIx64 "\n", instance, reinterpret_cast(pcm_input), reinterpret_cast(bitstream_output)); return AjmAppendJob(info, AJM_JOB_RUN_SIZE, "encode"); } int KYTY_SYSV_ABI AjmBatchJobGetInfo(AjmBatchInfo* info, uint32_t instance, void* result) { PRINT_NAME(); EXIT_NOT_IMPLEMENTED(info == nullptr); AjmDecodeResult decode_result {}; const auto format = AjmGetInstanceFormat(instance, &decode_result); AjmWriteFormat(result, AJM_SIDEBAND_RESULT_SIZE + AJM_SIDEBAND_FORMAT_SIZE, format, decode_result); LOGF("\t instance = 0x%08" PRIx32 "\n", instance); return AjmAppendJob(info, AJM_JOB_RUN_SIZE, "get-info"); } int KYTY_SYSV_ABI AjmBatchJobGetCodecInfo(AjmBatchInfo* info, uint32_t instance, void* result, size_t result_size) { PRINT_NAME(); EXIT_NOT_IMPLEMENTED(info == nullptr); AjmDecodeResult decode_result {}; AjmDecoder* decoder = nullptr; (void)AjmGetInstanceFormat(instance, &decode_result, &decoder); AjmWriteResult(result, result_size, decode_result); AjmWriteCodecInfo(decoder, result, result_size, decode_result); LOGF("\t instance = 0x%08" PRIx32 ", result_size = 0x%016" PRIx64 "\n", instance, static_cast(result_size)); return AjmAppendJob(info, AJM_JOB_RUN_SIZE, "get-codec-info"); } int KYTY_SYSV_ABI AjmBatchJobSetGaplessDecode(AjmBatchInfo* info, uint32_t instance, const void* gapless_decode, int reset, void* result) { PRINT_NAME(); EXIT_NOT_IMPLEMENTED(info == nullptr); const auto decode_result = AjmSetGaplessDecode(instance, gapless_decode, reset); AjmWriteResult(result, AJM_SIDEBAND_RESULT_SIZE, decode_result); LOGF("\t instance = 0x%08" PRIx32 ", gapless_decode = 0x%016" PRIx64 ", reset = %d\n", instance, reinterpret_cast(gapless_decode), reset); return AjmAppendJob(info, AJM_JOB_CONTROL_SIZE, "set-gapless"); } int KYTY_SYSV_ABI AjmBatchJobGetGaplessDecode(AjmBatchInfo* info, uint32_t instance, void* result) { PRINT_NAME(); EXIT_NOT_IMPLEMENTED(info == nullptr); AjmDecodeResult decode_result {}; AjmSidebandGaplessDecode gapless_decode {}; decode_result = AjmGetGaplessDecode(instance, &gapless_decode); AjmWriteResult(result, AJM_SIDEBAND_RESULT_SIZE + AJM_SIDEBAND_GAPLESS_SIZE, decode_result); if (result != nullptr) { auto* gapless = reinterpret_cast(static_cast(result) + AJM_SIDEBAND_RESULT_SIZE); *gapless = gapless_decode; } LOGF("\t instance = 0x%08" PRIx32 "\n", instance); return AjmAppendJob(info, AJM_JOB_RUN_SIZE, "get-gapless"); } int KYTY_SYSV_ABI AjmBatchJobSetResampleParameters(AjmBatchInfo* info, uint32_t instance, float ratio, uint32_t flags, void* result) { PRINT_NAME(); EXIT_NOT_IMPLEMENTED(info == nullptr); AjmWriteResult(result, AJM_SIDEBAND_RESULT_SIZE); LOGF("\t instance = 0x%08" PRIx32 ", ratio = %f, flags = 0x%08" PRIx32 "\n", instance, static_cast(ratio), flags); return AjmAppendJob(info, AJM_JOB_SET_RESAMPLE_PARAMETERS_SIZE, "set-resample"); } int KYTY_SYSV_ABI AjmBatchJobSetResampleParametersEx(AjmBatchInfo* info, uint32_t instance, float ratio_start, float ratio_change_per_sample, uint32_t flags, void* result) { PRINT_NAME(); EXIT_NOT_IMPLEMENTED(info == nullptr); AjmWriteResult(result, AJM_SIDEBAND_RESULT_SIZE); LOGF("\t instance = 0x%08" PRIx32 ", ratio_start = %f, ratio_change = %f, flags = 0x%08" PRIx32 "\n", instance, static_cast(ratio_start), static_cast(ratio_change_per_sample), flags); return AjmAppendJob(info, AJM_JOB_SET_RESAMPLE_PARAMETERS_SIZE, "set-resample-ex"); } int KYTY_SYSV_ABI AjmBatchJobGetResampleInfo(AjmBatchInfo* info, uint32_t instance, void* result) { PRINT_NAME(); EXIT_NOT_IMPLEMENTED(info == nullptr); AjmDecodeResult decode_result {}; (void)AjmGetInstanceFormat(instance, &decode_result); AjmWriteResult(result, AJM_SIDEBAND_RESULT_SIZE + AJM_SIDEBAND_RESAMPLE_INFO_SIZE, decode_result); if (result != nullptr) { auto* resample = reinterpret_cast(static_cast(result) + AJM_SIDEBAND_RESULT_SIZE); resample->ratio = 1.0f; } LOGF("\t instance = 0x%08" PRIx32 "\n", instance); return AjmAppendJob(info, AJM_JOB_RUN_SIZE, "get-resample-info"); } int KYTY_SYSV_ABI AjmBatchJobGetStatistics(AjmBatchInfo* info, float interval, void* result) { PRINT_NAME(); EXIT_NOT_IMPLEMENTED(info == nullptr); AjmWriteResult(result, sizeof(AjmGetStatisticsResult)); (void)interval; return AjmAppendJob(info, AJM_JOB_GET_STATISTICS_SIZE, "get-statistics"); } int KYTY_SYSV_ABI AjmBatchJobControl(AjmBatchInfo* info, uint32_t instance, uint64_t flags, const void* sideband_input, size_t sideband_input_size, void* sideband_output, size_t sideband_output_size) { PRINT_NAME(); EXIT_NOT_IMPLEMENTED(info == nullptr); AjmDecoder* decoder = nullptr; AjmGaplessState* gapless = nullptr; const auto decode_result = AjmControlInstance(instance, flags, sideband_input, sideband_input_size, &decoder, &gapless); AjmWriteSideband(flags, sideband_output, sideband_output_size, decoder, gapless, decode_result); LOGF("\t instance = 0x%08" PRIx32 ", flags = 0x%016" PRIx64 ", input = 0x%016" PRIx64 ", input_size = 0x%016" PRIx64 ", output = 0x%016" PRIx64 ", output_size = 0x%016" PRIx64 "\n", instance, flags, reinterpret_cast(sideband_input), static_cast(sideband_input_size), reinterpret_cast(sideband_output), static_cast(sideband_output_size)); return AjmAppendJob(info, AJM_JOB_CONTROL_SIZE, "control"); } int KYTY_SYSV_ABI AjmBatchJobRun(AjmBatchInfo* info, uint32_t instance, uint64_t flags, const void* data_input, size_t data_input_size, void* data_output, size_t data_output_size, void* sideband_output, size_t sideband_output_size) { PRINT_NAME(); EXIT_NOT_IMPLEMENTED(info == nullptr); auto decode_result = AjmDecodeInstance(instance, data_input, data_input_size, data_output, data_output_size, (flags & AJM_FLAG_RUN_MULTIPLE_FRAMES) != 0); AjmDecoder* decoder = nullptr; AjmGaplessState* gapless = nullptr; (void)AjmGetInstanceFormat(instance, nullptr, &decoder, &gapless); AjmWriteSideband(flags, sideband_output, sideband_output_size, decoder, gapless, decode_result); LOGF("\t instance = 0x%08" PRIx32 ", flags = 0x%016" PRIx64 ", input = 0x%016" PRIx64 ", input_size = 0x%016" PRIx64 ", output = 0x%016" PRIx64 ", output_size = 0x%016" PRIx64 "\n", instance, flags, reinterpret_cast(data_input), static_cast(data_input_size), reinterpret_cast(data_output), static_cast(data_output_size)); return AjmAppendJob(info, AJM_JOB_RUN_SIZE, "run"); } int KYTY_SYSV_ABI AjmBatchJobRunSplit(AjmBatchInfo* info, uint32_t instance, uint64_t flags, const AjmBuffer* input_buffers, size_t input_buffers_num, const AjmBuffer* output_buffers, size_t output_buffers_num, void* sideband_output, size_t sideband_output_size) { PRINT_NAME(); EXIT_NOT_IMPLEMENTED(info == nullptr); auto decode_result = AjmDecodeSplitInstance(instance, input_buffers, input_buffers_num, output_buffers, output_buffers_num, (flags & AJM_FLAG_RUN_MULTIPLE_FRAMES) != 0); AjmDecoder* decoder = nullptr; AjmGaplessState* gapless = nullptr; (void)AjmGetInstanceFormat(instance, nullptr, &decoder, &gapless); AjmWriteSideband(flags, sideband_output, sideband_output_size, decoder, gapless, decode_result); return AjmAppendJob(info, 16 * (input_buffers_num + output_buffers_num) + 32, "run-split"); } const char* KYTY_SYSV_ABI AjmStrError(int error) { PRINT_NAME(); LOGF("\t error = %d\n", error); return "AJM"; } } // namespace Libs::Audio::Ajm