From 849c4012c0d5fcea55373cd28aee3dab7db28287 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Thu, 26 Aug 2021 14:20:36 +0200 Subject: [PATCH] filter/Filter: add method ReadMore() This allows FilterPCM() to return more data, which some implementations may need to do (e.g. FFmpeg). --- src/filter/Filter.hxx | 21 +++++++++++++-- src/filter/Observer.cxx | 4 +++ src/filter/plugins/FfmpegFilter.cxx | 6 +++++ src/filter/plugins/FfmpegFilter.hxx | 1 + src/filter/plugins/TwoFilters.cxx | 42 ++++++++++++++++++++++++++--- src/filter/plugins/TwoFilters.hxx | 1 + src/output/Source.cxx | 14 ++++++++-- test/run_filter.cxx | 5 ++-- 8 files changed, 84 insertions(+), 10 deletions(-) diff --git a/src/filter/Filter.hxx b/src/filter/Filter.hxx index ed82a2a19..4087aeb94 100644 --- a/src/filter/Filter.hxx +++ b/src/filter/Filter.hxx @@ -42,11 +42,28 @@ public: * * @param src the input buffer * @return the output buffer (will be invalidated by deleting - * this object or any call to Reset(), FilterPCM() or - * Flush()); may be empty if no output is currently available + * this object or any call to Reset(), FilterPCM(), ReadMore() + * or Flush()); may be empty if no output is currently + * available */ virtual std::span FilterPCM(std::span src) = 0; + /** + * Read more result data from the filter. After each + * FilterPCM() call, this should be called repeatedly until it + * returns an empty span. + * + * Throws on error. + * + * @return the output buffer (will be invalidated by deleting + * this object or any call to Reset(), FilterPCM(), ReadMore() + * or Flush()); may be empty if no output is currently + * available + */ + virtual std::span ReadMore() { + return {}; + } + /** * Flush pending data and return it. This should be called * repeatedly until it returns an empty span. diff --git a/src/filter/Observer.cxx b/src/filter/Observer.cxx index bbf5ba001..213be7774 100644 --- a/src/filter/Observer.cxx +++ b/src/filter/Observer.cxx @@ -68,6 +68,10 @@ public: return filter->FilterPCM(src); } + std::span ReadMore() override { + return filter->ReadMore(); + } + std::span Flush() override { return filter->Flush(); } diff --git a/src/filter/plugins/FfmpegFilter.cxx b/src/filter/plugins/FfmpegFilter.cxx index 55d61c145..29bf7ada5 100644 --- a/src/filter/plugins/FfmpegFilter.cxx +++ b/src/filter/plugins/FfmpegFilter.cxx @@ -84,6 +84,12 @@ FfmpegFilter::FilterPCM(std::span src) return ReadOutput(); } +std::span +FfmpegFilter::ReadMore() +{ + return ReadOutput(); +} + std::span FfmpegFilter::Flush() { diff --git a/src/filter/plugins/FfmpegFilter.hxx b/src/filter/plugins/FfmpegFilter.hxx index 0ab521e06..a22b560c2 100644 --- a/src/filter/plugins/FfmpegFilter.hxx +++ b/src/filter/plugins/FfmpegFilter.hxx @@ -54,6 +54,7 @@ public: /* virtual methods from class Filter */ std::span FilterPCM(std::span src) override; + std::span ReadMore() override; std::span Flush() override; private: diff --git a/src/filter/plugins/TwoFilters.cxx b/src/filter/plugins/TwoFilters.cxx index a7597c527..d10bb6900 100644 --- a/src/filter/plugins/TwoFilters.cxx +++ b/src/filter/plugins/TwoFilters.cxx @@ -21,13 +21,47 @@ TwoFilters::FilterPCM(std::span src) } std::span -TwoFilters::Flush() +TwoFilters::ReadMore() { - if (auto result = first->Flush(); !result.empty()) - /* Flush() output from the first Filter must be - filtered by the second Filter */ + assert(first); + assert(second); + + /* first read all remaining data from the second filter */ + if (auto result = second->ReadMore(); !result.empty()) + return result; + + /* now read more data from the first filter and process it + with the second filter */ + if (auto result = first->ReadMore(); !result.empty()) + /* output from the first Filter must be filtered by + the second Filter */ return second->FilterPCM(result); + /* both filters have been queried and there's no more data */ + return {}; +} + +std::span +TwoFilters::Flush() +{ + assert(second); + + /* first read all remaining data from the second filter */ + if (auto result = second->ReadMore(); !result.empty()) + return result; + + /* now flush the first filter and process it with the second + filter */ + if (first) { + if (auto result = first->Flush(); !result.empty()) + /* output from the first Filter must be + filtered by the second Filter */ + return second->FilterPCM(result); + + first.reset(); + } + + /* finally flush the second filter */ return second->Flush(); } diff --git a/src/filter/plugins/TwoFilters.hxx b/src/filter/plugins/TwoFilters.hxx index 423c0befb..0ee9a222c 100644 --- a/src/filter/plugins/TwoFilters.hxx +++ b/src/filter/plugins/TwoFilters.hxx @@ -29,6 +29,7 @@ public: } std::span FilterPCM(std::span src) override; + std::span ReadMore() override; std::span Flush() override; }; diff --git a/src/output/Source.cxx b/src/output/Source.cxx index 8ab97a7ef..acf50e893 100644 --- a/src/output/Source.cxx +++ b/src/output/Source.cxx @@ -138,6 +138,8 @@ AudioOutputSource::GetChunkData(const MusicChunk &chunk, *replay_gain_serial_p = chunk.replay_gain_serial; } + /* note: the ReplayGainFilter doesn't have a + ReadMore() method */ data = current_replay_gain_filter->FilterPCM(data); } @@ -231,10 +233,18 @@ AudioOutputSource::Fill(Mutex &mutex) void AudioOutputSource::ConsumeData(size_t nbytes) noexcept { + assert(filter); + pending_data = pending_data.subspan(nbytes); - if (pending_data.empty()) - DropCurrentChunk(); + if (pending_data.empty()) { + /* give the filter a chance to return more data in + another buffer */ + pending_data = filter->ReadMore(); + + if (pending_data.empty()) + DropCurrentChunk(); + } } std::span diff --git a/test/run_filter.cxx b/test/run_filter.cxx index 2c318b7c4..7eb294e72 100644 --- a/test/run_filter.cxx +++ b/test/run_filter.cxx @@ -122,8 +122,9 @@ try { if (nbytes == 0) break; - auto dest = filter->FilterPCM(std::span{buffer}.first(nbytes)); - output_fd.FullWrite(dest); + for (auto dest = filter->FilterPCM(std::span{buffer}.first(nbytes)); + !dest.empty(); dest = filter->ReadMore()) + output_fd.FullWrite(dest); } while (true) {