diff --git a/src/libs/ajm.cpp b/src/libs/ajm.cpp index 89473ad62..06b2dc27e 100644 --- a/src/libs/ajm.cpp +++ b/src/libs/ajm.cpp @@ -1,6 +1,10 @@ #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" @@ -58,47 +62,6 @@ struct AjmSidebandMFrame { uint32_t reserved; }; -struct AjmSidebandFormat { - uint32_t channel_num; - uint32_t channel_mask; - uint32_t sampling_frequency; - uint32_t sample_encoding; - uint32_t bitrate; - uint32_t reserved; -}; - -struct AjmSidebandGaplessDecode { - uint32_t total_samples; - uint16_t skip_samples; - uint16_t skipped_samples; -}; - -struct AjmGaplessState { - AjmSidebandGaplessDecode init {}; - AjmSidebandGaplessDecode current {}; - - [[nodiscard]] bool HasSampleLimit() const { - return init.total_samples != 0 && - init.total_samples != std::numeric_limits::max(); - } - - [[nodiscard]] bool IsEnd() const { return HasSampleLimit() && current.total_samples == 0; } - - void Reset() { - current = init; - current.skipped_samples = 0; - } - - void Set(const AjmSidebandGaplessDecode& params, bool reset) { - init.total_samples = params.total_samples; - init.skip_samples = params.skip_samples; - if (reset || (current.total_samples == 0 && current.skip_samples == 0 && - current.skipped_samples == 0)) { - Reset(); - } - } -}; - struct AjmSidebandResampleInfo { float ratio; int32_t num_samples; @@ -130,34 +93,10 @@ enum class AjmCodec : uint32_t { DecOpus = 24, }; -enum class AjmSampleEncoding : uint32_t { - S16 = 0, - S32 = 1, - Float = 2, -}; - -struct AjmDecodeResult { - int32_t result = OK; - int32_t internal_result = OK; - size_t input_consumed = 0; - size_t output_written = 0; - uint64_t total_decoded_samples = 0; - uint32_t frames = 0; - uint32_t frames_per_packet = 0; - AjmSidebandFormat format {}; -}; - 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 int32_t AJM_RESULT_NOT_INITIALIZED = 0x00000001; -constexpr int32_t AJM_RESULT_INVALID_DATA = 0x00000002; -constexpr int32_t AJM_RESULT_INVALID_PARAMETER = 0x00000004; -constexpr int32_t AJM_RESULT_PARTIAL_INPUT = 0x00000008; -constexpr int32_t AJM_RESULT_NOT_ENOUGH_ROOM = 0x00000010; -constexpr int32_t AJM_RESULT_CODEC_ERROR = 0x40000000; -constexpr int32_t AJM_RESULT_FATAL = static_cast(0x80000000u); constexpr size_t AJM_JOB_CONTROL_SIZE = 48; constexpr size_t AJM_JOB_RUN_SIZE = 64; constexpr size_t AJM_JOB_GET_STATISTICS_SIZE = @@ -181,7 +120,6 @@ 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 uint64_t AJM_INSTANCE_FLAG_CODEC_OFFSET = 32u; constexpr uint32_t AJM_DEC_OPUS_MAX_CHANNELS_FOR_10CH = 10; constexpr uint32_t AJM_DEC_OPUS_FRAME_SAMPLES = 960; @@ -204,41 +142,6 @@ static AjmSampleEncoding AjmGetFlagSampleEncoding(uint64_t flags) { return AjmSampleEncoding::S16; } -static size_t AjmBytesPerSample(AjmSampleEncoding encoding) { - switch (encoding) { - case AjmSampleEncoding::S16: return sizeof(int16_t); - case AjmSampleEncoding::S32: return sizeof(int32_t); - case AjmSampleEncoding::Float: return sizeof(float); - default: EXIT("unsupported AJM PCM sample encoding %u\n", static_cast(encoding)); - } - return sizeof(int16_t); -} - -static uint32_t AjmChannelMask(uint32_t channels) { - switch (channels) { - case 1: return 0x4; - case 2: return 0x3; - case 3: return 0x7; - case 4: return 0x33; - case 5: return 0x607; - case 6: return 0x60f; - case 7: return 0x70f; - case 8: return 0x63f; - default: return 0; - } -} - -static AjmSidebandFormat AjmMakeFormat(uint32_t channels, uint32_t sample_rate, - AjmSampleEncoding encoding) { - AjmSidebandFormat format {}; - format.channel_num = channels; - format.channel_mask = AjmChannelMask(channels); - format.sampling_frequency = sample_rate; - format.sample_encoding = static_cast(encoding); - format.bitrate = 0; - return format; -} - static const char* AjmCodecName(uint32_t codec) { switch (codec) { case static_cast(AjmCodec::DecMp3): return "MP3 decoder"; @@ -255,67 +158,6 @@ static const char* AjmCodecName(uint32_t codec) { } } -class AjmDecoder { -public: - AjmDecoder(uint32_t channels, uint32_t sample_rate, AjmSampleEncoding encoding) - : m_channels(channels), m_sample_rate(sample_rate), m_sample_encoding(encoding) {} - virtual ~AjmDecoder() = default; - - KYTY_CLASS_NO_COPY(AjmDecoder); - - virtual AjmDecodeResult Initialize(const void* codec_parameters, size_t codec_parameters_size) { - (void)codec_parameters; - (void)codec_parameters_size; - return MakeResult(); - } - - virtual void Reset() { m_total_decoded_samples = 0; } - - virtual AjmDecodeResult Decode(const void* input, size_t input_size, void* output, - size_t output_size, bool multiple_frames, - AjmGaplessState* gapless) = 0; - - virtual void WriteCodecInfo(void* output, size_t output_size, - const AjmDecodeResult& result) const { - (void)output; - (void)output_size; - (void)result; - } - - [[nodiscard]] virtual size_t CodecInfoSize() const { return 0; } - - [[nodiscard]] virtual AjmSidebandFormat GetFormat() const { - return AjmMakeFormat(m_channels, m_sample_rate, m_sample_encoding); - } - - [[nodiscard]] AjmDecodeResult MakeResult() const { - AjmDecodeResult result {}; - result.total_decoded_samples = m_total_decoded_samples; - result.format = GetFormat(); - return result; - } - -protected: - void SetFormat(uint32_t channels, uint32_t sample_rate, AjmSampleEncoding encoding) { - m_channels = channels; - m_sample_rate = sample_rate; - m_sample_encoding = encoding; - } - - uint32_t m_channels = 2; - uint32_t m_sample_rate = 48000; - AjmSampleEncoding m_sample_encoding = AjmSampleEncoding::S16; - uint64_t m_total_decoded_samples = 0; -}; - -} // namespace Libs::Audio::Ajm - -#include "libs/ajm/aac_decoder.h" -#include "libs/ajm/atrac9_decoder.h" -#include "libs/ajm/mp3_decoder.h" - -namespace Libs::Audio::Ajm { - static_assert(sizeof(AjmDecAt9ConfigDataInfo) == 20); int KYTY_SYSV_ABI AjmDecAt9ParseConfigData(const void* config_data, diff --git a/src/libs/ajm/aac_decoder.h b/src/libs/ajm/aac_decoder.h index 56c2ab552..9281e3628 100644 --- a/src/libs/ajm/aac_decoder.h +++ b/src/libs/ajm/aac_decoder.h @@ -1,6 +1,7 @@ #pragma once #include "common/logging/log.h" +#include "libs/ajm/decoder.h" #include "libs/ajm/ffmpeg_decoder_common.h" #include diff --git a/src/libs/ajm/atrac9_decoder.h b/src/libs/ajm/atrac9_decoder.h index cb7587c56..b6aab056a 100644 --- a/src/libs/ajm/atrac9_decoder.h +++ b/src/libs/ajm/atrac9_decoder.h @@ -2,6 +2,7 @@ #include "common/assert.h" #include "common/logging/log.h" +#include "libs/ajm/decoder.h" #include #include diff --git a/src/libs/ajm/decoder.h b/src/libs/ajm/decoder.h new file mode 100644 index 000000000..ca404424e --- /dev/null +++ b/src/libs/ajm/decoder.h @@ -0,0 +1,168 @@ +#pragma once + +#include "common/assert.h" +#include "libs/errno.h" + +#include +#include +#include + +namespace Libs::Audio::Ajm { + +struct AjmSidebandFormat { + uint32_t channel_num; + uint32_t channel_mask; + uint32_t sampling_frequency; + uint32_t sample_encoding; + uint32_t bitrate; + uint32_t reserved; +}; + +struct AjmSidebandGaplessDecode { + uint32_t total_samples; + uint16_t skip_samples; + uint16_t skipped_samples; +}; + +struct AjmGaplessState { + AjmSidebandGaplessDecode init {}; + AjmSidebandGaplessDecode current {}; + + [[nodiscard]] bool HasSampleLimit() const { + return init.total_samples != 0 && + init.total_samples != std::numeric_limits::max(); + } + + [[nodiscard]] bool IsEnd() const { return HasSampleLimit() && current.total_samples == 0; } + + void Reset() { + current = init; + current.skipped_samples = 0; + } + + void Set(const AjmSidebandGaplessDecode& params, bool reset) { + init.total_samples = params.total_samples; + init.skip_samples = params.skip_samples; + if (reset || (current.total_samples == 0 && current.skip_samples == 0 && + current.skipped_samples == 0)) { + Reset(); + } + } +}; + +enum class AjmSampleEncoding : uint32_t { + S16 = 0, + S32 = 1, + Float = 2, +}; + +struct AjmDecodeResult { + int32_t result = OK; + int32_t internal_result = OK; + size_t input_consumed = 0; + size_t output_written = 0; + uint64_t total_decoded_samples = 0; + uint32_t frames = 0; + uint32_t frames_per_packet = 0; + AjmSidebandFormat format {}; +}; + +constexpr int32_t AJM_RESULT_NOT_INITIALIZED = 0x00000001; +constexpr int32_t AJM_RESULT_INVALID_DATA = 0x00000002; +constexpr int32_t AJM_RESULT_INVALID_PARAMETER = 0x00000004; +constexpr int32_t AJM_RESULT_PARTIAL_INPUT = 0x00000008; +constexpr int32_t AJM_RESULT_NOT_ENOUGH_ROOM = 0x00000010; +constexpr int32_t AJM_RESULT_CODEC_ERROR = 0x40000000; +constexpr int32_t AJM_RESULT_FATAL = static_cast(0x80000000u); + +constexpr uint64_t AJM_INSTANCE_FLAG_CODEC_OFFSET = 32u; + +inline size_t AjmBytesPerSample(AjmSampleEncoding encoding) { + switch (encoding) { + case AjmSampleEncoding::S16: return sizeof(int16_t); + case AjmSampleEncoding::S32: return sizeof(int32_t); + case AjmSampleEncoding::Float: return sizeof(float); + default: EXIT("unsupported AJM PCM sample encoding %u\n", static_cast(encoding)); + } + return sizeof(int16_t); +} + +inline uint32_t AjmChannelMask(uint32_t channels) { + switch (channels) { + case 1: return 0x4; + case 2: return 0x3; + case 3: return 0x7; + case 4: return 0x33; + case 5: return 0x607; + case 6: return 0x60f; + case 7: return 0x70f; + case 8: return 0x63f; + default: return 0; + } +} + +inline AjmSidebandFormat AjmMakeFormat(uint32_t channels, uint32_t sample_rate, + AjmSampleEncoding encoding) { + AjmSidebandFormat format {}; + format.channel_num = channels; + format.channel_mask = AjmChannelMask(channels); + format.sampling_frequency = sample_rate; + format.sample_encoding = static_cast(encoding); + format.bitrate = 0; + return format; +} + +class AjmDecoder { +public: + AjmDecoder(uint32_t channels, uint32_t sample_rate, AjmSampleEncoding encoding) + : m_channels(channels), m_sample_rate(sample_rate), m_sample_encoding(encoding) {} + virtual ~AjmDecoder() = default; + + KYTY_CLASS_NO_COPY(AjmDecoder); + + virtual AjmDecodeResult Initialize(const void* codec_parameters, size_t codec_parameters_size) { + (void)codec_parameters; + (void)codec_parameters_size; + return MakeResult(); + } + + virtual void Reset() { m_total_decoded_samples = 0; } + + virtual AjmDecodeResult Decode(const void* input, size_t input_size, void* output, + size_t output_size, bool multiple_frames, + AjmGaplessState* gapless) = 0; + + virtual void WriteCodecInfo(void* output, size_t output_size, + const AjmDecodeResult& result) const { + (void)output; + (void)output_size; + (void)result; + } + + [[nodiscard]] virtual size_t CodecInfoSize() const { return 0; } + + [[nodiscard]] virtual AjmSidebandFormat GetFormat() const { + return AjmMakeFormat(m_channels, m_sample_rate, m_sample_encoding); + } + + [[nodiscard]] AjmDecodeResult MakeResult() const { + AjmDecodeResult result {}; + result.total_decoded_samples = m_total_decoded_samples; + result.format = GetFormat(); + return result; + } + +protected: + void SetFormat(uint32_t channels, uint32_t sample_rate, AjmSampleEncoding encoding) { + m_channels = channels; + m_sample_rate = sample_rate; + m_sample_encoding = encoding; + } + + uint32_t m_channels = 2; + uint32_t m_sample_rate = 48000; + AjmSampleEncoding m_sample_encoding = AjmSampleEncoding::S16; + uint64_t m_total_decoded_samples = 0; +}; + +} // namespace Libs::Audio::Ajm diff --git a/src/libs/ajm/ffmpeg_decoder_common.h b/src/libs/ajm/ffmpeg_decoder_common.h index 3b1dffdde..61dc0fff1 100644 --- a/src/libs/ajm/ffmpeg_decoder_common.h +++ b/src/libs/ajm/ffmpeg_decoder_common.h @@ -1,6 +1,7 @@ #pragma once #include "common/assert.h" +#include "libs/ajm/decoder.h" #include @@ -14,7 +15,7 @@ extern "C" { namespace Libs::Audio::Ajm { -static AVSampleFormat AjmSampleEncodingToAvFormat(AjmSampleEncoding encoding) { +inline AVSampleFormat AjmSampleEncodingToAvFormat(AjmSampleEncoding encoding) { switch (encoding) { case AjmSampleEncoding::S16: return AV_SAMPLE_FMT_S16; case AjmSampleEncoding::S32: return AV_SAMPLE_FMT_S32; @@ -24,7 +25,7 @@ static AVSampleFormat AjmSampleEncodingToAvFormat(AjmSampleEncoding encoding) { return AV_SAMPLE_FMT_S16; } -static AVFrame* AjmConvertFfmpegFrame(const AVFrame* frame, AjmSampleEncoding encoding, +inline AVFrame* AjmConvertFfmpegFrame(const AVFrame* frame, AjmSampleEncoding encoding, AjmDecodeResult* result) { if (frame == nullptr || result == nullptr) { return nullptr; diff --git a/src/libs/ajm/mp3_decoder.h b/src/libs/ajm/mp3_decoder.h index b3de890c1..1ed3ca436 100644 --- a/src/libs/ajm/mp3_decoder.h +++ b/src/libs/ajm/mp3_decoder.h @@ -1,6 +1,7 @@ #pragma once #include "common/logging/log.h" +#include "libs/ajm/decoder.h" #include "libs/ajm/ffmpeg_decoder_common.h" #include