Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

extract stream decoding to Decoder classes #1321

Merged
merged 39 commits into from
Oct 4, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
0922977
extract stream decoding to Decoder classes (HEVC, J2K)
farindk Oct 1, 2024
5f86fe3
move decode_single_frame_from_compressed_data() to Decoder
farindk Oct 1, 2024
957093a
revert: cmake OpenJPEG version check
farindk Oct 1, 2024
9d891b8
initialize Decoder in ImageItem::on_load_file()
farindk Oct 1, 2024
9f1156a
decode HEVC through Decoder_HEVC
farindk Oct 2, 2024
d1e662f
default implementation of ImageItem::get_luma_bits_per_pixel() just f…
farindk Oct 2, 2024
33d3870
move AVIF decoding into Decoder_AVIF
farindk Oct 2, 2024
6a5b62c
(change struct to class)
farindk Oct 2, 2024
1e64957
replace all get_item_type() with get_item_type_4cc()
farindk Oct 2, 2024
1bb435c
replace all fourcc-type strings with uint32_t constants
farindk Oct 2, 2024
fa791d0
rename 'to_fourcc()' to 'fourcc_to_string()'
farindk Oct 2, 2024
0eb4109
rename HeifFile::get_compressed_image_data() to HeifFile::get_uncompr…
farindk Oct 2, 2024
da93fdc
remove mutex from get_uncompressed_item_data() as it is not used for …
farindk Oct 2, 2024
e029ffc
add Decoder classes for VVC, AVC, JPEG
farindk Oct 3, 2024
ef58e35
(remove 'struct' forward declaration)
farindk Oct 3, 2024
11cf60d
JPEG: parse SOF for chroma format
farindk Oct 3, 2024
6295206
(minor edit)
farindk Oct 3, 2024
73fb313
add Decoder_uncompressed class
farindk Oct 3, 2024
497dc5b
uncompressed: separated codec from ImageItem
farindk Oct 3, 2024
6190b58
uncompressed: pulled AbstractDecoder into its own file
farindk Oct 3, 2024
77292c2
uncompressed: pulled ComponentInterleaveDecoder into its own file
farindk Oct 3, 2024
8cae3b0
uncompressed: pulled PixelInterleaveDecoder into its own file
farindk Oct 3, 2024
8b0dade
uncompressed: pulled MixedInterleaveDecoder into its own file
farindk Oct 3, 2024
b4a06be
uncompressed: pulled RowInterleaveDecoder into its own file
farindk Oct 3, 2024
ab2f694
uncompressed: pulled TileComponentInterleaveDecoder into its own file
farindk Oct 3, 2024
5ae88f6
uncompressed: (cleanup)
farindk Oct 3, 2024
cba01e8
uncompressed: simplify nAlignmentSkipBytes()
farindk Oct 3, 2024
901ce17
uncompressed: (cleanup includes)
farindk Oct 3, 2024
a1e388c
JPEG2000: use Decoder_JPEG2000 in ImageItem_JPEG2000
farindk Oct 3, 2024
6882bd6
extract AVIF and AVC boxes into their own files
farindk Oct 4, 2024
a47b217
remove dead code
farindk Oct 4, 2024
098da51
fix wrong AVIF chroma parsing
farindk Oct 4, 2024
c8715dd
extract HEVC boxes processing into its own file
farindk Oct 4, 2024
fd73fc3
extract JPEG boxes processing into its own file
farindk Oct 4, 2024
3117743
extract JPEG2000 boxes processing into its own file
farindk Oct 4, 2024
8e94574
extract VVC boxes processing into its own file
farindk Oct 4, 2024
0a423e9
scale vvenc encoder quality from 0-100 and invert, so that 100 is max…
farindk Oct 4, 2024
e127d20
move all ImageItem classes into its own directory
farindk Oct 4, 2024
562f826
add 'unci' to Decoder::alloc_for_infe_type()
farindk Oct 4, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ endif()
plugin_option(OpenJPEG_ENCODER "OpenJPEG J2K encoder" OFF ON)
plugin_option(OpenJPEG_DECODER "OpenJPEG J2K decoder" OFF ON)
if (WITH_OpenJPEG_ENCODER OR WITH_OpenJPEG_DECODER)
find_package(OpenJPEG 2)
find_package(OpenJPEG)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know why you do this (I do too), but probably not wanted in this PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I'll have to put that on .gitignore. No matter how much care I take, sooner or later it slips through :-)

endif()

