mirror of
https://github.com/RGBCube/serenity
synced 2025-05-14 09:24:57 +00:00
LibGfx: Add initial ISO BMFF parsing and a utility to print file info
Currently, the `isobmff` utility will only print the media file type info from the FileTypeBox (major brand and compatible brands), as well as the names and sizes of top-level boxes.
This commit is contained in:
parent
9caa0bda7d
commit
66c9696687
13 changed files with 510 additions and 0 deletions
|
@ -534,6 +534,9 @@ if (BUILD_LAGOM)
|
||||||
add_executable(image ../../Userland/Utilities/image.cpp)
|
add_executable(image ../../Userland/Utilities/image.cpp)
|
||||||
target_link_libraries(image LibCore LibGfx LibMain)
|
target_link_libraries(image LibCore LibGfx LibMain)
|
||||||
|
|
||||||
|
add_executable(isobmff ../../Userland/Utilities/isobmff.cpp)
|
||||||
|
target_link_libraries(isobmff LibCore LibGfx LibMain)
|
||||||
|
|
||||||
add_executable(ttfdisasm ../../Userland/Utilities/ttfdisasm.cpp)
|
add_executable(ttfdisasm ../../Userland/Utilities/ttfdisasm.cpp)
|
||||||
target_link_libraries(ttfdisasm LibGfx LibMain)
|
target_link_libraries(ttfdisasm LibGfx LibMain)
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ set(TEST_SOURCES
|
||||||
TestGfxBitmap.cpp
|
TestGfxBitmap.cpp
|
||||||
TestICCProfile.cpp
|
TestICCProfile.cpp
|
||||||
TestImageDecoder.cpp
|
TestImageDecoder.cpp
|
||||||
|
TestParseISOBMFF.cpp
|
||||||
TestRect.cpp
|
TestRect.cpp
|
||||||
TestScalingFunctions.cpp
|
TestScalingFunctions.cpp
|
||||||
)
|
)
|
||||||
|
|
37
Tests/LibGfx/TestParseISOBMFF.cpp
Normal file
37
Tests/LibGfx/TestParseISOBMFF.cpp
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2023, Gregory Bertilson <zaggy1024@gmail.com>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <LibTest/TestCase.h>
|
||||||
|
|
||||||
|
#include <AK/MemoryStream.h>
|
||||||
|
#include <LibCore/MappedFile.h>
|
||||||
|
#include <LibGfx/ImageFormats/ISOBMFF/Reader.h>
|
||||||
|
|
||||||
|
TEST_CASE(parse_animated_avif)
|
||||||
|
{
|
||||||
|
auto file = MUST(Core::MappedFile::map("./test-inputs/loop_forever.avif"sv));
|
||||||
|
auto reader = MUST(Gfx::ISOBMFF::Reader::create(MUST(try_make<FixedMemoryStream>(file->bytes()))));
|
||||||
|
auto boxes = MUST(reader.read_entire_file());
|
||||||
|
|
||||||
|
for (auto& box : boxes)
|
||||||
|
box->dump();
|
||||||
|
|
||||||
|
VERIFY(boxes.size() == 4);
|
||||||
|
VERIFY(boxes[0]->box_type() == Gfx::ISOBMFF::BoxType::FileTypeBox);
|
||||||
|
auto& file_type_box = static_cast<Gfx::ISOBMFF::FileTypeBox&>(*boxes[0]);
|
||||||
|
VERIFY(file_type_box.major_brand == Gfx::ISOBMFF::BrandIdentifier::avis);
|
||||||
|
VERIFY(file_type_box.minor_version == 0);
|
||||||
|
Vector<Gfx::ISOBMFF::BrandIdentifier, 7> expected_compatible_brands = {
|
||||||
|
Gfx::ISOBMFF::BrandIdentifier::avif,
|
||||||
|
Gfx::ISOBMFF::BrandIdentifier::avis,
|
||||||
|
Gfx::ISOBMFF::BrandIdentifier::msf1,
|
||||||
|
Gfx::ISOBMFF::BrandIdentifier::iso8,
|
||||||
|
Gfx::ISOBMFF::BrandIdentifier::mif1,
|
||||||
|
Gfx::ISOBMFF::BrandIdentifier::miaf,
|
||||||
|
Gfx::ISOBMFF::BrandIdentifier::MA1A,
|
||||||
|
};
|
||||||
|
VERIFY(file_type_box.compatible_brands == expected_compatible_brands);
|
||||||
|
}
|
BIN
Tests/LibGfx/test-inputs/loop_forever.avif
Normal file
BIN
Tests/LibGfx/test-inputs/loop_forever.avif
Normal file
Binary file not shown.
|
@ -37,6 +37,8 @@ set(SOURCES
|
||||||
ImageFormats/GIFLoader.cpp
|
ImageFormats/GIFLoader.cpp
|
||||||
ImageFormats/ICOLoader.cpp
|
ImageFormats/ICOLoader.cpp
|
||||||
ImageFormats/ImageDecoder.cpp
|
ImageFormats/ImageDecoder.cpp
|
||||||
|
ImageFormats/ISOBMFF/Boxes.cpp
|
||||||
|
ImageFormats/ISOBMFF/Reader.cpp
|
||||||
ImageFormats/JPEGLoader.cpp
|
ImageFormats/JPEGLoader.cpp
|
||||||
ImageFormats/JPEGXLLoader.cpp
|
ImageFormats/JPEGXLLoader.cpp
|
||||||
ImageFormats/JPEGWriter.cpp
|
ImageFormats/JPEGWriter.cpp
|
||||||
|
|
48
Userland/Libraries/LibGfx/ImageFormats/ISOBMFF/BoxStream.h
Normal file
48
Userland/Libraries/LibGfx/ImageFormats/ISOBMFF/BoxStream.h
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2023, Gregory Bertilson <Zaggy1024@gmail.com>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <AK/Stream.h>
|
||||||
|
|
||||||
|
namespace Gfx::ISOBMFF {
|
||||||
|
|
||||||
|
class BoxStream final : public Stream {
|
||||||
|
public:
|
||||||
|
explicit BoxStream(Stream& stream, size_t size)
|
||||||
|
: m_stream(stream)
|
||||||
|
, m_data_left(size)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool is_eof() const override { return m_stream.is_eof() || remaining() == 0; }
|
||||||
|
virtual bool is_open() const override { return m_stream.is_open(); }
|
||||||
|
virtual void close() override { m_stream.close(); }
|
||||||
|
virtual ErrorOr<Bytes> read_some(Bytes bytes) override
|
||||||
|
{
|
||||||
|
auto read_bytes = TRY(m_stream.read_some(bytes));
|
||||||
|
m_data_left -= min(read_bytes.size(), m_data_left);
|
||||||
|
return read_bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ErrorOr<size_t> write_some(ReadonlyBytes) override { VERIFY_NOT_REACHED(); }
|
||||||
|
virtual ErrorOr<void> write_until_depleted(ReadonlyBytes) override { VERIFY_NOT_REACHED(); }
|
||||||
|
|
||||||
|
size_t remaining() const
|
||||||
|
{
|
||||||
|
return m_data_left;
|
||||||
|
}
|
||||||
|
ErrorOr<void> discard_remaining()
|
||||||
|
{
|
||||||
|
return discard(remaining());
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Stream& m_stream;
|
||||||
|
size_t m_data_left;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
102
Userland/Libraries/LibGfx/ImageFormats/ISOBMFF/Boxes.cpp
Normal file
102
Userland/Libraries/LibGfx/ImageFormats/ISOBMFF/Boxes.cpp
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2023, Gregory Bertilson <Zaggy1024@gmail.com>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Boxes.h"
|
||||||
|
|
||||||
|
namespace Gfx::ISOBMFF {
|
||||||
|
|
||||||
|
ErrorOr<BoxHeader> read_box_header(Stream& stream)
|
||||||
|
{
|
||||||
|
BoxHeader header;
|
||||||
|
u64 total_size = TRY(stream.read_value<BigEndian<u32>>());
|
||||||
|
header.type = TRY(stream.read_value<BoxType>());
|
||||||
|
|
||||||
|
u64 data_size_read = sizeof(u32) + sizeof(BoxType);
|
||||||
|
|
||||||
|
if (total_size == 1) {
|
||||||
|
total_size = TRY(stream.read_value<BigEndian<u64>>());
|
||||||
|
data_size_read += sizeof(u64);
|
||||||
|
}
|
||||||
|
|
||||||
|
header.contents_size = total_size - data_size_read;
|
||||||
|
return header;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Box::dump(String const& prepend) const
|
||||||
|
{
|
||||||
|
outln("{}{}", prepend, box_type());
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorOr<void> FullBox::read_from_stream(BoxStream& stream)
|
||||||
|
{
|
||||||
|
u32 data = TRY(stream.read_value<BigEndian<u32>>());
|
||||||
|
// unsigned int(8) version
|
||||||
|
version = static_cast<u8>(data >> 24);
|
||||||
|
// unsigned int(24) flags
|
||||||
|
flags = data & 0xFFF;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
void FullBox::dump(String const& prepend) const
|
||||||
|
{
|
||||||
|
outln("{}{} (version = {}, flags = 0x{:x})", prepend, box_type(), version, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
static String add_indent(String const& string)
|
||||||
|
{
|
||||||
|
return MUST(String::formatted("{} ", string));
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorOr<void> UnknownBox::read_from_stream(BoxStream& stream)
|
||||||
|
{
|
||||||
|
m_contents_size = stream.remaining();
|
||||||
|
TRY(stream.discard_remaining());
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
void UnknownBox::dump(String const& prepend) const
|
||||||
|
{
|
||||||
|
Box::dump(prepend);
|
||||||
|
|
||||||
|
auto indented_prepend = add_indent(prepend);
|
||||||
|
outln("{}[ {} bytes ]", prepend, m_contents_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorOr<void> FileTypeBox::read_from_stream(BoxStream& stream)
|
||||||
|
{
|
||||||
|
// unsigned int(32) major_brand;
|
||||||
|
major_brand = TRY(stream.read_value<BrandIdentifier>());
|
||||||
|
// unsigned int(32) minor_version;
|
||||||
|
minor_version = TRY(stream.read_value<BigEndian<u32>>());
|
||||||
|
|
||||||
|
// unsigned int(32) compatible_brands[]; // to end of the box
|
||||||
|
if (stream.remaining() % sizeof(BrandIdentifier) != 0)
|
||||||
|
return Error::from_string_literal("FileTypeBox compatible_brands contains a partial brand");
|
||||||
|
|
||||||
|
for (auto minor_brand_count = stream.remaining() / sizeof(BrandIdentifier); minor_brand_count > 0; minor_brand_count--)
|
||||||
|
TRY(compatible_brands.try_append(TRY(stream.read_value<BrandIdentifier>())));
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileTypeBox::dump(String const& prepend) const
|
||||||
|
{
|
||||||
|
FullBox::dump(prepend);
|
||||||
|
|
||||||
|
auto indented_prepend = add_indent(prepend);
|
||||||
|
|
||||||
|
outln("{}- major_brand = {}", prepend, major_brand);
|
||||||
|
outln("{}- minor_version = {}", prepend, minor_version);
|
||||||
|
|
||||||
|
StringBuilder compatible_brands_string;
|
||||||
|
compatible_brands_string.append("- compatible_brands = { "sv);
|
||||||
|
for (size_t i = 0; i < compatible_brands.size() - 1; i++)
|
||||||
|
compatible_brands_string.appendff("{}, ", compatible_brands[i]);
|
||||||
|
compatible_brands_string.appendff("{} }}", compatible_brands[compatible_brands.size() - 1]);
|
||||||
|
outln("{}{}", prepend, compatible_brands_string.string_view());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
96
Userland/Libraries/LibGfx/ImageFormats/ISOBMFF/Boxes.h
Normal file
96
Userland/Libraries/LibGfx/ImageFormats/ISOBMFF/Boxes.h
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2023, Gregory Bertilson <Zaggy1024@gmail.com>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <AK/Endian.h>
|
||||||
|
#include <AK/NonnullOwnPtr.h>
|
||||||
|
#include <AK/String.h>
|
||||||
|
#include <AK/Traits.h>
|
||||||
|
#include <AK/Vector.h>
|
||||||
|
#include <LibVideo/DecoderError.h>
|
||||||
|
|
||||||
|
#include "BoxStream.h"
|
||||||
|
#include "Enums.h"
|
||||||
|
|
||||||
|
namespace Gfx::ISOBMFF {
|
||||||
|
|
||||||
|
// ISO/IEC 14496-12 Fifth Edition
|
||||||
|
|
||||||
|
// 4.2 Object Structure
|
||||||
|
struct BoxHeader {
|
||||||
|
BoxType type { BoxType::None };
|
||||||
|
u64 contents_size { 0 };
|
||||||
|
};
|
||||||
|
|
||||||
|
ErrorOr<BoxHeader> read_box_header(Stream& stream);
|
||||||
|
|
||||||
|
struct Box {
|
||||||
|
Box() = default;
|
||||||
|
virtual ~Box() = default;
|
||||||
|
virtual ErrorOr<void> read_from_stream(BoxStream&) { return {}; }
|
||||||
|
virtual BoxType box_type() const { return BoxType::None; }
|
||||||
|
virtual void dump(String const& prepend = {}) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
using BoxList = Vector<NonnullOwnPtr<Box>>;
|
||||||
|
|
||||||
|
struct FullBox : public Box {
|
||||||
|
virtual ErrorOr<void> read_from_stream(BoxStream& stream) override;
|
||||||
|
virtual void dump(String const& prepend = {}) const override;
|
||||||
|
|
||||||
|
u8 version { 0 };
|
||||||
|
u32 flags { 0 };
|
||||||
|
};
|
||||||
|
|
||||||
|
struct UnknownBox final : public Box {
|
||||||
|
static ErrorOr<NonnullOwnPtr<UnknownBox>> create_from_stream(BoxType type, BoxStream& stream)
|
||||||
|
{
|
||||||
|
auto box = TRY(try_make<UnknownBox>(type, stream.remaining()));
|
||||||
|
TRY(box->read_from_stream(stream));
|
||||||
|
return box;
|
||||||
|
}
|
||||||
|
UnknownBox(BoxType type, size_t contents_size)
|
||||||
|
: m_box_type(type)
|
||||||
|
, m_contents_size(contents_size)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
virtual ~UnknownBox() override = default;
|
||||||
|
virtual ErrorOr<void> read_from_stream(BoxStream&) override;
|
||||||
|
virtual BoxType box_type() const override { return m_box_type; }
|
||||||
|
virtual void dump(String const& prepend = {}) const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
BoxType m_box_type { BoxType::None };
|
||||||
|
size_t m_contents_size { 0 };
|
||||||
|
};
|
||||||
|
|
||||||
|
#define BOX_SUBTYPE(BoxName) \
|
||||||
|
static ErrorOr<NonnullOwnPtr<BoxName>> create_from_stream(BoxStream& stream) \
|
||||||
|
{ \
|
||||||
|
auto box = TRY(try_make<BoxName>()); \
|
||||||
|
TRY(box->read_from_stream(stream)); \
|
||||||
|
return box; \
|
||||||
|
} \
|
||||||
|
BoxName() = default; \
|
||||||
|
virtual ~BoxName() override = default; \
|
||||||
|
virtual ErrorOr<void> read_from_stream(BoxStream& stream) override; \
|
||||||
|
virtual BoxType box_type() const override \
|
||||||
|
{ \
|
||||||
|
return BoxType::BoxName; \
|
||||||
|
} \
|
||||||
|
virtual void dump(String const& prepend = {}) const override;
|
||||||
|
|
||||||
|
// 4.3 File Type Box
|
||||||
|
struct FileTypeBox final : public FullBox {
|
||||||
|
BOX_SUBTYPE(FileTypeBox);
|
||||||
|
|
||||||
|
BrandIdentifier major_brand { BrandIdentifier::None };
|
||||||
|
u32 minor_version;
|
||||||
|
Vector<BrandIdentifier> compatible_brands;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
117
Userland/Libraries/LibGfx/ImageFormats/ISOBMFF/Enums.h
Normal file
117
Userland/Libraries/LibGfx/ImageFormats/ISOBMFF/Enums.h
Normal file
|
@ -0,0 +1,117 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2023, Gregory Bertilson <Zaggy1024@gmail.com>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <AK/Endian.h>
|
||||||
|
#include <AK/Types.h>
|
||||||
|
|
||||||
|
namespace Gfx::ISOBMFF {
|
||||||
|
|
||||||
|
// Define all Box types:
|
||||||
|
#define ENUMERATE_ALL() \
|
||||||
|
ENUMERATE_ONE(FileTypeBox, ftyp) \
|
||||||
|
ENUMERATE_ONE(MetaBox, meta) \
|
||||||
|
ENUMERATE_ONE(MovieBox, moov) \
|
||||||
|
ENUMERATE_ONE(MediaDataBox, mdat) \
|
||||||
|
ENUMERATE_ONE(FreeBox, free)
|
||||||
|
|
||||||
|
constexpr u32 fourcc_to_number(char const fourcc[4])
|
||||||
|
{
|
||||||
|
return AK::convert_between_host_and_big_endian((fourcc[0] << 24) | (fourcc[1] << 16) | (fourcc[2] << 8) | fourcc[3]);
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class BoxType : u32 {
|
||||||
|
None = 0,
|
||||||
|
|
||||||
|
#define ENUMERATE_ONE(box_name, box_4cc) box_name = fourcc_to_number(#box_4cc),
|
||||||
|
|
||||||
|
ENUMERATE_ALL()
|
||||||
|
|
||||||
|
#undef ENUMERATE_ONE
|
||||||
|
};
|
||||||
|
|
||||||
|
static Optional<StringView> box_type_to_string(BoxType type)
|
||||||
|
{
|
||||||
|
switch (type) {
|
||||||
|
#define ENUMERATE_ONE(box_name, box_4cc) \
|
||||||
|
case BoxType::box_name: \
|
||||||
|
return #box_name " ('" #box_4cc "')"sv;
|
||||||
|
|
||||||
|
ENUMERATE_ALL()
|
||||||
|
|
||||||
|
#undef ENUMERATE_ONE
|
||||||
|
|
||||||
|
default:
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef ENUMERATE_ALL
|
||||||
|
|
||||||
|
// Define all FileTypeBox brand identifiers:
|
||||||
|
#define ENUMERATE_ALL() \
|
||||||
|
ENUMERATE_ONE(iso8) \
|
||||||
|
ENUMERATE_ONE(avif) \
|
||||||
|
ENUMERATE_ONE(avis) \
|
||||||
|
ENUMERATE_ONE(mif1) \
|
||||||
|
ENUMERATE_ONE(msf1) \
|
||||||
|
ENUMERATE_ONE(miaf) \
|
||||||
|
ENUMERATE_ONE(MA1A)
|
||||||
|
|
||||||
|
enum class BrandIdentifier : u32 {
|
||||||
|
None = 0,
|
||||||
|
|
||||||
|
#define ENUMERATE_ONE(brand_4cc) brand_4cc = fourcc_to_number(#brand_4cc),
|
||||||
|
|
||||||
|
ENUMERATE_ALL()
|
||||||
|
|
||||||
|
#undef ENUMERATE_ONE
|
||||||
|
};
|
||||||
|
|
||||||
|
static Optional<StringView> brand_identifier_to_string(BrandIdentifier type)
|
||||||
|
{
|
||||||
|
switch (type) {
|
||||||
|
#define ENUMERATE_ONE(brand_4cc) \
|
||||||
|
case BrandIdentifier::brand_4cc: \
|
||||||
|
return #brand_4cc##sv;
|
||||||
|
|
||||||
|
ENUMERATE_ALL()
|
||||||
|
|
||||||
|
#undef ENUMERATE_ONE
|
||||||
|
|
||||||
|
default:
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef ENUMERATE_ALL
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
template<>
|
||||||
|
struct AK::Formatter<Gfx::ISOBMFF::BoxType> : Formatter<FormatString> {
|
||||||
|
ErrorOr<void> format(FormatBuilder& builder, Gfx::ISOBMFF::BoxType const& box_type)
|
||||||
|
{
|
||||||
|
auto string = Gfx::ISOBMFF::box_type_to_string(box_type);
|
||||||
|
if (string.has_value()) {
|
||||||
|
return Formatter<FormatString>::format(builder, "{}"sv, string.release_value());
|
||||||
|
}
|
||||||
|
return Formatter<FormatString>::format(builder, "Unknown Box ('{}')"sv, StringView((char const*)&box_type, 4));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<>
|
||||||
|
struct AK::Formatter<Gfx::ISOBMFF::BrandIdentifier> : Formatter<FormatString> {
|
||||||
|
ErrorOr<void> format(FormatBuilder& builder, Gfx::ISOBMFF::BrandIdentifier const& brand_identifier)
|
||||||
|
{
|
||||||
|
auto string = Gfx::ISOBMFF::brand_identifier_to_string(brand_identifier);
|
||||||
|
if (string.has_value()) {
|
||||||
|
return Formatter<FormatString>::format(builder, "{}"sv, string.release_value());
|
||||||
|
}
|
||||||
|
return Formatter<FormatString>::format(builder, "{}"sv, StringView((char const*)&brand_identifier, 4));
|
||||||
|
}
|
||||||
|
};
|
39
Userland/Libraries/LibGfx/ImageFormats/ISOBMFF/Reader.cpp
Normal file
39
Userland/Libraries/LibGfx/ImageFormats/ISOBMFF/Reader.cpp
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2023, Gregory Bertilson <Zaggy1024@gmail.com>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Reader.h"
|
||||||
|
|
||||||
|
namespace Gfx::ISOBMFF {
|
||||||
|
|
||||||
|
ErrorOr<Reader> Reader::create(MaybeOwned<SeekableStream> stream)
|
||||||
|
{
|
||||||
|
return Reader(move(stream));
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorOr<BoxList> Reader::read_entire_file()
|
||||||
|
{
|
||||||
|
BoxList top_level_boxes;
|
||||||
|
|
||||||
|
while (!m_stream->is_eof()) {
|
||||||
|
auto box_header = TRY(read_box_header(*m_stream));
|
||||||
|
BoxStream box_stream { *m_stream, box_header.contents_size };
|
||||||
|
|
||||||
|
switch (box_header.type) {
|
||||||
|
case BoxType::FileTypeBox:
|
||||||
|
TRY(top_level_boxes.try_append(TRY(FileTypeBox::create_from_stream(box_stream))));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
TRY(top_level_boxes.try_append(TRY(UnknownBox::create_from_stream(box_header.type, box_stream))));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!box_stream.is_eof())
|
||||||
|
return Error::from_string_literal("Reader did not consume all data");
|
||||||
|
}
|
||||||
|
return top_level_boxes;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
36
Userland/Libraries/LibGfx/ImageFormats/ISOBMFF/Reader.h
Normal file
36
Userland/Libraries/LibGfx/ImageFormats/ISOBMFF/Reader.h
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2023, Gregory Bertilson <Zaggy1024@gmail.com>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <AK/MaybeOwned.h>
|
||||||
|
#include <AK/Stream.h>
|
||||||
|
|
||||||
|
#include "Boxes.h"
|
||||||
|
|
||||||
|
namespace Gfx::ISOBMFF {
|
||||||
|
|
||||||
|
class Reader {
|
||||||
|
public:
|
||||||
|
static ErrorOr<Reader> create(MaybeOwned<SeekableStream> stream);
|
||||||
|
|
||||||
|
ErrorOr<BoxList> read_entire_file();
|
||||||
|
|
||||||
|
ErrorOr<BrandIdentifier> get_major_brand();
|
||||||
|
ErrorOr<Vector<BrandIdentifier>> get_minor_brands();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Reader(MaybeOwned<SeekableStream> stream)
|
||||||
|
: m_stream(move(stream))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorOr<void> parse_initial_data();
|
||||||
|
|
||||||
|
MaybeOwned<SeekableStream> m_stream;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -102,6 +102,7 @@ target_link_libraries(image PRIVATE LibGfx)
|
||||||
target_link_libraries(image2bin PRIVATE LibGfx)
|
target_link_libraries(image2bin PRIVATE LibGfx)
|
||||||
target_link_libraries(ini PRIVATE LibFileSystem)
|
target_link_libraries(ini PRIVATE LibFileSystem)
|
||||||
target_link_libraries(install-bin PRIVATE LibFileSystem)
|
target_link_libraries(install-bin PRIVATE LibFileSystem)
|
||||||
|
target_link_libraries(isobmff PRIVATE LibGfx)
|
||||||
target_link_libraries(jail-attach PRIVATE LibCore LibMain)
|
target_link_libraries(jail-attach PRIVATE LibCore LibMain)
|
||||||
target_link_libraries(jail-create PRIVATE LibCore LibMain)
|
target_link_libraries(jail-create PRIVATE LibCore LibMain)
|
||||||
target_link_libraries(js PRIVATE LibCrypto LibJS LibLine LibLocale LibTextCodec)
|
target_link_libraries(js PRIVATE LibCrypto LibJS LibLine LibLocale LibTextCodec)
|
||||||
|
|
28
Userland/Utilities/isobmff.cpp
Normal file
28
Userland/Utilities/isobmff.cpp
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2023, Gregory Bertilson <Zaggy1024@gmail.com>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <AK/MemoryStream.h>
|
||||||
|
#include <LibCore/ArgsParser.h>
|
||||||
|
#include <LibCore/MappedFile.h>
|
||||||
|
#include <LibGfx/ImageFormats/ISOBMFF/Reader.h>
|
||||||
|
|
||||||
|
ErrorOr<int> serenity_main(Main::Arguments arguments)
|
||||||
|
{
|
||||||
|
Core::ArgsParser args_parser;
|
||||||
|
|
||||||
|
StringView path;
|
||||||
|
args_parser.add_positional_argument(path, "Path to ISO Base Media File Format file", "FILE");
|
||||||
|
|
||||||
|
args_parser.parse(arguments);
|
||||||
|
|
||||||
|
auto file = TRY(Core::MappedFile::map(path));
|
||||||
|
auto reader = TRY(Gfx::ISOBMFF::Reader::create(TRY(try_make<FixedMemoryStream>(file->bytes()))));
|
||||||
|
auto boxes = TRY(reader.read_entire_file());
|
||||||
|
|
||||||
|
for (auto& box : boxes)
|
||||||
|
box->dump();
|
||||||
|
return 0;
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue