LibAudio: Extract loader stream creation from the plugins

This removes a lot of duplicated stream creation code from the plugins,
and also simplifies the way that the appropriate plugin is found. This
mirrors the ImageDecoderPlugin design and necessitates new sniffing
methods on the loaders.
This commit is contained in:
kleines Filmröllchen 2023-06-24 17:04:38 +02:00 committed by Sam Atkins
parent dfd48ab643
commit 5f1dbbaaa6
Notes: sideshowbarker 2024-07-17 04:49:48 +09:00
15 changed files with 124 additions and 131 deletions

View file

@ -4,14 +4,16 @@
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/MemoryStream.h>
#include <LibAudio/FlacLoader.h>
#include <stddef.h>
#include <stdint.h>
extern "C" int LLVMFuzzerTestOneInput(uint8_t const* data, size_t size)
{
auto flac_data = ByteBuffer::copy(data, size).release_value();
auto flac_or_error = Audio::FlacLoaderPlugin::create(flac_data.bytes());
auto const flac_bytes = ByteBuffer::copy(data, size).release_value();
auto flac_data = try_make<FixedMemoryStream>(flac_bytes).release_value();
auto flac_or_error = Audio::FlacLoaderPlugin::create(move(flac_data));
if (flac_or_error.is_error())
return 0;

View file

@ -10,8 +10,9 @@
extern "C" int LLVMFuzzerTestOneInput(uint8_t const* data, size_t size)
{
auto flac_data = ByteBuffer::copy(data, size).release_value();
auto mp3_or_error = Audio::MP3LoaderPlugin::create(flac_data.bytes());
auto const mp3_bytes = ByteBuffer::copy(data, size).release_value();
auto mp3_data = try_make<FixedMemoryStream>(mp3_bytes).release_value();
auto mp3_or_error = Audio::MP3LoaderPlugin::create(move(mp3_data));
if (mp3_or_error.is_error())
return 0;

View file

@ -4,14 +4,16 @@
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/MemoryStream.h>
#include <LibAudio/QOALoader.h>
#include <stddef.h>
#include <stdint.h>
extern "C" int LLVMFuzzerTestOneInput(uint8_t const* data, size_t size)
{
auto qoa_data = ByteBuffer::copy(data, size).release_value();
auto qoa_or_error = Audio::QOALoaderPlugin::create(qoa_data.bytes());
auto const qoa_bytes = ByteBuffer::copy(data, size).release_value();
auto qoa_data = try_make<FixedMemoryStream>(qoa_bytes).release_value();
auto qoa_or_error = Audio::QOALoaderPlugin::create(move(qoa_data));
if (qoa_or_error.is_error())
return 0;

View file

@ -4,17 +4,16 @@
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/Stream.h>
#include <AK/MemoryStream.h>
#include <LibAudio/WavLoader.h>
#include <stddef.h>
#include <stdint.h>
extern "C" int LLVMFuzzerTestOneInput(uint8_t const* data, size_t size)
{
if (!data)
return 0;
auto wav_data = ReadonlyBytes { data, size };
auto wav_or_error = Audio::WavLoaderPlugin::create(wav_data);
auto const wav_bytes = ByteBuffer::copy(data, size).release_value();
auto wav_data = try_make<FixedMemoryStream>(wav_bytes).release_value();
auto wav_or_error = Audio::WavLoaderPlugin::create(move(wav_data));
if (wav_or_error.is_error())
return 0;

View file

@ -19,7 +19,17 @@ struct DiscoverFLACTestsHack {
Test::add_test_case_to_suite(adopt_ref(*new ::Test::TestCase(
DeprecatedString::formatted("flac_spec_test_{}", path.basename()),
[path = move(path)]() {
auto result = Audio::FlacLoaderPlugin::create(path.string());
auto file = Core::File::open(path.string(), Core::File::OpenMode::Read);
if (file.is_error()) {
FAIL(DeprecatedString::formatted("{}", file.error()));
return;
}
auto buffered_file = Core::InputBufferedFile::create(file.release_value());
if (buffered_file.is_error()) {
FAIL(DeprecatedString::formatted("{}", buffered_file.error()));
return;
}
auto result = Audio::FlacLoaderPlugin::create(buffered_file.release_value());
if (result.is_error()) {
FAIL(DeprecatedString::formatted("{}", result.error()));
return;

View file

@ -12,6 +12,7 @@
#include <AK/IntegralMath.h>
#include <AK/Math.h>
#include <AK/MemoryStream.h>
#include <AK/NonnullOwnPtr.h>
#include <AK/ScopeGuard.h>
#include <AK/StdLibExtras.h>
#include <AK/Try.h>
@ -34,23 +35,10 @@ FlacLoaderPlugin::FlacLoaderPlugin(NonnullOwnPtr<SeekableStream> stream)
{
}
Result<NonnullOwnPtr<FlacLoaderPlugin>, LoaderError> FlacLoaderPlugin::create(StringView path)
ErrorOr<NonnullOwnPtr<LoaderPlugin>, LoaderError> FlacLoaderPlugin::create(NonnullOwnPtr<SeekableStream> stream)
{
auto stream = LOADER_TRY(Core::InputBufferedFile::create(LOADER_TRY(Core::File::open(path, Core::File::OpenMode::Read))));
auto loader = make<FlacLoaderPlugin>(move(stream));
LOADER_TRY(loader->initialize());
return loader;
}
Result<NonnullOwnPtr<FlacLoaderPlugin>, LoaderError> FlacLoaderPlugin::create(Bytes buffer)
{
auto stream = LOADER_TRY(try_make<FixedMemoryStream>(buffer));
auto loader = make<FlacLoaderPlugin>(move(stream));
LOADER_TRY(loader->initialize());
TRY(loader->initialize());
return loader;
}
@ -61,6 +49,13 @@ MaybeLoaderError FlacLoaderPlugin::initialize()
return {};
}
bool FlacLoaderPlugin::sniff(SeekableStream& stream)
{
BigEndianInputBitStream bit_input { MaybeOwned<Stream>(stream) };
auto maybe_flac = bit_input.read_bits<u32>(32);
return !maybe_flac.is_error() && maybe_flac.value() == 0x664C6143; // "flaC"
}
// 11.5 STREAM
MaybeLoaderError FlacLoaderPlugin::parse_header()
{

View file

@ -40,8 +40,8 @@ public:
explicit FlacLoaderPlugin(NonnullOwnPtr<SeekableStream> stream);
virtual ~FlacLoaderPlugin() override = default;
static Result<NonnullOwnPtr<FlacLoaderPlugin>, LoaderError> create(StringView path);
static Result<NonnullOwnPtr<FlacLoaderPlugin>, LoaderError> create(Bytes buffer);
static bool sniff(SeekableStream& stream);
static ErrorOr<NonnullOwnPtr<LoaderPlugin>, LoaderError> create(NonnullOwnPtr<SeekableStream>);
virtual ErrorOr<Vector<FixedArray<Sample>>, LoaderError> load_chunks(size_t samples_to_read_from_input) override;

View file

@ -10,6 +10,7 @@
#include <LibAudio/MP3Loader.h>
#include <LibAudio/QOALoader.h>
#include <LibAudio/WavLoader.h>
#include <LibCore/File.h>
namespace Audio {
@ -23,59 +24,43 @@ Loader::Loader(NonnullOwnPtr<LoaderPlugin> plugin)
{
}
Result<NonnullOwnPtr<LoaderPlugin>, LoaderError> Loader::create_plugin(StringView path)
struct LoaderPluginInitializer {
bool (*sniff)(SeekableStream&);
ErrorOr<NonnullOwnPtr<LoaderPlugin>, LoaderError> (*create)(NonnullOwnPtr<SeekableStream>);
};
#define ENUMERATE_LOADER_PLUGINS \
__ENUMERATE_LOADER_PLUGIN(Wav) \
__ENUMERATE_LOADER_PLUGIN(Flac) \
__ENUMERATE_LOADER_PLUGIN(QOA) \
__ENUMERATE_LOADER_PLUGIN(MP3)
static constexpr LoaderPluginInitializer s_initializers[] = {
#define __ENUMERATE_LOADER_PLUGIN(Type) \
{ Type##LoaderPlugin::sniff, Type##LoaderPlugin::create },
ENUMERATE_LOADER_PLUGINS
#undef __ENUMERATE_LOADER_PLUGIN
};
ErrorOr<NonnullRefPtr<Loader>, LoaderError> Loader::create(StringView path)
{
{
auto plugin = WavLoaderPlugin::create(path);
if (!plugin.is_error())
return NonnullOwnPtr<LoaderPlugin>(plugin.release_value());
}
{
auto plugin = FlacLoaderPlugin::create(path);
if (!plugin.is_error())
return NonnullOwnPtr<LoaderPlugin>(plugin.release_value());
}
{
auto plugin = MP3LoaderPlugin::create(path);
if (!plugin.is_error())
return NonnullOwnPtr<LoaderPlugin>(plugin.release_value());
}
{
auto plugin = QOALoaderPlugin::create(path);
if (!plugin.is_error())
return NonnullOwnPtr<LoaderPlugin>(plugin.release_value());
}
return LoaderError { "No loader plugin available" };
auto stream = LOADER_TRY(Core::InputBufferedFile::create(LOADER_TRY(Core::File::open(path, Core::File::OpenMode::Read))));
return adopt_ref(*new (nothrow) Loader(TRY(Loader::create_plugin(move(stream)))));
}
ErrorOr<NonnullRefPtr<Loader>, LoaderError> Loader::create(Bytes buffer)
{
auto stream = LOADER_TRY(try_make<FixedMemoryStream>(buffer));
return adopt_ref(*new (nothrow) Loader(TRY(Loader::create_plugin(move(stream)))));
}
Result<NonnullOwnPtr<LoaderPlugin>, LoaderError> Loader::create_plugin(Bytes buffer)
ErrorOr<NonnullOwnPtr<LoaderPlugin>, LoaderError> Loader::create_plugin(NonnullOwnPtr<SeekableStream> stream)
{
{
auto plugin = WavLoaderPlugin::create(buffer);
if (!plugin.is_error())
return NonnullOwnPtr<LoaderPlugin>(plugin.release_value());
}
{
auto plugin = FlacLoaderPlugin::create(buffer);
if (!plugin.is_error())
return NonnullOwnPtr<LoaderPlugin>(plugin.release_value());
}
{
auto plugin = MP3LoaderPlugin::create(buffer);
if (!plugin.is_error())
return NonnullOwnPtr<LoaderPlugin>(plugin.release_value());
}
{
auto plugin = QOALoaderPlugin::create(buffer);
if (!plugin.is_error())
return NonnullOwnPtr<LoaderPlugin>(plugin.release_value());
for (auto const& loader : s_initializers) {
if (loader.sniff(*stream)) {
TRY(stream->seek(0, SeekMode::SetPosition));
return loader.create(move(stream));
}
TRY(stream->seek(0, SeekMode::SetPosition));
}
return LoaderError { "No loader plugin available" };

View file

@ -6,12 +6,12 @@
#pragma once
#include <AK/Error.h>
#include <AK/FixedArray.h>
#include <AK/NonnullOwnPtr.h>
#include <AK/NonnullRefPtr.h>
#include <AK/RefCounted.h>
#include <AK/RefPtr.h>
#include <AK/Result.h>
#include <AK/Span.h>
#include <AK/Stream.h>
#include <AK/StringView.h>
@ -24,8 +24,6 @@
namespace Audio {
static constexpr StringView no_plugin_error = "No loader plugin available"sv;
// Experimentally determined to be a decent buffer size on i686:
// 4K (the default) is slightly worse, and 64K is much worse.
// At sufficiently large buffer sizes, the advantage of infrequent read() calls is outweighed by the memmove() overhead.
@ -87,8 +85,8 @@ protected:
class Loader : public RefCounted<Loader> {
public:
static Result<NonnullRefPtr<Loader>, LoaderError> create(StringView path) { return adopt_ref(*new Loader(TRY(create_plugin(path)))); }
static Result<NonnullRefPtr<Loader>, LoaderError> create(Bytes buffer) { return adopt_ref(*new Loader(TRY(create_plugin(buffer)))); }
static ErrorOr<NonnullRefPtr<Loader>, LoaderError> create(StringView path);
static ErrorOr<NonnullRefPtr<Loader>, LoaderError> create(Bytes buffer);
// Will only read less samples if we're at the end of the stream.
LoaderSamples get_more_samples(size_t samples_to_read_from_input = 128 * KiB);
@ -111,8 +109,7 @@ public:
Vector<PictureData> const& pictures() const { return m_plugin->pictures(); };
private:
static Result<NonnullOwnPtr<LoaderPlugin>, LoaderError> create_plugin(StringView path);
static Result<NonnullOwnPtr<LoaderPlugin>, LoaderError> create_plugin(Bytes buffer);
static ErrorOr<NonnullOwnPtr<LoaderPlugin>, LoaderError> create_plugin(NonnullOwnPtr<SeekableStream> stream);
explicit Loader(NonnullOwnPtr<LoaderPlugin>);

View file

@ -8,6 +8,7 @@
#include "MP3HuffmanTables.h"
#include "MP3Tables.h"
#include "MP3Types.h"
#include <AK/Endian.h>
#include <AK/FixedArray.h>
#include <LibCore/File.h>
@ -21,23 +22,34 @@ MP3LoaderPlugin::MP3LoaderPlugin(NonnullOwnPtr<SeekableStream> stream)
{
}
Result<NonnullOwnPtr<MP3LoaderPlugin>, LoaderError> MP3LoaderPlugin::create(StringView path)
bool MP3LoaderPlugin::sniff(SeekableStream& stream)
{
auto stream = LOADER_TRY(Core::InputBufferedFile::create(LOADER_TRY(Core::File::open(path, Core::File::OpenMode::Read))));
auto loader = make<MP3LoaderPlugin>(move(stream));
auto maybe_bit_stream = try_make<BigEndianInputBitStream>(MaybeOwned<Stream>(stream));
if (maybe_bit_stream.is_error())
return false;
auto bit_stream = maybe_bit_stream.release_value();
LOADER_TRY(loader->initialize());
auto synchronization_result = synchronize(*bit_stream, 0);
if (synchronization_result.is_error())
return false;
auto maybe_mp3 = stream.read_value<BigEndian<u16>>();
if (maybe_mp3.is_error())
return false;
return loader;
ErrorOr<int> id = bit_stream->read_bit();
if (id.is_error() || id.value() != 1)
return false;
auto raw_layer = bit_stream->read_bits(2);
if (raw_layer.is_error())
return false;
auto layer = MP3::Tables::LayerNumberLookup[raw_layer.value()];
return layer == 3;
}
Result<NonnullOwnPtr<MP3LoaderPlugin>, LoaderError> MP3LoaderPlugin::create(Bytes buffer)
ErrorOr<NonnullOwnPtr<LoaderPlugin>, LoaderError> MP3LoaderPlugin::create(NonnullOwnPtr<SeekableStream> stream)
{
auto stream = LOADER_TRY(try_make<FixedMemoryStream>(buffer));
auto loader = make<MP3LoaderPlugin>(move(stream));
LOADER_TRY(loader->initialize());
return loader;
}

View file

@ -24,8 +24,8 @@ public:
explicit MP3LoaderPlugin(NonnullOwnPtr<SeekableStream> stream);
virtual ~MP3LoaderPlugin() = default;
static Result<NonnullOwnPtr<MP3LoaderPlugin>, LoaderError> create(StringView path);
static Result<NonnullOwnPtr<MP3LoaderPlugin>, LoaderError> create(Bytes buffer);
static bool sniff(SeekableStream& stream);
static ErrorOr<NonnullOwnPtr<LoaderPlugin>, LoaderError> create(NonnullOwnPtr<SeekableStream>);
virtual ErrorOr<Vector<FixedArray<Sample>>, LoaderError> load_chunks(size_t samples_to_read_from_input) override;

View file

@ -24,22 +24,16 @@ QOALoaderPlugin::QOALoaderPlugin(NonnullOwnPtr<AK::SeekableStream> stream)
{
}
Result<NonnullOwnPtr<QOALoaderPlugin>, LoaderError> QOALoaderPlugin::create(StringView path)
bool QOALoaderPlugin::sniff(SeekableStream& stream)
{
auto stream = LOADER_TRY(Core::InputBufferedFile::create(LOADER_TRY(Core::File::open(path, Core::File::OpenMode::Read))));
auto loader = make<QOALoaderPlugin>(move(stream));
LOADER_TRY(loader->initialize());
return loader;
auto maybe_qoa = stream.read_value<BigEndian<u32>>();
return !maybe_qoa.is_error() && maybe_qoa.value() == QOA::magic;
}
Result<NonnullOwnPtr<QOALoaderPlugin>, LoaderError> QOALoaderPlugin::create(Bytes buffer)
ErrorOr<NonnullOwnPtr<LoaderPlugin>, LoaderError> QOALoaderPlugin::create(NonnullOwnPtr<SeekableStream> stream)
{
auto loader = make<QOALoaderPlugin>(make<FixedMemoryStream>(buffer));
auto loader = make<QOALoaderPlugin>(move(stream));
LOADER_TRY(loader->initialize());
return loader;
}

View file

@ -7,6 +7,7 @@
#pragma once
#include <AK/Error.h>
#include <AK/NonnullOwnPtr.h>
#include <AK/Span.h>
#include <AK/Types.h>
#include <LibAudio/Loader.h>
@ -24,8 +25,8 @@ public:
explicit QOALoaderPlugin(NonnullOwnPtr<AK::SeekableStream> stream);
virtual ~QOALoaderPlugin() override = default;
static Result<NonnullOwnPtr<QOALoaderPlugin>, LoaderError> create(StringView path);
static Result<NonnullOwnPtr<QOALoaderPlugin>, LoaderError> create(Bytes buffer);
static bool sniff(SeekableStream& stream);
static ErrorOr<NonnullOwnPtr<LoaderPlugin>, LoaderError> create(NonnullOwnPtr<SeekableStream>);
virtual ErrorOr<Vector<FixedArray<Sample>>, LoaderError> load_chunks(size_t samples_to_read_from_input) override;

View file

@ -12,9 +12,9 @@
#include <AK/Endian.h>
#include <AK/FixedArray.h>
#include <AK/MemoryStream.h>
#include <AK/NonnullOwnPtr.h>
#include <AK/NumericLimits.h>
#include <AK/Try.h>
#include <LibCore/File.h>
namespace Audio {
@ -23,33 +23,29 @@ WavLoaderPlugin::WavLoaderPlugin(NonnullOwnPtr<SeekableStream> stream)
{
}
Result<NonnullOwnPtr<WavLoaderPlugin>, LoaderError> WavLoaderPlugin::create(StringView path)
bool WavLoaderPlugin::sniff(SeekableStream& stream)
{
auto stream = LOADER_TRY(Core::InputBufferedFile::create(LOADER_TRY(Core::File::open(path, Core::File::OpenMode::Read))));
auto loader = make<WavLoaderPlugin>(move(stream));
auto riff = stream.read_value<RIFF::ChunkID>();
if (riff.is_error())
return false;
if (riff.value() != RIFF::riff_magic)
return false;
LOADER_TRY(loader->initialize());
auto size = stream.read_value<LittleEndian<u32>>();
if (size.is_error())
return false;
return loader;
auto wave = stream.read_value<RIFF::ChunkID>();
return !wave.is_error() && wave.value() == RIFF::wave_subformat_id;
}
Result<NonnullOwnPtr<WavLoaderPlugin>, LoaderError> WavLoaderPlugin::create(Bytes buffer)
ErrorOr<NonnullOwnPtr<LoaderPlugin>, LoaderError> WavLoaderPlugin::create(NonnullOwnPtr<SeekableStream> stream)
{
auto stream = LOADER_TRY(try_make<FixedMemoryStream>(buffer));
auto loader = make<WavLoaderPlugin>(move(stream));
LOADER_TRY(loader->initialize());
LOADER_TRY(loader->parse_header());
return loader;
}
MaybeLoaderError WavLoaderPlugin::initialize()
{
LOADER_TRY(parse_header());
return {};
}
template<typename SampleReader>
MaybeLoaderError WavLoaderPlugin::read_samples_from_stream(Stream& stream, SampleReader read_sample, FixedArray<Sample>& samples) const
{

View file

@ -25,8 +25,9 @@ namespace Audio {
class WavLoaderPlugin : public LoaderPlugin {
public:
explicit WavLoaderPlugin(NonnullOwnPtr<SeekableStream> stream);
static Result<NonnullOwnPtr<WavLoaderPlugin>, LoaderError> create(StringView path);
static Result<NonnullOwnPtr<WavLoaderPlugin>, LoaderError> create(Bytes buffer);
static bool sniff(SeekableStream& stream);
static ErrorOr<NonnullOwnPtr<LoaderPlugin>, LoaderError> create(NonnullOwnPtr<SeekableStream>);
virtual ErrorOr<Vector<FixedArray<Sample>>, LoaderError> load_chunks(size_t samples_to_read_from_input) override;
@ -44,8 +45,6 @@ public:
virtual PcmSampleFormat pcm_format() override { return m_sample_format; }
private:
MaybeLoaderError initialize();
MaybeLoaderError parse_header();
MaybeLoaderError load_wav_info_block(Vector<RIFF::Chunk> info_chunks);