# ffmpeg
Expand Down
6 changes: 6 additions & 0 deletions libheif/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -50,14 +50,20 @@ set(libheif_sources
api/libheif/heif_items.cc
api/libheif/heif_experimental.h
api/libheif/heif_experimental.cc
codecs/decoder.h
codecs/decoder.cc
codecs/hevc.cc
codecs/hevc.h
codecs/hevc_dec.cc
codecs/hevc_dec.h
codecs/avif.cc
codecs/avif.h
codecs/jpeg.h
codecs/jpeg.cc
codecs/jpeg2000.h
codecs/jpeg2000.cc
codecs/jpeg2000_dec.h
codecs/jpeg2000_dec.cc
codecs/vvc.h
codecs/vvc.cc
codecs/avc.h
Expand Down
4 changes: 3 additions & 1 deletion libheif/api/libheif/heif.h
Original file line number Diff line number Diff line change
Expand Up @@ -1141,16 +1141,18 @@ LIBHEIF_API
int heif_image_handle_is_premultiplied_alpha(const struct heif_image_handle*);

// Returns -1 on error, e.g. if this information is not present in the image.
// Only defined for images coded in the YCbCr colorspace.
LIBHEIF_API
int heif_image_handle_get_luma_bits_per_pixel(const struct heif_image_handle*);

// Returns -1 on error, e.g. if this information is not present in the image.
// Only defined for images coded in the YCbCr colorspace.
LIBHEIF_API
int heif_image_handle_get_chroma_bits_per_pixel(const struct heif_image_handle*);

// Return the colorspace that libheif proposes to use for decoding.
// Usually, these will be either YCbCr or Monochrome, but it may also propose RGB for images
// encoded with matrix_coefficients=0.
// encoded with matrix_coefficients=0 or for images coded natively in RGB.
// It may also return *_undefined if the file misses relevant information to determine this without decoding.
LIBHEIF_API
struct heif_error heif_image_handle_get_preferred_decoding_colorspace(const struct heif_image_handle* image_handle,
Expand Down
183 changes: 183 additions & 0 deletions libheif/codecs/decoder.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
/*
* HEIF codec.
* Copyright (c) 2024 Dirk Farin <[email protected]>
*
* This file is part of libheif.
*
* libheif is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* libheif is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with libheif. If not, see <http://www.gnu.org/licenses/>.
*/

#include "codecs/decoder.h"
#include "error.h"
#include "context.h"
#include "plugin_registry.h"
#include "libheif/api_structs.h"

#include "codecs/hevc_dec.h"
#include "codecs/jpeg2000_dec.h"

#include <limits>


void DataExtent::set_from_image_item(class HeifFile* file, heif_item_id item)
{
m_iloc = file->get_property<Box_iloc>(item);
m_item_id = item;
}


Result<std::vector<uint8_t>*> DataExtent::read_data() const
{
if (!m_raw.empty()) {
return &m_raw;
}
else if (m_iloc) {
// image
m_iloc->read_data(m_item_id, m_file->get_reader(), nullptr, &m_raw, 0, std::numeric_limits<uint64_t>::max());
}
else {
// sequence
assert(false); // TODO
}

return &m_raw;
}


Result<std::vector<uint8_t>> DataExtent::read_data(uint64_t offset, uint64_t size) const
{
std::vector<uint8_t> data;

if (!m_raw.empty()) {
data.insert(data.begin(), m_raw.begin() + offset, m_raw.begin() + offset + size);
return data;
}
else if (m_iloc) {
// TODO: cache data

// image
Error err = m_iloc->read_data(m_item_id, m_file->get_reader(), nullptr, &m_raw, 0, size);
if (err) {
return err;
}
return data;
}
else {
// sequence
assert(false); // TODO
return Error::Ok;
}
}


std::shared_ptr<Decoder> Decoder::alloc_for_compression_format(const HeifContext* ctx, heif_item_id id, uint32_t format_4cc)
{
switch (format_4cc) {
case fourcc("hvc1"): {
auto hvcC = ctx->get_heif_file()->get_property<Box_hvcC>(id);
return std::make_shared<Decoder_HEVC>(hvcC);
}
case fourcc("j2k1"): {
auto j2kH = ctx->get_heif_file()->get_property<Box_j2kH>(id);
return std::make_shared<Decoder_JPEG2000>(j2kH);
}
#if WITH_UNCOMPRESSED_CODEC
#endif
default:
assert(false);
return nullptr;
}
}


Result<std::vector<uint8_t>> Decoder::get_compressed_data() const
{
// --- get the compressed image data

// data from configuration blocks

Result<std::vector<uint8_t>> confData = read_bitstream_configuration_data();
if (confData.error) {
return confData.error;
}

std::vector<uint8_t> data = confData.value;

// image data, usually from 'mdat'

Result dataResult = m_data_extent.read_data();
if (dataResult.error) {
return dataResult.error;
}

data.insert(data.begin(), dataResult.value->begin(), dataResult.value->end());

return data;
}


Result<std::shared_ptr<HeifPixelImage>>
Decoder::decode_single_frame_from_compressed_data(heif_compression_format compression_format,
const struct heif_decoding_options& options,
const std::vector<uint8_t>& data)
{
const struct heif_decoder_plugin* decoder_plugin = get_decoder(compression_format, options.decoder_id);
if (!decoder_plugin) {
return Error(heif_error_Plugin_loading_error, heif_suberror_No_matching_decoder_installed);
}


// --- decode image with the plugin

void* decoder;
struct heif_error err = decoder_plugin->new_decoder(&decoder);
if (err.code != heif_error_Ok) {
return Error(err.code, err.subcode, err.message);
}

if (decoder_plugin->plugin_api_version >= 2) {
if (decoder_plugin->set_strict_decoding) {
decoder_plugin->set_strict_decoding(decoder, options.strict_decoding);
}
}

err = decoder_plugin->push_data(decoder, data.data(), data.size());
if (err.code != heif_error_Ok) {
decoder_plugin->free_decoder(decoder);
return Error(err.code, err.subcode, err.message);
}

heif_image* decoded_img = nullptr;

err = decoder_plugin->decode_image(decoder, &decoded_img);
if (err.code != heif_error_Ok) {
decoder_plugin->free_decoder(decoder);
return Error(err.code, err.subcode, err.message);
}

if (!decoded_img) {
// TODO(farindk): The plugin should return an error in this case.
decoder_plugin->free_decoder(decoder);
return Error(heif_error_Decoder_plugin_error, heif_suberror_Unspecified);
}

// -- cleanup

std::shared_ptr<HeifPixelImage> img = std::move(decoded_img->image);
heif_image_release(decoded_img);

decoder_plugin->free_decoder(decoder);

return img;
}
90 changes: 90 additions & 0 deletions libheif/codecs/decoder.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/*
* HEIF codec.
* Copyright (c) 2024 Dirk Farin <[email protected]>
*
* This file is part of libheif.
*
* libheif is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* libheif is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with libheif. If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef HEIF_DECODER_H
#define HEIF_DECODER_H

#include "libheif/heif.h"
#include "box.h"
#include "error.h"
#include "file.h"

#include <memory>
#include <string>
#include <utility>
#include <vector>
#include <codecs/hevc.h>


// Specifies the input data for decoding.
// For images, this points to the iloc extents.
// For sequences, this points to the track data.
struct DataExtent
{
std::shared_ptr<HeifFile> m_file;

// --- raw data
mutable std::vector<uint8_t> m_raw; // also for cached data

// --- image
std::shared_ptr<Box_iloc> m_iloc;
heif_item_id m_item_id = 0;

// --- sequence
// TODO

void set_from_image_item(class HeifFile* file, heif_item_id item);

Result<std::vector<uint8_t>*> read_data() const;

Result<std::vector<uint8_t>> read_data(uint64_t offset, uint64_t size) const;
};


class Decoder
{
public:
virtual ~Decoder() = default;

static std::shared_ptr<Decoder> alloc_for_compression_format(const HeifContext* ctx, heif_item_id, uint32_t format_4cc);

void set_data_extent(DataExtent extent) { m_data_extent = std::move(extent); }

[[nodiscard]] virtual int get_luma_bits_per_pixel() const = 0;

[[nodiscard]] virtual int get_chroma_bits_per_pixel() const = 0;

[[nodiscard]] virtual Error get_coded_image_colorspace(heif_colorspace*, heif_chroma*) const = 0;

[[nodiscard]] virtual Result<std::vector<uint8_t>> read_bitstream_configuration_data() const = 0;

Result<std::vector<uint8_t>> get_compressed_data() const;

static Result<std::shared_ptr<HeifPixelImage>>
decode_single_frame_from_compressed_data(heif_compression_format compression_format,
const struct heif_decoding_options& options,
const std::vector<uint8_t>& data);

private:
DataExtent m_data_extent;
};


#endif
Loading
Loading