disable breaking the loop in multi-frame if storage is insufficient

This commit is contained in:
Vladislav Mikhalin 2024-11-12 20:16:17 +03:00
parent 5f63f46204
commit 4d4d9d5e2c
7 changed files with 44 additions and 27 deletions

View File

@ -123,14 +123,16 @@ AjmSidebandFormat AjmAt9Decoder::GetFormat() const {
.channel_mask = GetChannelMask(u32(m_codec_info.channels)), .channel_mask = GetChannelMask(u32(m_codec_info.channels)),
.sampl_freq = u32(m_codec_info.samplingRate), .sampl_freq = u32(m_codec_info.samplingRate),
.sample_encoding = m_format, .sample_encoding = m_format,
.bitrate = u32(m_codec_info.samplingRate * GetPCMSize(m_format) * 8), .bitrate = u32((m_codec_info.samplingRate * m_codec_info.superframeSize * 8) /
(m_codec_info.framesInSuperframe * m_codec_info.frameSamples)),
.reserved = 0, .reserved = 0,
}; };
} }
u32 AjmAt9Decoder::GetNextFrameSize(u32 max_samples) const { u32 AjmAt9Decoder::GetNextFrameSize(u32 skip_samples, u32 max_samples) const {
return std::min(u32(m_codec_info.frameSamples), max_samples) * m_codec_info.channels * skip_samples = std::min({skip_samples, u32(m_codec_info.frameSamples), max_samples});
GetPCMSize(m_format); return (std::min(u32(m_codec_info.frameSamples), max_samples) - skip_samples) *
m_codec_info.channels * GetPCMSize(m_format);
} }
} // namespace Libraries::Ajm } // namespace Libraries::Ajm

View File

