mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-12-11 17:00:37 +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
Notes:
sideshowbarker
2024-07-16 22:51:10 +09:00
Author: https://github.com/Zaggy1024 Commit: https://github.com/SerenityOS/serenity/commit/66c9696687 Pull-request: https://github.com/SerenityOS/serenity/pull/19547
13 changed files with 510 additions and 0 deletions
|
@ -534,6 +534,9 @@ if (BUILD_LAGOM)
|
|||
add_executable(image ../../Userland/Utilities/image.cpp)
|
||||
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)
|
||||
target_link_libraries(ttfdisasm LibGfx LibMain)
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ set(TEST_SOURCES
|
|||
TestGfxBitmap.cpp
|
||||
TestICCProfile.cpp
|
||||
TestImageDecoder.cpp
|
||||
TestParseISOBMFF.cpp
|
||||
TestRect.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/ICOLoader.cpp
|
||||
ImageFormats/ImageDecoder.cpp
|
||||
ImageFormats/ISOBMFF/Boxes.cpp
|
||||
ImageFormats/ISOBMFF/Reader.cpp
|
||||
ImageFormats/JPEGLoader.cpp
|
||||
ImageFormats/JPEGXLLoader.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(ini 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-create PRIVATE LibCore LibMain)
|
||||
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…
Reference in a new issue