@ -35,7 +35,7 @@ struct AjmAt9Decoder final : AjmCodec {
void Initialize(const void* buffer, u32 buffer_size) override; void Initialize(const void* buffer, u32 buffer_size) override;
void GetInfo(void* out_info) const override; void GetInfo(void* out_info) const override;
AjmSidebandFormat GetFormat() const override; AjmSidebandFormat GetFormat() const override;
u32 GetNextFrameSize(u32 max_samples) const override; u32 GetNextFrameSize(u32 skip_samples, u32 max_samples) const override;
std::tuple<u32, u32> ProcessData(std::span<u8>& input, SparseOutputBuffer& output, std::tuple<u32, u32> ProcessData(std::span<u8>& input, SparseOutputBuffer& output,
AjmSidebandGaplessDecode& gapless, AjmSidebandGaplessDecode& gapless,
std::optional<u32> max_samples) override; std::optional<u32> max_samples) override;

View File

@ -135,7 +135,10 @@ AjmJob AjmJobFromBatchBuffer(u32 instance_id, AjmBatchBuffer batch_buffer) {
case Identifier::AjmIdentInputControlBuf: { case Identifier::AjmIdentInputControlBuf: {
ASSERT_MSG(!input_control_buffer.has_value(), ASSERT_MSG(!input_control_buffer.has_value(),
"Only one instance of input control buffer is allowed per job"); "Only one instance of input control buffer is allowed per job");
input_control_buffer = batch_buffer.Consume<AjmChunkBuffer>(); const auto& buffer = batch_buffer.Consume<AjmChunkBuffer>();
if (buffer.p_address != nullptr && buffer.size != 0) {
input_control_buffer = buffer;
}
break; break;
} }
case Identifier::AjmIdentControlFlags: case Identifier::AjmIdentControlFlags:
@ -155,19 +158,27 @@ AjmJob AjmJobFromBatchBuffer(u32 instance_id, AjmBatchBuffer batch_buffer) {
case Identifier::AjmIdentInlineBuf: { case Identifier::AjmIdentInlineBuf: {
ASSERT_MSG(!output_control_buffer.has_value(), ASSERT_MSG(!output_control_buffer.has_value(),
"Only one instance of inline buffer is allowed per job"); "Only one instance of inline buffer is allowed per job");
inline_buffer = batch_buffer.Consume<AjmChunkBuffer>(); const auto& buffer = batch_buffer.Consume<AjmChunkBuffer>();
if (buffer.p_address != nullptr && buffer.size != 0) {
inline_buffer = buffer;
}
break; break;
} }
case Identifier::AjmIdentOutputRunBuf: { case Identifier::AjmIdentOutputRunBuf: {
auto& buffer = batch_buffer.Consume<AjmChunkBuffer>(); auto& buffer = batch_buffer.Consume<AjmChunkBuffer>();
u8* p_begin = reinterpret_cast<u8*>(buffer.p_address); u8* p_begin = reinterpret_cast<u8*>(buffer.p_address);
job.output.buffers.emplace_back(std::span<u8>(p_begin, p_begin + buffer.size)); if (p_begin != nullptr && buffer.size != 0) {
job.output.buffers.emplace_back(std::span<u8>(p_begin, p_begin + buffer.size));
}
break; break;
} }
case Identifier::AjmIdentOutputControlBuf: { case Identifier::AjmIdentOutputControlBuf: {
ASSERT_MSG(!output_control_buffer.has_value(), ASSERT_MSG(!output_control_buffer.has_value(),
"Only one instance of output control buffer is allowed per job"); "Only one instance of output control buffer is allowed per job");
output_control_buffer = batch_buffer.Consume<AjmChunkBuffer>(); const auto& buffer = batch_buffer.Consume<AjmChunkBuffer>();
if (buffer.p_address != nullptr && buffer.size != 0) {
output_control_buffer = buffer;
}
break; break;
} }
default: default:

View File

@ -78,8 +78,12 @@ void AjmInstance::ExecuteJob(AjmJob& job) {
} }
if (job.input.gapless_decode.has_value()) { if (job.input.gapless_decode.has_value()) {
auto& params = job.input.gapless_decode.value(); auto& params = job.input.gapless_decode.value();
m_gapless.total_samples = params.total_samples; if (params.total_samples != 0) {
m_gapless.skip_samples = params.skip_samples; m_gapless.total_samples = std::max(params.total_samples, m_gapless.total_samples);
}
if (params.skip_samples != 0) {
m_gapless.skip_samples = std::max(params.skip_samples, m_gapless.skip_samples);
}
} }
if (!job.input.buffer.empty() && !job.output.buffers.empty()) { if (!job.input.buffer.empty() && !job.output.buffers.empty()) {
@ -90,16 +94,14 @@ void AjmInstance::ExecuteJob(AjmJob& job) {
auto in_size = in_buf.size(); auto in_size = in_buf.size();
auto out_size = out_buf.Size(); auto out_size = out_buf.Size();
while (!in_buf.empty() && !out_buf.IsEmpty() && !IsGaplessEnd()) { while (!in_buf.empty() && !out_buf.IsEmpty() && !IsGaplessEnd()) {
const auto samples_remain = GetNumRemainingSamples(); if (!HasEnoughSpace(out_buf)) {
if (!HasEnoughSpace(out_buf, samples_remain)) { if (job.output.p_mframe == nullptr || frames_decoded == 0) {
if (job.output.p_mframe == nullptr) {
LOG_ERROR(Lib_Ajm, "Single-frame job buffer too small.");
job.output.p_result->result = ORBIS_AJM_RESULT_NOT_ENOUGH_ROOM; job.output.p_result->result = ORBIS_AJM_RESULT_NOT_ENOUGH_ROOM;
break;
} }
break;
} }
const auto [nframes, nsamples] = const auto [nframes, nsamples] =
m_codec->ProcessData(in_buf, out_buf, m_gapless, samples_remain); m_codec->ProcessData(in_buf, out_buf, m_gapless, GetNumRemainingSamples());
frames_decoded += nframes; frames_decoded += nframes;
m_total_samples += nsamples; m_total_samples += nsamples;
m_gapless_samples += nsamples; m_gapless_samples += nsamples;
@ -138,10 +140,11 @@ bool AjmInstance::IsGaplessEnd() const {
return m_gapless.total_samples != 0 && m_gapless_samples >= m_gapless.total_samples; return m_gapless.total_samples != 0 && m_gapless_samples >= m_gapless.total_samples;
} }
bool AjmInstance::HasEnoughSpace(const SparseOutputBuffer& output, bool AjmInstance::HasEnoughSpace(const SparseOutputBuffer& output) const {
std::optional<u32> opt_samples_remain) const { const auto skip =
const auto remain = opt_samples_remain.value_or(std::numeric_limits<u32>::max()); m_gapless.skip_samples - std::min(m_gapless.skip_samples, m_gapless.skipped_samples);
return output.Size() >= m_codec->GetNextFrameSize(remain); const auto remain = GetNumRemainingSamples().value_or(std::numeric_limits<u32>::max());
return output.Size() >= m_codec->GetNextFrameSize(skip, remain);
} }
std::optional<u32> AjmInstance::GetNumRemainingSamples() const { std::optional<u32> AjmInstance::GetNumRemainingSamples() const {

View File

@ -66,7 +66,7 @@ public:
virtual void Reset() = 0; virtual void Reset() = 0;
virtual void GetInfo(void* out_info) const = 0; virtual void GetInfo(void* out_info) const = 0;
virtual AjmSidebandFormat GetFormat() const = 0; virtual AjmSidebandFormat GetFormat() const = 0;
virtual u32 GetNextFrameSize(u32 max_samples) const = 0; virtual u32 GetNextFrameSize(u32 skip_samples, u32 max_samples) const = 0;
virtual std::tuple<u32, u32> ProcessData(std::span<u8>& input, SparseOutputBuffer& output, virtual std::tuple<u32, u32> ProcessData(std::span<u8>& input, SparseOutputBuffer& output,
AjmSidebandGaplessDecode& gapless, AjmSidebandGaplessDecode& gapless,
std::optional<u32> max_samples_per_channel) = 0; std::optional<u32> max_samples_per_channel) = 0;
@ -80,7 +80,7 @@ public:
private: private:
bool IsGaplessEnd() const; bool IsGaplessEnd() const;
bool HasEnoughSpace(const SparseOutputBuffer& output, std::optional<u32> samples_remain) const; bool HasEnoughSpace(const SparseOutputBuffer& output) const;
std::optional<u32> GetNumRemainingSamples() const; std::optional<u32> GetNumRemainingSamples() const;
AjmInstanceFlags m_flags{}; AjmInstanceFlags m_flags{};

View File

@ -219,9 +219,10 @@ std::tuple<u32, u32> AjmMp3Decoder::ProcessData(std::span<u8>& in_buf, SparseOut
return {frames_decoded, samples_decoded / m_codec_context->ch_layout.nb_channels}; return {frames_decoded, samples_decoded / m_codec_context->ch_layout.nb_channels};
} }
u32 AjmMp3Decoder::GetNextFrameSize(u32 max_samples) const { u32 AjmMp3Decoder::GetNextFrameSize(u32 skip_samples, u32 max_samples) const {
return std::min(m_frame_samples, max_samples) * m_codec_context->ch_layout.nb_channels * skip_samples = std::min({skip_samples, m_frame_samples, max_samples});
GetPCMSize(m_format); return (std::min(m_frame_samples, max_samples) - skip_samples) *
m_codec_context->ch_layout.nb_channels * GetPCMSize(m_format);
} }
class BitReader { class BitReader {

View File

@ -70,7 +70,7 @@ public:
void Initialize(const void* buffer, u32 buffer_size) override {} void Initialize(const void* buffer, u32 buffer_size) override {}
void GetInfo(void* out_info) const override; void GetInfo(void* out_info) const override;
AjmSidebandFormat GetFormat() const override; AjmSidebandFormat GetFormat() const override;
u32 GetNextFrameSize(u32 max_samples) const override; u32 GetNextFrameSize(u32 skip_samples, u32 max_samples) const override;
std::tuple<u32, u32> ProcessData(std::span<u8>& input, SparseOutputBuffer& output, std::tuple<u32, u32> ProcessData(std::span<u8>& input, SparseOutputBuffer& output,
AjmSidebandGaplessDecode& gapless, AjmSidebandGaplessDecode& gapless,
std::optional<u32> max_samples_per_channel) override; std::optional<u32> max_samples_per_channel) override;